diff --git a/.changeset/README.md b/.changeset/README.md new file mode 100644 index 00000000000..e5b6d8d6a67 --- /dev/null +++ b/.changeset/README.md @@ -0,0 +1,8 @@ +# Changesets + +Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works +with multi-package repos, or single-package repos to help you version and publish your code. You can +find the full documentation for it [in our repository](https://github.com/changesets/changesets) + +We have a quick list of common questions to get you started engaging with this project in +[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) diff --git a/.changeset/beige-planes-sort.md b/.changeset/beige-planes-sort.md new file mode 100644 index 00000000000..90e922b7fd4 --- /dev/null +++ b/.changeset/beige-planes-sort.md @@ -0,0 +1,5 @@ +--- +'@celo/contractkit': minor +--- + +add FeeHandler Wrapper diff --git a/.changeset/config.json b/.changeset/config.json new file mode 100644 index 00000000000..267c3a001eb --- /dev/null +++ b/.changeset/config.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://unpkg.com/@changesets/config@2.3.1/schema.json", + "changelog": "@changesets/cli/changelog", + "commit": false, + "fixed": [ + [ + "@celo/wallet-*" + ] + ], + "linked": [], + "access": "public", + "baseBranch": "master", + "updateInternalDependencies": "patch", + "ignore": [], + "snapshot": { + "useCalculatedVersion": true, + "prereleaseTemplate": "{tag}-{commit}" + } +} diff --git a/.circleci/config.yml b/.circleci/config.yml index 01c71a56692..e8118867f14 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,11 +2,21 @@ # and https://github.com/facebook/react-native/blob/master/.circleci/config.yml version: 2.1 +setup: true orbs: node: circleci/node@5.1.0 parameters: + run-workflow-general: + type: boolean + default: false + run-workflow-npm-install: + type: boolean + default: false + run-workflow-protocol-coverage: + type: boolean + default: false # When you need to force a rebuild of the node modules cache then bump this version node-modules-cache-version: type: integer @@ -38,8 +48,7 @@ defaults: &defaults contract-defaults: &contract-defaults <<: *defaults environment: - # RELEASE_TAG: core-contracts.v9 - RELEASE_TAG: ganache-v7-core-contracts.v9 + RELEASE_TAG: core-contracts.v10 e2e-defaults: &e2e-defaults <<: *defaults @@ -201,8 +210,7 @@ jobs: general-test: <<: *defaults - # XXX (soloseng): Increased size because this test was hitting the ram limit and failing - resource_class: large + resource_class: xlarge steps: - attach_workspace: at: ~/app @@ -217,7 +225,6 @@ jobs: --ignore @celo/celotool \ --ignore @celo/celocli \ --ignore @celo/env-tests \ - --ignore @celo/identity \ --ignore @celo/transactions-uri \ --ignore '@celo/wallet-*' \ run test @@ -254,6 +261,7 @@ jobs: name: Opcode tests command: yarn --cwd packages/protocol check-opcodes - node/install: + # TODO(soloseng): remove this node 12 install once the latest contract release is done on node18. install-yarn: true node-version: '12.22.11' - run: @@ -452,28 +460,6 @@ jobs: name: Run Tests command: yarn --cwd=packages/sdk/contractkit test - # extrated to reuse the devchain generated by contractkit - identity-tests: - <<: *defaults - steps: - - attach_workspace: - at: ~/app - - run: - name: Check if the test should run - command: | - ./scripts/ci_check_if_test_should_run_v2.sh @celo/identity - # - run: - # name: Copy DevChain generated by Contractkit - # command: | - # (cp -r packages/sdk/contractkit/.tmp packages/sdk/identity/.tmp) - - run: - name: Generate DevChain - command: | - (cd packages/sdk/identity && yarn test:reset) - - run: - name: Run Tests - command: yarn --cwd=packages/sdk/identity test - # extrated to reuse the devchain generated by contractkit transactions-uri-tests: <<: *defaults @@ -793,29 +779,6 @@ jobs: name: Minor test of celocli command: ./node_modules/.bin/celocli account:new # Small test - odis-test: - <<: *defaults - # Source: https://circleci.com/docs/2.0/configuration-reference/#resource_class - resource_class: large - steps: - - attach_workspace: - at: ~/app - - run: - name: Check if the test should run - command: | - ./scripts/ci_check_if_test_should_run_v2.sh @celo/phone-number-privacy-signer,@celo/phone-number-privacy-combiner,@celo/phone-number-privacy-common - - run: - name: Run Tests for common package - command: yarn --cwd=packages/phone-number-privacy/common test:coverage - - run: - name: Run Tests for combiner - no_output_timeout: 30m - command: yarn --cwd=packages/phone-number-privacy/combiner test:coverage - - run: - name: Run Tests for signer - no_output_timeout: 30m - command: yarn --cwd=packages/phone-number-privacy/signer test:coverage - certora-test: working_directory: ~/app docker: @@ -885,20 +848,21 @@ jobs: cd packages/protocol ./specs/scripts/reserve.sh fi - - flakey-test-summary: - <<: *defaults + # Dummy job for not showing CircleCI error: "Error: All Workflows have been filtered from this Pipeline" + # Check https://github.com/CircleCI-Public/circleci-cli/issues/577 for more context + noop: + docker: + - image: cimg/base:current + resource_class: small steps: - - attach_workspace: - at: ~/app - - run: - name: Add summary of flakey tests to GitHub Checks - command: | - node ./packages/flake-tracker/scripts/summary.js + - run: echo "noop" workflows: version: 2 celo-monorepo-build: + # Contitionally triggered + when: + or: [<< pipeline.parameters.run-workflow-general >>] jobs: - install_dependencies - certora-test: @@ -919,12 +883,6 @@ workflows: - wallets-test: requires: - install_dependencies - - identity-tests: - requires: - - contractkit-test - - transactions-uri-tests: - requires: - - contractkit-test - pre-protocol-test-release: requires: - lint-checks @@ -997,52 +955,18 @@ workflows: requires: - lint-checks - contractkit-test - - odis-test: - requires: - - lint-checks - # - flakey-test-summary: - # requires: - # - protocol-test-common - # - protocol-test-compatibility - # - protocol-test-governance-network - # - protocol-test-governance-validators - # - protocol-test-governance-voting - # - protocol-test-identity - # - protocol-test-stability - # - end-to-end-geth-transfer-test - # - end-to-end-geth-blockchain-parameters-test - # - end-to-end-geth-slashing-test - # - end-to-end-geth-governance-test - # - end-to-end-geth-replica-test - # - end-to-end-geth-sync-test - # - end-to-end-geth-validator-order-test - # - end-to-end-cip35-eth-compatibility-test - # - odis-test npm-install-testing-cron-workflow: - triggers: - - schedule: - # 7 PM in UTC = noon in PDT. - # Best for test to fail during SF afternoon, so that, someone can fix it during the day time. - cron: '0 19 * * *' - filters: - branches: - only: - - master + # Contitionally triggered + when: + or: [<< pipeline.parameters.run-workflow-npm-install >>] jobs: - test-typescript-npm-package-install - test-utils-npm-package-install - test-contractkit-npm-package-install - test-celocli-npm-package-install protocol-testing-with-code-coverage-cron-workflow: - triggers: - - schedule: - # 1 PM in UTC = 6 AM in PDT. - # Best for this slow test (~3 hours) to run during SF early morning. - cron: '0 13 * * *' - filters: - branches: - only: - - master + when: + or: [<< pipeline.parameters.run-workflow-protocol-coverage >>] jobs: - install_dependencies - lint-checks: @@ -1051,3 +975,8 @@ workflows: - protocol-test-with-code-coverage: requires: - lint-checks + # Dummy job for not showing CircleCI error: "Error: All Workflows have been filtered from this Pipeline" + # Check https://github.com/CircleCI-Public/circleci-cli/issues/577 for more context + no-changes: + jobs: + - noop diff --git a/.env b/.env index 8087faab907..caa932d6c08 100644 --- a/.env +++ b/.env @@ -8,8 +8,6 @@ GETH_VMODULE="consensus/*=2" GETH_ENABLE_METRICS=false GETH_USE_MYCELO=false -VM_BASED=false - KUBERNETES_CLUSTER_NAME="celo-networks-dev" KUBERNETES_CLUSTER_ZONE="us-west1-a" CLUSTER_DOMAIN_NAME="celo-networks-dev" @@ -60,10 +58,6 @@ TRANSACTION_METRICS_EXPORTER_DOCKER_IMAGE_REPOSITORY="gcr.io/celo-testnet/celo-m TRANSACTION_METRICS_EXPORTER_DOCKER_IMAGE_TAG="transaction-metrics-exporter-dc5e5dfa07231a4ff4664816a95eae606293eae9" TRANSACTION_METRICS_EXPORTER_SUFFIX='1' -EKSPORTISTO_DOCKER_IMAGE_REPOSITORY="us.gcr.io/celo-testnet/eksportisto" -EKSPORTISTO_DOCKER_IMAGE_TAG="12f9f9e822ea35db2f965be616abef126f1724fb" -EKSPORTISTO_SUFFIX='1' - # Genesis Vars NETWORK_ID=1101 CONSENSUS_TYPE="istanbul" diff --git a/.env.alfajores b/.env.alfajores index a59e37cff62..9be969dcaa1 100644 --- a/.env.alfajores +++ b/.env.alfajores @@ -2,22 +2,12 @@ ENV_TYPE="production" GETH_VERBOSITY=2 -VM_BASED=false - KUBERNETES_CLUSTER_NAME="alfajores" KUBERNETES_CLUSTER_ZONE="us-west1-a" CLUSTER_DOMAIN_NAME="celo-testnet" TESTNET_PROJECT_NAME="celo-testnet-production" -AZURE_KOMENCI_EASTUS_AZURE_SUBSCRIPTION_ID=97e2b592-255b-4f92-bce0-127257163c36 -AZURE_KOMENCI_EASTUS_AZURE_TENANT_ID=7cb7628a-e37c-4afb-8332-2029e418980e -AZURE_KOMENCI_EASTUS_AZURE_REGION_NAME=eus - -AZURE_KOMENCI_WESTEU_AZURE_SUBSCRIPTION_ID=97e2b592-255b-4f92-bce0-127257163c36 -AZURE_KOMENCI_WESTEU_AZURE_TENANT_ID=7cb7628a-e37c-4afb-8332-2029e418980e -AZURE_KOMENCI_WESTEU_AZURE_REGION_NAME=weu - BLOCKSCOUT_DOCKER_IMAGE_TAG="0362f9f4d1d4842f27adb634d628f969f53c046d" # Assign a new value everytime you redeploy blockscout. Or else the deployment will fail due to the # existing database. @@ -33,20 +23,23 @@ CELOSTATS_BANNED_ADDRESSES="" CELOSTATS_RESERVED_ADDRESSES="" ORACLE_DOCKER_IMAGE_REPOSITORY="us-west1-docker.pkg.dev/celo-testnet-production/celo-oracle/celo-oracle" -ORACLE_DOCKER_IMAGE_TAG="1.0.3" +ORACLE_DOCKER_IMAGE_TAG="latest" AZURE_ORACLE_CENTRALUS_AZURE_SUBSCRIPTION_ID=7a6f5f20-bd43-4267-8c35-a734efca140c AZURE_ORACLE_CENTRALUS_AZURE_TENANT_ID=7cb7628a-e37c-4afb-8332-2029e418980e AZURE_ORACLE_CENTRALUS_AZURE_KUBERNETES_RESOURCE_GROUP=baklava-oracles-centralus AZURE_ORACLE_CENTRALUS_KUBERNETES_CLUSTER_NAME=baklava-oracles-centralus AZURE_ORACLE_CENTRALUS_AZURE_REGION_NAME=centralus -AZURE_ORACLE_CENTRALUS_CELOUSD_ORACLE_ADDRESSES_FROM_MNEMONIC_COUNT=6 -AZURE_ORACLE_CENTRALUS_CELOEUR_ORACLE_ADDRESSES_FROM_MNEMONIC_COUNT=6 -AZURE_ORACLE_CENTRALUS_CELOBTC_ORACLE_ADDRESSES_FROM_MNEMONIC_COUNT=10 -AZURE_ORACLE_CENTRALUS_CELOBRL_ORACLE_ADDRESSES_FROM_MNEMONIC_COUNT=6 -AZURE_ORACLE_CENTRALUS_USDCUSD_ORACLE_ADDRESSES_FROM_MNEMONIC_COUNT=5 -AZURE_ORACLE_CENTRALUS_USDCEUR_ORACLE_ADDRESSES_FROM_MNEMONIC_COUNT=5 -AZURE_ORACLE_CENTRALUS_USDCBRL_ORACLE_ADDRESSES_FROM_MNEMONIC_COUNT=5 +AZURE_ORACLE_CENTRALUS_CELOUSD_ORACLE_ADDRESSES_FROM_MNEMONIC_COUNT=4 +AZURE_ORACLE_CENTRALUS_CELOEUR_ORACLE_ADDRESSES_FROM_MNEMONIC_COUNT=4 +AZURE_ORACLE_CENTRALUS_CELOBRL_ORACLE_ADDRESSES_FROM_MNEMONIC_COUNT=4 +AZURE_ORACLE_CENTRALUS_USDCUSD_ORACLE_ADDRESSES_FROM_MNEMONIC_COUNT=4 +AZURE_ORACLE_CENTRALUS_USDCEUR_ORACLE_ADDRESSES_FROM_MNEMONIC_COUNT=4 +AZURE_ORACLE_CENTRALUS_USDCBRL_ORACLE_ADDRESSES_FROM_MNEMONIC_COUNT=4 +AZURE_ORACLE_CENTRALUS_EUROCEUR_ORACLE_ADDRESSES_FROM_MNEMONIC_COUNT=4 +AZURE_ORACLE_CENTRALUS_CELOXOF_ORACLE_ADDRESSES_FROM_MNEMONIC_COUNT=4 +AZURE_ORACLE_CENTRALUS_EURXOF_ORACLE_ADDRESSES_FROM_MNEMONIC_COUNT=4 +AZURE_ORACLE_CENTRALUS_EUROCXOF_ORACLE_ADDRESSES_FROM_MNEMONIC_COUNT=4 AZURE_ORACLE_CENTRALUS_FULL_NODES_COUNT=2 AZURE_ORACLE_CENTRALUS_FULL_NODES_DISK_SIZE=30 AZURE_ORACLE_CENTRALUS_FULL_NODES_ROLLING_UPDATE_PARTITION=0 @@ -84,10 +77,6 @@ MOCK_ORACLE_DOCKER_IMAGE_TAG="default" TRANSACTION_METRICS_EXPORTER_DOCKER_IMAGE_REPOSITORY="gcr.io/celo-testnet/celo-monorepo" TRANSACTION_METRICS_EXPORTER_DOCKER_IMAGE_TAG="transaction-metrics-exporter-f4a55e143932ea559cf4bcbd9bcccc14da43d6ed" -EKSPORTISTO_DOCKER_IMAGE_REPOSITORY="us.gcr.io/celo-testnet/eksportisto" -EKSPORTISTO_DOCKER_IMAGE_TAG="c88f7e8babcdb01062a6a12ae729705ba9769add" -EKSPORTISTO_SUFFIX='1' - # Genesis Vars NETWORK_ID=44787 CONSENSUS_TYPE="istanbul" @@ -135,7 +124,7 @@ MOBILE_WALLET_PLAYSTORE_LINK="https://play.google.com/apps/internaltest/47009904 # each context should have its own environment variables, generally of the form # _* -CONTEXTS=azure-komenci-eastus,azure-komenci-westeu,azure-oracle-centralus,azure-odis-eastus-1,azure-odis-eastus-2,azure-odis-eastus-3 +CONTEXTS=azure-oracle-centralus,azure-odis-eastus-1,azure-odis-eastus-2,azure-odis-eastus-3 # --- ODIS --- @@ -246,64 +235,6 @@ AZURE_ODIS_EASTUS_3_PROM_SIDECAR_GCP_PROJECT=celo-phone-number-privacy AZURE_ODIS_EASTUS_3_PROM_SIDECAR_GCP_REGION=us-east1 AZURE_ODIS_EASTUS_3_PROM_SIDECAR_DISABLED="true" -# --- Komenci --- - -KOMENCI_DOCKER_IMAGE_REPOSITORY="celotestnet.azurecr.io/komenci/komenci" -KOMENCI_DOCKER_IMAGE_TAG="08081d2d276a6fd0d420805f3bbe3866e866a63a" - -AZURE_KOMENCI_EASTUS_AZURE_KUBERNETES_RESOURCE_GROUP=staging-komenci-eastus -AZURE_KOMENCI_EASTUS_KUBERNETES_CLUSTER_NAME=staging-komenci-eastus -AZURE_KOMENCI_EASTUS_REGION_NAME=eus - -AZURE_KOMENCI_EASTUS_KOMENCI_DB_HOST=staging-komenci-eastus.postgres.database.azure.com -AZURE_KOMENCI_EASTUS_KOMENCI_DB_PORT=5432 -AZURE_KOMENCI_EASTUS_KOMENCI_DB_USERNAME=postgres@staging-komenci-eastus -AZURE_KOMENCI_EASTUS_KOMENCI_DB_PASSWORD_VAULT_NAME=staging-komenci-eus - -AZURE_KOMENCI_WESTEU_AZURE_KUBERNETES_RESOURCE_GROUP=staging-komenci-weu -AZURE_KOMENCI_WESTEU_KUBERNETES_CLUSTER_NAME=staging-komenci-weu -AZURE_KOMENCI_WESTEU_REGION_NAME=weu - -AZURE_KOMENCI_WESTEU_KOMENCI_DB_HOST=staging-komenci-weu.postgres.database.azure.com -AZURE_KOMENCI_WESTEU_KOMENCI_DB_PORT=5432 -AZURE_KOMENCI_WESTEU_KOMENCI_DB_USERNAME=postgres@staging-komenci-weu -AZURE_KOMENCI_WESTEU_KOMENCI_DB_PASSWORD_VAULT_NAME=staging-komenci-weu - -AZURE_KOMENCI_EASTUS_KOMENCI_REWARD_SERVICE_DB_HOST=staging-komenci-weu.postgres.database.azure.com -AZURE_KOMENCI_EASTUS_KOMENCI_REWARD_SERVICE_DB_PORT=5432 -AZURE_KOMENCI_EASTUS_KOMENCI_REWARD_SERVICE_DB_USERNAME=postgres@staging-komenci-weu -AZURE_KOMENCI_EASTUS_KOMENCI_REWARD_SERVICE_DB_PASSWORD_VAULT_NAME=staging-komenci-weu - -AZURE_KOMENCI_WESTEU_KOMENCI_REWARD_SERVICE_DB_HOST=staging-komenci-weu.postgres.database.azure.com -AZURE_KOMENCI_WESTEU_KOMENCI_REWARD_SERVICE_DB_PORT=5432 -AZURE_KOMENCI_WESTEU_KOMENCI_REWARD_SERVICE_DB_USERNAME=postgres@staging-komenci-weu -AZURE_KOMENCI_WESTEU_KOMENCI_REWARD_SERVICE_DB_PASSWORD_VAULT_NAME=staging-komenci-weu - -# Secrets -AZURE_KOMENCI_EASTUS_KOMENCI_APP_SECRETS_VAULT_NAME=staging-komenci-eus -AZURE_KOMENCI_WESTEU_KOMENCI_APP_SECRETS_VAULT_NAME=staging-komenci-weu - -# Rule config > Captcha -KOMENCI_RULE_CONFIG_CAPTCHA_BYPASS_TOKEN=special-captcha-bypass-token -AZURE_KOMENCI_EASTUS_KOMENCI_RULE_CONFIG_CAPTCHA_BYPASS_ENABLED=true -AZURE_KOMENCI_WESTEU_KOMENCI_RULE_CONFIG_CAPTCHA_BYPASS_ENABLED=true - -# Format should be a comma-separated sequence of: -#
:: -AZURE_KOMENCI_EASTUS_KOMENCI_ADDRESS_AZURE_KEY_VAULTS=0x00454cac6dae53f8800f71395b9a174f07a784b1:staging-komenci-eus,0xc6f0f9bfb1aed83620ece3eac0add98a65a8574e:staging-komenci-eus -AZURE_KOMENCI_WESTEU_KOMENCI_ADDRESS_AZURE_KEY_VAULTS=0x0f812be74511b90ea6b2f80e77bea047e69a0b2a:staging-komenci-weu,0xb354d3d2908ba6a2b791683b0f454a38f69cb282:staging-komenci-weu -AZURE_KOMENCI_EASTUS_KOMENCI_CELOLABS_REWARDS_ADDRESS_AZURE_KEY_VAULTS=0xb04390478a57e3c2147599d5380434f25fa5234d:staging-komenci-rewards -AZURE_KOMENCI_WESTEU_KOMENCI_CELOLABS_REWARDS_ADDRESS_AZURE_KEY_VAULTS=0xb04390478a57e3c2147599d5380434f25fa5234d:staging-komenci-rewards - -# Celo Rewards -AZURE_KOMENCI_EASTUS_KOMENCI_REWARD_SERVICE_INSTANCE_COUNT = 1 -AZURE_KOMENCI_WESTEU_KOMENCI_REWARD_SERVICE_INSTANCE_COUNT = 1 -KOMENCI_SHOULD_SEND_REWARDS=true - -# Network -AZURE_KOMENCI_EASTUS_KOMENCI_NETWORK=alfajores -AZURE_KOMENCI_WESTEU_KOMENCI_NETWORK=alfajores - # For WalletConnect relay WALLET_CONNECT_IMAGE_REPOSITORY = 'us.gcr.io/celo-testnet/walletconnect' WALLET_CONNECT_IMAGE_TAG = '1472bcaad57e3746498f7a661c42ff5cf9acaf5a' diff --git a/.env.baklava b/.env.baklava index d353c7b4db6..9b394d601d7 100644 --- a/.env.baklava +++ b/.env.baklava @@ -5,7 +5,6 @@ ENV_TYPE="production" GETH_VERBOSITY=2 GETH_ENABLE_METRICS=true -VM_BASED=false KUBERNETES_CLUSTER_NAME="baklavastaging" KUBERNETES_CLUSTER_ZONE="us-west1-a" @@ -37,7 +36,7 @@ CELOCLI_STANDALONE_IMAGE_REPOSITORY="gcr.io/celo-testnet/celocli-standalone" CELOCLI_STANDALONE_IMAGE_TAG="0.0.30-beta2" ORACLE_DOCKER_IMAGE_REPOSITORY="us-west1-docker.pkg.dev/celo-testnet-production/celo-oracle/celo-oracle" -ORACLE_DOCKER_IMAGE_TAG="1.0.3" +ORACLE_DOCKER_IMAGE_TAG="latest" # ---- Full Node Chain Restore ---- @@ -65,6 +64,10 @@ AZURE_ORACLE_WESTUS2_CELOBRL_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0x86f9c87d13347e604 AZURE_ORACLE_WESTUS2_USDCUSD_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0x97ef27cf3ce65b2558161aeb1e3cff8b5f71fd04:baklava-usdcusd-oracle1:baklava-oracles-westus2,0x559702d23983eb29bcf30f2487d477945c0dbc6a:baklava-usdcusd-oracle3:baklava-oracles-westus2 AZURE_ORACLE_WESTUS2_USDCBRL_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0x8a2d375ae246e305c14c88e6687ff06acd66c9ba:baklava-brlusdc-oracle2:baklava-oracles-westus2,0x72434eca70d5544f8178c1a769762c8c1f0fd940:baklava-brlusdc-oracle5:baklava-oracles-westus2 AZURE_ORACLE_WESTUS2_USDCEUR_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0x99ce1e35574802e29644ee7a8284f9987fceee3d:baklava-eurusdc-oracle6:baklava-oracles-westus2,0x4c37e2cc2e9105984fef866a3f06aa953cc660d1:baklava-eurusdc-oracle7:baklava-oracles-westus2 +AZURE_ORACLE_WESTUS2_EUROCEUR_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0x6866e306b32acae7310d3b87dd53fc948f0e0629:baklava-euroceur-oracle2:baklava-oracles-westus2,0xe33502b13be6e0444a08de933faa24a59ae9b585:baklava-euroceur-oracle3:baklava-oracles-westus2 +AZURE_ORACLE_WESTUS2_CELOXOF_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0x96eda2cad69c8cd1daeb80da86d24825f45f46b7:baklava-celoxof-oracle2,0x4e9d441fd1c77222395a1853d851fea8a0e3aed8:baklava-celoxof-oracle3 +AZURE_ORACLE_WESTUS2_EURXOF_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0x7fe5f297dd812ca21e7bf1cbf145a0b59227b35f:baklava-eurxof-oracle2,0x2addc69c2ce3a9d93a8291419319bf7f0a2c6c82:baklava-eurxof-oracle3 +AZURE_ORACLE_WESTUS2_EUROCXOF_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0x729e058e97c099c79af674bbe2f687171432dd17:baklava-eurocxof-oracle2,0xd226aa9ee80ee282339c1ae69f3f811dbe5d895a:baklava-eurocxof-oracle4 AZURE_ORACLE_WESTUS2_FULL_NODES_COUNT=2 AZURE_ORACLE_WESTUS2_FULL_NODES_ROLLING_UPDATE_PARTITION=0 AZURE_ORACLE_WESTUS2_FULL_NODES_DISK_SIZE=30 @@ -87,6 +90,10 @@ AZURE_ORACLE_CENTRALUS_CELOBRL_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0xe467003845bdcbe AZURE_ORACLE_CENTRALUS_USDCUSD_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0x6a0e5d8a496feb59464028250c88a08341ea0831:baklava-usdcusd-oracle0:baklava-oracles-centralus,0xb15833400aecc72cb759d4e57a3a5a9c2963a5c5:baklava-usdcusd-oracle2:baklava-oracles-centralus AZURE_ORACLE_CENTRALUS_USDCBRL_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0xa326d557b79422952d708d4eea8523d29b7174ec:baklava-brlusdc-oracle0:baklava-oracles-centralus,0xe73b1f39d1289e608df9a055c459b909f4bc1592:baklava-brlusdc-oracle1:baklava-oracles-centralus AZURE_ORACLE_CENTRALUS_USDCEUR_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0xa6a5ae0c1c6b0ed52f2c6bb5666b61cfa86ac3ae:baklava-eurusdc-oracle4:baklava-oracles-centralus,0x2679be4034f47219bf9dfcbfb55bad60fe741315:baklava-eurusdc-oracle5:baklava-oracles-centralus +AZURE_ORACLE_CENTRALUS_EUROCEUR_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0x9a0613e8a1ff6cfd72ab692e6b450fbf02deba81:baklava-euroceur-oracle0:baklava-oracles-centralus,0xbe4bbd15177e2857c7c3297da12331033eeacd93:baklava-euroceur-oracle1:baklava-oracles-centralus +AZURE_ORACLE_CENTRALUS_CELOXOF_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0xd056a29e86161a34692c34f4c95933b59de077dc:baklava-celoxof-oracle0,0x5ad07f89176298ae3a0f3d20d0b4a756307d46e7:baklava-celoxof-oracle1 +AZURE_ORACLE_CENTRALUS_EURXOF_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0xa4a46db00840e6525ffe79aee5990abaebb7479d:baklava-eurxof-oracle0,0x6e537c9462ed968ff08eab430c5f8c11eab7df1a:baklava-eurxof-oracle1 +AZURE_ORACLE_CENTRALUS_EUROCXOF_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0x1a637c38671512866317475d19df5f55b0802276:baklava-eurocxof-oracle0,0x8589f0bb307581b96877f9e1a5ce3fcb05127fd0:baklava-eurocxof-oracle1 AZURE_ORACLE_CENTRALUS_FULL_NODES_COUNT=2 AZURE_ORACLE_CENTRALUS_FULL_NODES_ROLLING_UPDATE_PARTITION=0 AZURE_ORACLE_CENTRALUS_FULL_NODES_DISK_SIZE=30 @@ -130,10 +137,6 @@ GCP_FORNO_EUROPE_WEST1_PROM_SIDECAR_DISABLED="true" TRANSACTION_METRICS_EXPORTER_DOCKER_IMAGE_REPOSITORY="gcr.io/celo-testnet/celo-monorepo" TRANSACTION_METRICS_EXPORTER_DOCKER_IMAGE_TAG="transaction-metrics-exporter-dc5e5dfa07231a4ff4664816a95eae606293eae9" -EKSPORTISTO_DOCKER_IMAGE_REPOSITORY="us.gcr.io/celo-testnet/eksportisto" -EKSPORTISTO_DOCKER_IMAGE_TAG="b199a634866ef34c4fa1925381dff4de40eb0a3c" -EKSPORTISTO_SUFFIX='1' - # Genesis Vars NETWORK_ID=62320 CONSENSUS_TYPE="istanbul" diff --git a/.env.oracledev b/.env.oracledev index 6676cba2c1e..2cda4367b37 100644 --- a/.env.oracledev +++ b/.env.oracledev @@ -5,7 +5,7 @@ ORACLE_UNUSED_ORACLE_ADDRESSES= # each context should have its own environment variables, generally of the form # _* -CONTEXTS=azure-eastus,aws-test,gcp-test,gcp-test-asia +CONTEXTS=azure-eastus,gcp-test,gcp-test-asia FORNO_FULL_NODE_CONTEXTS=gcp-test,gcp-test-asia FORNO_DOMAINS=oracledev-forno.celo-networks-dev.org. @@ -23,15 +23,8 @@ AZURE_EASTUS_CELOUSD_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0x21860ca3a0a6f7e450b8f24bd AZURE_EASTUS_FULL_NODES_COUNT=2 AZURE_EASTUS_FULL_NODES_DISK_SIZE=10 -AWS_TEST_AWS_KUBERNETES_CLUSTER_REGION=us-west-2 -AWS_TEST_AWS_KUBERNETES_RESOURCE_GROUP=adorable-monster-1597251246 -AWS_TEST_KUBERNETES_CLUSTER_NAME=adorable-monster-1597251246 # Format should be a comma-separated sequence of: #
:: -AWS_TEST_CELOUSD_ORACLE_ADDRESS_AWS_KEY_ALIASES=0xf7af8e3f613e5cb210f6f96b46da41fb91338e95:test-ecc-key:eu-central-1,0x3ec7d9e8e13c85b9ed38039d8f9807534f73f713:trevor-test-ecc-key:eu-central-1 -AWS_TEST_FULL_NODES_COUNT=2 -AWS_TEST_FULL_NODES_DISK_SIZE=10 - GCP_TEST_GCP_PROJECT_NAME=celo-testnet GCP_TEST_GCP_ZONE=us-west4-a GCP_TEST_KUBERNETES_CLUSTER_NAME=federated-dev-us-west4-a @@ -61,7 +54,6 @@ CLUSTER_CREATION_FLAGS="--enable-autoscaling --min-nodes 3 --max-nodes 8 --machi # ---- VM ---- -VM_BASED=false # ---- Blockscout ---- diff --git a/.env.rc1 b/.env.rc1 index a618d05bb1b..4194f61f635 100644 --- a/.env.rc1 +++ b/.env.rc1 @@ -13,13 +13,6 @@ CLUSTER_DOMAIN_NAME="celo-testnet" TESTNET_PROJECT_NAME="celo-testnet-production" -AZURE_KOMENCI_SOUTHBR_AZURE_SUBSCRIPTION_ID=7a6f5f20-bd43-4267-8c35-a734efca140c -AZURE_KOMENCI_SOUTHBR_AZURE_TENANT_ID=7cb7628a-e37c-4afb-8332-2029e418980e -AZURE_KOMENCI_SOUTHBR_AZURE_REGION_NAME=br -AZURE_KOMENCI_SEA_AZURE_SUBSCRIPTION_ID=7a6f5f20-bd43-4267-8c35-a734efca140c -AZURE_KOMENCI_SEA_AZURE_TENANT_ID=7cb7628a-e37c-4afb-8332-2029e418980e -AZURE_KOMENCI_SEA_AZURE_REGION_NAME=sea - BLOCKSCOUT_DOCKER_IMAGE_TAG="0362f9f4d1d4842f27adb634d628f969f53c046d" BLOCKSCOUT_DB_SUFFIX=3 @@ -52,7 +45,7 @@ CELOCLI_STANDALONE_IMAGE_TAG="0.0.42" MOCK_ORACLE_CRON_SCHEDULE="*/5 * * * *" ORACLE_DOCKER_IMAGE_REPOSITORY="us-west1-docker.pkg.dev/celo-testnet-production/celo-oracle/celo-oracle" -ORACLE_DOCKER_IMAGE_TAG="1.0.3" +ORACLE_DOCKER_IMAGE_TAG="1.0.6" ORACLE_UNUSED_ORACLE_ADDRESSES=0xB93Fe7906ea4221b3fbe23412D18Ab1B07FE2F71,0x8d25D74E43789079Ef3C6B965c3D22b63A1233aC,0xCD88Cc79342a7cFE78E91FAa173eC87704bDcA9a,0x5091110175318A2A8aF88309D1648c1D84d31B29,0xBBd6e54Af7A5722f42461C6313F37Bd50729F195,0xE23a4c6615669526Ab58E9c37088bee4eD2b2dEE @@ -63,10 +56,10 @@ GSTORAGE_DATA_BUCKET=celo-chain-backup/mainnet # ---- Contexts ---- -# A list of every valid context. Must start with one of: gcp,aws,azure +# A list of every valid context. Must start with one of: gcp,azure # each context should have its own environment variables, generally of the form # _* -CONTEXTS=azure-oracle-westus,azure-oracle-westeurope,azure-oracle-eastus2,gcp-forno-us-west1,gcp-forno-us-east1,gcp-forno-asia-east1,gcp-forno-europe-west1,gcp-forno-southamerica-east1,azure-komenci-southbr,azure-komenci-sea,azure-odis-westus2-a,azure-odis-eastasia-a,azure-odis-westeurope-a,azure-odis-brazilsouth-a,gcp-private-txnodes +CONTEXTS=azure-oracle-westus,azure-oracle-westeurope,azure-oracle-eastus2,gcp-forno-us-west1,gcp-forno-us-east1,gcp-forno-asia-east1,gcp-forno-europe-west1,gcp-forno-southamerica-east1,azure-odis-westus2-a,azure-odis-eastasia-a,azure-odis-westeurope-a,azure-odis-brazilsouth-a,gcp-private-txnodes # ---- Oracle Contexts ---- @@ -81,6 +74,12 @@ AZURE_ORACLE_WESTUS_CELOUSD_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0x0aee051be85ba9c7c1 AZURE_ORACLE_WESTUS_CELOEUR_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0xb8bDBfdd591a5be5980983A7ba1710a5F46f42B5:mainnet-eur-oracle-wus2:mainnet-oracles-westus2,0x929Ad7f2b781CE830014E824CA2eF0b7b8de87C2:mainnet-eur-oracle-wus3:mainnet-oracles-westus2,0xCCC0B54edD8dAe3c15b5C002dd5d348495d4f7fe:mainnet-eur-oracle-wus4:mainnet-oracles-westus2 AZURE_ORACLE_WESTUS_CELOBRL_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0x57d8a7bf9e7f4113c49e077b140fd8e1d7f78a76:mainnet-brl-oracle-wus0:mainnet-oracles-westus2,0x1299dd007cd5120262e546dca893e30d1cff8a10:mainnet-brl-oracle-wus1:mainnet-oracles-westus2,0x116951e440aee97a328614f9937710c9bb2f0839:mainnet-brl-oracle-wus4:mainnet-oracles-westus2 AZURE_ORACLE_WESTUS_USDCUSD_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0x2986c21824c9b804d170270a316ceb07149f79c5:mainnet-usdcusd-wus0,0x09e2e47bb5df7b3464407746970a65c7b02883b3:mainnet-usdcusd-wus1,0xd5e7454932f6e853af849f70044570b62ca2596e:mainnet-usdcusd-wus2,0xfe3276b7142dee2cda34b1d14852eb32f436483d:mainnet-usdcusd-wus3 +AZURE_ORACLE_WESTUS_USDCEUR_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0xeccd1e9439094d025ac7d08d16b0bfe0da3bea53:mainnet-usdceur-wus0,0x9b242d2bd848fc92060ca7546033c3af352583d2:mainnet-usdceur-wus1,0x905ab001a9199d45c3f5c7b055b65ace5fc7d70a:mainnet-usdceur-wus2,0xdf5dd31d8f78520185d6a9fb0498c4bbddfe0708:mainnet-usdceur-wus3 +AZURE_ORACLE_WESTUS_USDCBRL_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0x8dba01f832c7b0bb5f0bad4efe181cc07f8b322e:mainnet-usdcbrl-wus0,0xffb417d009d09bd1140244e70babbaa52d69ec84:mainnet-usdcbrl-wus1,0x5f755b8350a2e6b8b042cb3e052580e4c5b0ac35:mainnet-usdcbrl-wus2,0x8e1349b48ee82ef5437c912662e6640f3590c6f9:mainnet-usdcbrl-wus3 +AZURE_ORACLE_WESTUS_EUROCEUR_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0x6e7c84f83778569016ea0a7f6f119d6b779eed17:mainnet-euroceur-wus0,0xf96bf6c3b9ea73150f0d7f452fba685ff456c322:mainnet-euroceur-wus1,0xc5bf0748538f8673691bc9c8e4eae386d5987559:mainnet-euroceur-wus2,0xe868cd23a3941dfbab10597b103313706a027191:mainnet-euroceur-wus3 +AZURE_ORACLE_WESTUS_CELOXOF_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0xce696d465dde582095fce8b67e1a31ceb45ad922:mainnet-celoxof-wus0,0xbc211b8dfecdd5784f9c419ce64f7de1377bae88:mainnet-celoxof-wus1,0xc659ab5c049b726c2945a8a44b783ce6afbd2ceb:mainnet-celoxof-wus2,0x9094bf2b2eb028c6fcc56e7d46ea28bb6e03c9a5:mainnet-celoxof-wus3,0xb947c54be882314623ee3d74684d0d785dd50335:mainnet-celoxof-wus4 +AZURE_ORACLE_WESTUS_EURXOF_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0x6a033b7217fbae843a3ffc9783ef9f87dd3a1c04:mainnet-eurxof-wus0,0x8a164c0523bbd7ec70172807723cca9a948858bb:mainnet-eurxof-wus1,0xd0066f198ed7f8dc3684ff3ac77511ef58a9aed3:mainnet-eurxof-wus2,0x441061f8b1f8ee2722d3608bfa0b5c4c14dee813:mainnet-eurxof-wus3,0x87089ec6adbf3c994ae7c47d3aa7d4fc104d0422:mainnet-eurxof-wus4 +AZURE_ORACLE_WESTUS_EUROCXOF_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0xc5a86597d514b423579684cdf9f49b6df37e3689:mainnet-eurocxof-wus0,0x8e1423ca0bcb15093f52d1d07675e0aa04e3da75:mainnet-eurocxof-wus1,0xa47e6a8a7db5ee22b5293704a4f0f5f8fdaab06f:mainnet-eurocxof-wus2,0x77d148efdd40202d0eec787073a70c7f6bc9c485:mainnet-eurocxof-wus3,0xfef8748fd3f039fb8cfa77c7744b171f4396659c:mainnet-eurocxof-wus4 AZURE_ORACLE_WESTUS_FULL_NODES_COUNT=5 AZURE_ORACLE_WESTUS_FULL_NODES_ROLLING_UPDATE_PARTITION=0 AZURE_ORACLE_WESTUS_FULL_NODES_DISK_SIZE=100 @@ -101,6 +100,12 @@ AZURE_ORACLE_WESTEUROPE_CELOUSD_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0xfe9925e6ae9c4c AZURE_ORACLE_WESTEUROPE_CELOEUR_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0x87C45738DAd8Dc3D2b1cCe779E0766329cc408C6:mainnet-eur-oracle-weu0:mainnet-oracles-westeurope,0xeF1E143C554EFC43B0537Af00Ac27C828dE6cF8D:mainnet-eur-oracle-weu1:mainnet-oracles-westeurope,0xF4B4AA107F30206EA019DE145A9b778a220f9fc0:mainnet-eur-oracle-weu2:mainnet-oracles-westeurope,0x24c303e6395DD19806F739619960A311764e3F40:mainnet-eur-oracle-weu3:mainnet-oracles-westeurope,0xDA413875FB45E5905950Bc08a908ebD246Ee6581:mainnet-eur-oracle-weu5:mainnet-oracles-westeurope AZURE_ORACLE_WESTEUROPE_CELOBRL_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0x3b91bbb873f3b979bd6671dc018d5fc1848882dd:mainnet-brl-oracle-weu0:mainnet-oracles-westeurope,0xc3994b2af0e82490e432d49e9f2246cdfd84da8f:mainnet-brl-oracle-weu1:mainnet-oracles-westeurope,0x9b376b33c33325332df8c6ca951a9896889a6d1e:mainnet-brl-oracle-weu2:mainnet-oracles-westeurope,0x554ba7f4d200c7b233b93b7f2223bc1ea7c467fd:mainnet-brl-oracle-weu3:mainnet-oracles-westeurope,0x535cea1834d6b52e4e9724642fdd7008f569ba5c:mainnet-brl-oracle-weu4:mainnet-oracles-westeurope AZURE_ORACLE_WESTEUROPE_USDCUSD_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0x477185291403ca2ed5f56d59ed0d568a16222013:mainnet-usdcusd-weu0,0x9a0a52d483c62df76d54f41ab3283cc7cb41ba91:mainnet-usdcusd-weu1,0x2ddb86898a2c2c884fc5cc3ca344898b0170a00d:mainnet-usdcusd-weu2,0x79be0a692e3a4bcd22b96c3e93a108b485becbb2:mainnet-usdcusd-weu3 +AZURE_ORACLE_WESTEUROPE_USDCEUR_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0x0781f530100e619936f5b427263441cb0414f885:mainnet-usdceur-weu0,0x55de75fd0c2b37987757172fef7ba2ea935d284d:mainnet-usdceur-weu1,0xdc0c15fa73b13b2e74cd3eced23d8826569904c5:mainnet-usdceur-weu2,0x9048872f739cebbe72825763a1b72064c4df8f1f:mainnet-usdceur-weu3 +AZURE_ORACLE_WESTEUROPE_USDCBRL_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0x42b813b9ff8ce8f4837accea26bedda20d7c4982:mainnet-usdcbrl-weu0,0x09208127500963ee1c3af88bfbb3ef0cd34d6eb0:mainnet-usdcbrl-weu1,0xa8f5be092a8452eab98ed1c220d642114bb2731e:mainnet-usdcbrl-weu2,0xfd265c994a5a9c2847fe03a5e878648963f53a37:mainnet-usdcbrl-weu3 +AZURE_ORACLE_WESTEUROPE_EUROCEUR_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0xa633c79ac2c6881c0898b2b417a3aecda6f9eb10:mainnet-euroceur-weu0,0xbc32b5e6682bd7b64e52bdceead83f597ed0fd77:mainnet-euroceur-weu1,0x110d08157ed0c525f7fd983a857180583767cbcf:mainnet-euroceur-weu2,0xa8c15faf676df18566c4b8c4c653e5f992e687bb:mainnet-euroceur-weu3 +AZURE_ORACLE_WESTEUROPE_CELOXOF_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0x4d89a0c95de82ae78c42fad4f8d3f87c4495fd37:mainnet-celoxof-weu0,0xa97dbefac6026f93cc5714c4c150b7466e9502ef:mainnet-celoxof-weu1,0x676931c73c8d6b09b0c192baf821e3fd2d693750:mainnet-celoxof-weu2,0xfb8f294c8cd98cf059672c1a6153f85555f10a90:mainnet-celoxof-weu3,0xb7614f7174a07028a5ff5e1adc68a031b646857f:mainnet-celoxof-weu4 +AZURE_ORACLE_WESTEUROPE_EURXOF_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0xd2c4f59724df51026f857a7e188b322e35256e24:mainnet-eurxof-weu0,0xe47c9867dbb37110834aaaf65b8d760c49c22081:mainnet-eurxof-weu1,0x9cb4896447a8f2611f5fb6f5fc853ffa16a1d864:mainnet-eurxof-weu2,0x0f9786b083c8c22e2e839286230098048a20a0ec:mainnet-eurxof-weu3,0xe01890c7760445908128f0e64e1170866566e1f6:mainnet-eurxof-weu4 +AZURE_ORACLE_WESTEUROPE_EUROCXOF_ORACLE_ADDRESS_AZURE_KEY_VAULTS=0xdda1d71f3d5a6090bc04b77a18925fab7054d9c3:mainnet-eurocxof-weu0,0xee1d05f81e90b8ece440de6141282404e83830ce:mainnet-eurocxof-weu1,0xff6e35c6119742fd1eb3db780d976c4e55585108:mainnet-eurocxof-weu2,0x59eac333453279e71a3a98b4b72bdfa99ca51ad3:mainnet-eurocxof-weu3,0x378b95092bed2acb0d3ae6ab9c045eef1c250872:mainnet-eurocxof-weu4 AZURE_ORACLE_WESTEUROPE_FULL_NODES_COUNT=5 AZURE_ORACLE_WESTEUROPE_FULL_NODES_ROLLING_UPDATE_PARTITION=0 AZURE_ORACLE_WESTEUROPE_FULL_NODES_DISK_SIZE=100 @@ -240,10 +245,6 @@ GCP_PRIVATE_TXNODES_FULL_NODES_WS_PORT="8545" TRANSACTION_METRICS_EXPORTER_DOCKER_IMAGE_REPOSITORY="gcr.io/celo-testnet/celo-monorepo" TRANSACTION_METRICS_EXPORTER_DOCKER_IMAGE_TAG="transaction-metrics-exporter-f4a55e143932ea559cf4bcbd9bcccc14da43d6ed" -EKSPORTISTO_DOCKER_IMAGE_REPOSITORY="us.gcr.io/celo-testnet/eksportisto" -EKSPORTISTO_DOCKER_IMAGE_TAG="41fd75246c7108716be373d2d36cac13996034fc" -EKSPORTISTO_SUFFIX='' - # Genesis Vars NETWORK_ID=42220 CONSENSUS_TYPE="istanbul" @@ -465,73 +466,8 @@ AZURE_ODIS_BRAZILSOUTH_A_PROM_SIDECAR_GCP_PROJECT=celo-pgpnp-mainnet AZURE_ODIS_BRAZILSOUTH_A_PROM_SIDECAR_GCP_REGION=southamerica-east1-a AZURE_ODIS_BRAZILSOUTH_A_PROM_SIDECAR_DISABLED="true" -# --- Komenci --- - -KOMENCI_DOCKER_IMAGE_REPOSITORY="celotestnet.azurecr.io/komenci/komenci" -KOMENCI_DOCKER_IMAGE_TAG="e220c5610e196a1d674edde0f24be0d5eca30c00" - -AZURE_KOMENCI_SOUTHBR_AZURE_KUBERNETES_RESOURCE_GROUP=mainnet-komenci-brazil -AZURE_KOMENCI_SOUTHBR_KUBERNETES_CLUSTER_NAME=mainnet-komenci-brazil - -AZURE_KOMENCI_SOUTHBR_KOMENCI_DB_HOST=mainnet-komenci-brazil.postgres.database.azure.com -AZURE_KOMENCI_SOUTHBR_KOMENCI_DB_PORT=5432 -AZURE_KOMENCI_SOUTHBR_KOMENCI_DB_USERNAME=postgres@mainnet-komenci-brazil -AZURE_KOMENCI_SOUTHBR_KOMENCI_DB_PASSWORD_VAULT_NAME=mainnet-komenci-brazil - -AZURE_KOMENCI_SEA_AZURE_KUBERNETES_RESOURCE_GROUP=mainnet-komenci-southeastasia -AZURE_KOMENCI_SEA_KUBERNETES_CLUSTER_NAME=mainnet-komenci-southeastasia - -AZURE_KOMENCI_SEA_KOMENCI_DB_HOST=mainnet-komenci-southeastasia.postgres.database.azure.com -AZURE_KOMENCI_SEA_KOMENCI_DB_PORT=5432 -AZURE_KOMENCI_SEA_KOMENCI_DB_USERNAME=postgres@mainnet-komenci-southeastasia -AZURE_KOMENCI_SEA_KOMENCI_DB_PASSWORD_VAULT_NAME=mainnet-komenci-sea - -AZURE_SEA_KOMENCI_AZURE_KUBERNETES_RESOURCE_GROUP=mainnet-komenci-southeastasia -AZURE_SEA_KOMENCI_KUBERNETES_CLUSTER_NAME=mainnet-komenci-southeastasia - -AZURE_SEA_KOMENCI_DB_HOST=mainnet-komenci-southeastasia.postgres.database.azure.com -AZURE_SEA_KOMENCI_DB_PORT=5432 -AZURE_SEA_KOMENCI_DB_USERNAME=postgres@mainnet-komenci-southeastasia -AZURE_SEA_KOMENCI_DB_PASSWORD_VAULT_NAME=mainnet-komenci-sea - -AZURE_KOMENCI_SOUTHBR_KOMENCI_REWARD_SERVICE_DB_HOST=mainnet-komenci-brazil.postgres.database.azure.com -AZURE_KOMENCI_SOUTHBR_KOMENCI_REWARD_SERVICE_DB_PORT=5432 -AZURE_KOMENCI_SOUTHBR_KOMENCI_REWARD_SERVICE_DB_USERNAME=postgres@mainnet-komenci-brazil -AZURE_KOMENCI_SOUTHBR_KOMENCI_REWARD_SERVICE_DB_PASSWORD_VAULT_NAME=mainnet-komenci-brazil - -AZURE_KOMENCI_SEA_KOMENCI_REWARD_SERVICE_DB_HOST=mainnet-komenci-brazil.postgres.database.azure.com -AZURE_KOMENCI_SEA_KOMENCI_REWARD_SERVICE_DB_PORT=5432 -AZURE_KOMENCI_SEA_KOMENCI_REWARD_SERVICE_DB_USERNAME=postgres@mainnet-komenci-brazil -AZURE_KOMENCI_SEA_KOMENCI_REWARD_SERVICE_DB_PASSWORD_VAULT_NAME=mainnet-komenci-brazil - -# App Secrets -AZURE_KOMENCI_SOUTHBR_KOMENCI_APP_SECRETS_VAULT_NAME=mainnet-komenci-brazil -AZURE_KOMENCI_SEA_KOMENCI_APP_SECRETS_VAULT_NAME=mainnet-komenci-sea - -# Rule config > Captcha -KOMENCI_RULE_CONFIG_CAPTCHA_BYPASS_TOKEN=special-captcha-bypass-token -AZURE_KOMENCI_SOUTHBR_KOMENCI_RULE_CONFIG_CAPTCHA_BYPASS_ENABLED=false -AZURE_KOMENCI_SEA_KOMENCI_RULE_CONFIG_CAPTCHA_BYPASS_ENABLED=false - -# Relayer identities -# Format should be a comma-separated sequence of: -#
:: -AZURE_KOMENCI_SOUTHBR_KOMENCI_ADDRESS_AZURE_KEY_VAULTS=0x21888ae301658cdff7ce8c33cdf83a330a5e6273:mainnet-relayer0,0x1438128a2dcc645f0b9706350c1f5dad04845fe6:mainnet-relayer1,0x1e36bf42272a0693eba69332a6f623ce37694a27:mainnet-relayer2,0xd5afaaa7256c9eb86376c4214635dd56dffbd3a8:mainnet-relayer3,0xb09eba8bc1c8bedadd634a8219c0b09042170903:mainnet-relayer4 -AZURE_KOMENCI_SEA_KOMENCI_ADDRESS_AZURE_KEY_VAULTS=0x85a1e716608a84f455d7e07befb76c9b540ac040:mainnet-relayer5,0x2a094e77acf3faebb63279eb60e26d144b9048a2:mainnet-relayer6,0x2f23f9a8f68294a9d6b479c3dbe3dff4de510ced:mainnet-relayer7,0x3db3150c1267d3adeb7f960f3eef11c1dd47a38b:mainnet-relayer8,0xe170915ce32bb8e2ce2a4fcd9113e5298a2e10d2:mainnet-relayer9 -AZURE_KOMENCI_SOUTHBR_KOMENCI_CELOLABS_REWARDS_ADDRESS_AZURE_KEY_VAULTS=0x198e0D8601AB509ABf1B0B99Fd8f234583Ef1309:mainnet-komenci-rewards0 -AZURE_KOMENCI_SEA_KOMENCI_CELOLABS_REWARDS_ADDRESS_AZURE_KEY_VAULTS=0xbDD68B64e288171B37F01346042BEe6Eb7dFAE4f:mainnet-komenci-rewards1 - -# Celo Rewards -AZURE_KOMENCI_SOUTHBR_KOMENCI_REWARD_SERVICE_INSTANCE_COUNT=1 -AZURE_KOMENCI_SEA_KOMENCI_REWARD_SERVICE_INSTANCE_COUNT=1 -KOMENCI_SHOULD_SEND_REWARDS=false - -# Network -AZURE_KOMENCI_SOUTHBR_KOMENCI_NETWORK=rc1 -AZURE_KOMENCI_SEA_KOMENCI_NETWORK=rc1 - # For WalletConnect relay WALLET_CONNECT_IMAGE_REPOSITORY='us.gcr.io/celo-testnet/walletconnect' WALLET_CONNECT_IMAGE_TAG='1472bcaad57e3746498f7a661c42ff5cf9acaf5a' WALLET_CONNECT_REDIS_CLUSTER_ENABLED=false -WALLET_CONNECT_REDIS_CLUSTER_USEPASSWORD=false +WALLET_CONNECT_REDIS_CLUSTER_USEPASSWORD=false \ No newline at end of file diff --git a/.env.rc1staging b/.env.rc1staging index 9cfd22b5566..8d651268dfe 100644 --- a/.env.rc1staging +++ b/.env.rc1staging @@ -5,9 +5,6 @@ ENV_TYPE="staging" GETH_VERBOSITY=2 GETH_ENABLE_METRICS=true -# TODO: deprecated -VM_BASED=false - KUBERNETES_CLUSTER_NAME="rc1staging" KUBERNETES_CLUSTER_ZONE="us-west1-a" CLUSTER_DOMAIN_NAME="celo-testnet" diff --git a/.env.staging b/.env.staging index e9f275a5013..4dc0ef883ca 100644 --- a/.env.staging +++ b/.env.staging @@ -37,7 +37,6 @@ GETH_NODE_DOCKER_IMAGE_TAG="8a44c2cd92200bdffce595c7558e84a39ea2bc15" GETH_VERBOSITY=2 -VM_BASED=false KUBERNETES_CLUSTER_NAME=celo-networks-dev KUBERNETES_CLUSTER_ZONE="us-west1-a" diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f655701493b..82b8b09e47e 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -13,7 +13,6 @@ /packages/docs/ @celo-org/applications @celo-org/devrel /packages/env-tests/ @celo-org/applications /packages/faucet/ @celo-org/applications -/packages/flake-tracker/ @celo-org/flake-trackers /packages/helm-charts/ @celo-org/devopsre /packages/helm-charts/blockscout/ @celo-org/data-services @celo-org/devopsre /packages/helm-charts/oracle/ @celo-org/mento @celo-org/devopsre diff --git a/.github/workflows/circleci.yml b/.github/workflows/celo-monorepo.yml similarity index 81% rename from .github/workflows/circleci.yml rename to .github/workflows/celo-monorepo.yml index 62b81f71e52..acfa637274e 100644 --- a/.github/workflows/circleci.yml +++ b/.github/workflows/celo-monorepo.yml @@ -1,7 +1,7 @@ -name: celo-monorepo -run-name: celo-monorepo tests +name: celo-monorepo CI/CD +run-name: celo-monorepo CI/CD for ${{ github.head_ref || github.ref_name }} -# Dockefile for the self-hosted runner: +# Dockefile for the self-hosted runner: # https://github.com/celo-org/infrastructure/blob/master/terraform/root-modules/gcp/integration-tests-gke/files/github-arc/Dockerfile-monorepo on: @@ -11,28 +11,24 @@ on: pull_request: branches: - master - - martinvol/CR10Release - - 'release/**' - - pahor/0.8_from_Seb - - 'martinvol/isolate-0.8-artifacts' concurrency: - group: circle-ci-${{ github.ref }} + group: celo-monorepo-${{ github.ref }} cancel-in-progress: true defaults: run: shell: bash --login -eo pipefail {0} -env: +env: # Increment these to force cache rebuilding - NODE_MODULE_CACHE_VERSION: 3 + NODE_MODULE_CACHE_VERSION: 4 NODE_OPTIONS: '--max-old-space-size=4096' TERM: dumb GRADLE_OPTS: '-Dorg.gradle.daemon=false -Dorg.gradle.parallel=false -Dorg.gradle.configureondemand=true -Dorg.gradle.jvmargs="-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError"' # Git Tag for contract release to use - RELEASE_TAG: ganache-v7-core-contracts.v9 - # RELEASE_TAG: core-contracts.v9 + #RELEASE_TAG: ganache-v7-core-contracts.v9 + RELEASE_TAG: core-contracts.v10 # CELO_BLOCKCHAIN_BRANCH_TO_TEST: master CELO_BLOCKCHAIN_BRANCH_TO_TEST: release/1.7.x @@ -43,9 +39,9 @@ jobs: package-json-checksum: ${{ steps.node-checksums.outputs.PACKAGE_JSON_CHECKSUM }} # Propagate more outputs if you need https://github.com/tj-actions/changed-files#outputs # Adding a initial comma so ',' matches also for the first file - all_modified_files: ",${{ steps.changed-files.outputs.all_modified_files }}" + all_modified_files: ',${{ steps.changed-files.outputs.all_modified_files }}' # runs-on: ubuntu-latest - runs-on: ["self-hosted", "monorepo-node18"] + runs-on: ['self-hosted', 'monorepo-node18'] timeout-minutes: 30 steps: - name: Restore .git cache @@ -110,9 +106,9 @@ jobs: restore-keys: | code-${{ github.sha }} - name: Install yarn dependencies - run: yarn install + run: git config --global url."https://".insteadOf ssh:// && yarn install if: steps.cache_node.outputs.cache-hit != 'true' - - name: Run yarn postinstall if cache hitted + - name: Run yarn postinstall if cache hitted run: yarn run postinstall if: steps.cache_node.outputs.cache-hit == 'true' - name: Fail if generated dependency graph doesn't match committed @@ -121,7 +117,7 @@ jobs: run: | # This fails if there is any change if ! git diff-index HEAD --; then - echo "Git changes detected while building. If this is unexpected, bump NODE_MODULE_CACHE_VERSION in .github/workflows/circleci.yml" + echo "Git changes detected while building. If this is unexpected, bump NODE_MODULE_CACHE_VERSION in .github/workflows/celo-monorepo.yml" exit 1 fi - name: Build packages @@ -132,16 +128,16 @@ jobs: yarn check-licenses - name: Detect files changed in PR, and expose as output id: changed-files - uses: tj-actions/changed-files@v35 + uses: tj-actions/changed-files@v37 with: # Using comma as separator to be able to easily match full paths (using ,) - separator: "," + separator: ',' # Checking if changed in the last 100 commits in PRs - fetch_depth: "150" + fetch_depth: '150' - run: echo ",${{ steps.changed-files.outputs.all_modified_files }}" lint-checks: name: Lint code - runs-on: ["self-hosted", "monorepo-node18"] + runs-on: ['self-hosted', 'monorepo-node18'] timeout-minutes: 30 needs: install-dependencies steps: @@ -161,7 +157,7 @@ jobs: - run: yarn run lint general_test: name: General jest test - runs-on: ["self-hosted", "monorepo-node18"] + runs-on: ['self-hosted', 'monorepo-node18'] needs: install-dependencies steps: - uses: actions/cache/restore@v3 @@ -184,7 +180,6 @@ jobs: --ignore @celo/celotool \ --ignore @celo/celocli \ --ignore @celo/env-tests \ - --ignore @celo/identity \ --ignore @celo/transactions-uri \ --ignore '@celo/wallet-*' \ run test @@ -195,7 +190,7 @@ jobs: path: test-results/jest wallet-test: name: Wallet test - runs-on: ["self-hosted", "monorepo-node18"] + runs-on: ['self-hosted', 'monorepo-node18'] timeout-minutes: 30 needs: install-dependencies steps: @@ -214,16 +209,22 @@ jobs: yarn run lerna --scope '@celo/wallet-*' run test pre-protocol-test-release: name: Protocol Tests Prepare - runs-on: ["self-hosted", "monorepo-node18"] + runs-on: ['self-hosted', 'monorepo-node18'] + # container: + # image: us-west1-docker.pkg.dev/devopsre/actions-runner-controller/celo-monorepo:node18 + # # Required root to install node12 + # options: --user root timeout-minutes: 30 # Comment lint-checks dependency to speed up (as this is a dependency for many other jobs) # needs: [install-dependencies, lint-checks] needs: [install-dependencies] if: | + github.base_ref == 'master' || contains(github.base_ref, 'staging') || contains(github.base_ref, 'production') || contains(needs.install-dependencies.outputs.all_modified_files, 'packages/protocol') || + contains(needs.install-dependencies.outputs.all_modified_files, 'packages/sdk') || + contains(needs.install-dependencies.outputs.all_modified_files, 'packages/typescript') || contains(needs.install-dependencies.outputs.all_modified_files, ',package.json') || contains(needs.install-dependencies.outputs.all_modified_files, ',yarn.lock') || - github.event_name == 'pull_request' && contains(github.base_ref, 'release/') || false outputs: protocol-test-must-run: ${{ steps.protocol-test-must-run.outputs.PROTOCOL_TEST_MUST_RUN }} @@ -250,30 +251,36 @@ jobs: run: | yarn --cwd packages/protocol check-opcodes - uses: actions/setup-node@v3 + # with: + # node-version: 12.22.11 + # Workaround for https://stackoverflow.com/questions/72978485/git-submodule-update-failed-with-fatal-detected-dubious-ownership-in-repositor + - name: Configure git safe directories + run: git config --global --add safe.directory '*' + - name: Setup tmate session + uses: mxschmitt/action-tmate@v3 + timeout-minutes: 60 + if: false with: - node-version: 12.22.11 + limit-access-to-actor: true - name: Generate devchain of previous release - uses: nick-fields/retry@v2 - with: - timeout_minutes: 60 - max_attempts: 3 - command: | - echo "Comparing against $RELEASE_TAG" - # Github has phased out the git protocol so we ensure that we use - # https for all git operations that yarn may perform. - git config --global url."https://github.com".insteadOf git://github.com - yarn --cwd packages/protocol test:generate-old-devchain-and-build -b $RELEASE_TAG -d .tmp/released_chain -l /dev/stdout -g scripts/truffle/releaseGoldExampleConfigs.json - + run: | + echo "Comparing against $RELEASE_TAG" + # Github has phased out the git protocol so we ensure that we use + # https for all git operations that yarn may perform. + git config --global url."https://github.com".insteadOf git://github.com + yarn --cwd packages/protocol test:generate-old-devchain-and-build -b $RELEASE_TAG -d .tmp/released_chain -l /dev/stdout -g scripts/truffle/releaseGoldExampleConfigs.json protocol-test-release: name: Protocol Test Release - runs-on: ["self-hosted", "monorepo-node18"] - timeout-minutes: 30 + runs-on: ['self-hosted', 'monorepo-node18'] + timeout-minutes: 500 needs: [install-dependencies, lint-checks, pre-protocol-test-release] if: | + github.base_ref == 'master' || contains(github.base_ref, 'staging') || contains(github.base_ref, 'production') || contains(needs.install-dependencies.outputs.all_modified_files, 'packages/protocol') || + contains(needs.install-dependencies.outputs.all_modified_files, 'packages/sdk') || + contains(needs.install-dependencies.outputs.all_modified_files, 'packages/typescript') || contains(needs.install-dependencies.outputs.all_modified_files, ',package.json') || contains(needs.install-dependencies.outputs.all_modified_files, ',yarn.lock') || - github.event_name == 'pull_request' && contains(github.base_ref, 'release/') || false steps: - uses: actions/cache/restore@v3 @@ -290,34 +297,38 @@ jobs: run: | BUILD_AND_DEVCHAIN_DIR=$(echo build/$(echo $RELEASE_TAG | sed -e 's/\//_/g')) (cp -r packages/protocol/.tmp/released_chain packages/protocol/$BUILD_AND_DEVCHAIN_DIR) - - name: Setup tmate session - uses: mxschmitt/action-tmate@v3 - timeout-minutes: 20 - if: false - with: - limit-access-to-actor: true - name: Test against current release run: | echo "Comparing against $RELEASE_TAG" + sudo apt-get update + sudo apt-get install vim lsof --yes BUILD_AND_DEVCHAIN_DIR=$(echo build/$(echo $RELEASE_TAG | sed -e 's/\//_/g')) yarn --cwd packages/protocol test:devchain-release -b $RELEASE_TAG -d $BUILD_AND_DEVCHAIN_DIR -l /dev/stdout + # - name: Setup tmate session + # uses: mxschmitt/action-tmate@v3 + # timeout-minutes: 500 + # if: true + # with: + # limit-access-to-actor: true protocol-test-matrix: - # Keeping name short because GitHub UI does not handle long names well + # Keeping name short because GitHub UI does not handle long names well name: ${{ matrix.name }} - runs-on: ["self-hosted", "monorepo-node18"] - timeout-minutes: 120 + runs-on: ['self-hosted', 'monorepo-node18'] + timeout-minutes: 60 needs: [install-dependencies, lint-checks] if: | + github.base_ref == 'master' || contains(github.base_ref, 'staging') || contains(github.base_ref, 'production') || contains(needs.install-dependencies.outputs.all_modified_files, 'packages/protocol') || + contains(needs.install-dependencies.outputs.all_modified_files, 'packages/sdk') || + contains(needs.install-dependencies.outputs.all_modified_files, 'packages/typescript') || contains(needs.install-dependencies.outputs.all_modified_files, ',package.json') || contains(needs.install-dependencies.outputs.all_modified_files, ',yarn.lock') || - github.event_name == 'pull_request' && contains(github.base_ref, 'release/') || false strategy: fail-fast: false matrix: include: - - name: Protocol Reslease Snapshots + - name: Protocol Release Snapshots command: | yarn --cwd packages/protocol test:release-snapshots if [[ $(git status packages/protocol/releaseData/versionReports --porcelain) ]]; then @@ -366,25 +377,30 @@ jobs: yarn --cwd packages/protocol build - name: Setup tmate session uses: mxschmitt/action-tmate@v3 - if: false - timeout-minutes: 120 + timeout-minutes: 20 + if: contains(matrix.command, 'common/') && false + with: + limit-access-to-actor: true - name: Execute matrix command for test uses: nick-fields/retry@v2 with: - timeout_minutes: 60 + timeout_minutes: 40 max_attempts: 3 command: | ${{ matrix.command }} contractkit-tests: name: ContractKit Tests - runs-on: ["self-hosted", "monorepo-node18"] + runs-on: ['self-hosted', 'monorepo-node18'] timeout-minutes: 30 needs: [install-dependencies] if: | - contains(needs.install-dependencies.outputs.all_modified_files, 'packages/contractkit') || + github.base_ref == 'master' || contains(github.base_ref, 'staging') || contains(github.base_ref, 'production') || + contains(needs.install-dependencies.outputs.all_modified_files, 'packages/protocol') || + contains(needs.install-dependencies.outputs.all_modified_files, 'packages/dev-utils') || + contains(needs.install-dependencies.outputs.all_modified_files, 'packages/sdk') || + contains(needs.install-dependencies.outputs.all_modified_files, 'packages/typescript') || contains(needs.install-dependencies.outputs.all_modified_files, ',package.json') || contains(needs.install-dependencies.outputs.all_modified_files, ',yarn.lock') || - github.event_name == 'pull_request' && contains(github.base_ref, 'release/') || false steps: - uses: actions/cache/restore@v3 @@ -404,74 +420,18 @@ jobs: - name: Run tests run: | yarn --cwd=packages/sdk/contractkit test - identity-tests: - name: Identity Tests - runs-on: ["self-hosted", "monorepo-node18"] - timeout-minutes: 30 - needs: [install-dependencies, contractkit-tests] - if: | - contains(needs.install-dependencies.outputs.all_modified_files, 'packages/identity') || - contains(needs.install-dependencies.outputs.all_modified_files, ',package.json') || - contains(needs.install-dependencies.outputs.all_modified_files, ',yarn.lock') || - false - steps: - - uses: actions/cache/restore@v3 - id: cache_git - with: - path: .git - key: git-${{ github.ref }} - - uses: actions/checkout@v3 - - name: Sync workspace - uses: ./.github/actions/sync-workspace - with: - package-json-checksum: ${{ needs.install-dependencies.outputs.package-json-checksum }} - - name: Generate DevChain - run: | - cd packages/sdk/identity - yarn test:reset - - name: Run tests - run: | - yarn --cwd=packages/sdk/identity test - transactions-uri-tests: - name: Transaction URI Tests - runs-on: ["self-hosted", "monorepo-node18"] - timeout-minutes: 30 - needs: [install-dependencies, contractkit-tests] - if: | - contains(needs.install-dependencies.outputs.all_modified_files, 'packages/sdk/transactions-uri') || - contains(needs.install-dependencies.outputs.all_modified_files, ',package.json') || - contains(needs.install-dependencies.outputs.all_modified_files, ',yarn.lock') || - false - steps: - - uses: actions/cache/restore@v3 - id: cache_git - with: - path: .git - key: git-${{ github.ref }} - - uses: actions/checkout@v3 - - name: Sync workspace - uses: ./.github/actions/sync-workspace - with: - package-json-checksum: ${{ needs.install-dependencies.outputs.package-json-checksum }} - - name: Get changed files - id: changed-files - uses: tj-actions/changed-files@v35 - with: - fetch_depth: "100" - - name: Generate DevChain - run: | - cd packages/sdk/identity - yarn test:reset - - name: Run tests - run: | - yarn --cwd=packages/sdk/identity test cli-tests: name: CeloCli Tests - runs-on: ["self-hosted", "monorepo-node18"] + runs-on: ['self-hosted', 'monorepo-node18'] timeout-minutes: 30 needs: [install-dependencies] if: | + github.base_ref == 'master' || contains(github.base_ref, 'staging') || contains(github.base_ref, 'production') || contains(needs.install-dependencies.outputs.all_modified_files, 'packages/cli') || + contains(needs.install-dependencies.outputs.all_modified_files, 'packages/protocol') || + contains(needs.install-dependencies.outputs.all_modified_files, 'packages/dev-utils') || + contains(needs.install-dependencies.outputs.all_modified_files, 'packages/sdk') || + contains(needs.install-dependencies.outputs.all_modified_files, 'packages/typescript') || contains(needs.install-dependencies.outputs.all_modified_files, ',package.json') || contains(needs.install-dependencies.outputs.all_modified_files, ',yarn.lock') || false @@ -506,10 +466,11 @@ jobs: yarn --cwd=packages/cli run celocli account:new typescript-tests: name: Typescript package Tests - runs-on: ["self-hosted", "monorepo-node18"] + runs-on: ['self-hosted', 'monorepo-node18'] timeout-minutes: 30 needs: [install-dependencies] if: | + github.base_ref == 'master' || contains(github.base_ref, 'staging') || contains(github.base_ref, 'production') || contains(needs.install-dependencies.outputs.all_modified_files, 'packages/typescript') || contains(needs.install-dependencies.outputs.all_modified_files, ',package.json') || contains(needs.install-dependencies.outputs.all_modified_files, ',yarn.lock') || @@ -534,11 +495,13 @@ jobs: npm install $RUNNER_WORKSPACE/celo-monorepo/packages/typescript/*.tgz base-test: name: SDK Base package Tests - runs-on: ["self-hosted", "monorepo-node18"] + runs-on: ['self-hosted', 'monorepo-node18'] timeout-minutes: 30 needs: [install-dependencies] if: | + github.base_ref == 'master' || contains(github.base_ref, 'staging') || contains(github.base_ref, 'production') || contains(needs.install-dependencies.outputs.all_modified_files, 'packages/sdk') || + contains(needs.install-dependencies.outputs.all_modified_files, 'packages/typescript') || contains(needs.install-dependencies.outputs.all_modified_files, ',package.json') || contains(needs.install-dependencies.outputs.all_modified_files, ',yarn.lock') || false @@ -568,11 +531,13 @@ jobs: npm install $RUNNER_WORKSPACE/celo-monorepo/packages/sdk/base/*.tgz utils-test: name: SDK Utils package Tests - runs-on: ["self-hosted", "monorepo-node18"] + runs-on: ['self-hosted', 'monorepo-node18'] timeout-minutes: 30 needs: [install-dependencies] if: | + github.base_ref == 'master' || contains(github.base_ref, 'staging') || contains(github.base_ref, 'production') || contains(needs.install-dependencies.outputs.all_modified_files, 'packages/sdk') || + contains(needs.install-dependencies.outputs.all_modified_files, 'packages/typescript') || contains(needs.install-dependencies.outputs.all_modified_files, ',package.json') || contains(needs.install-dependencies.outputs.all_modified_files, ',yarn.lock') || false @@ -601,13 +566,16 @@ jobs: end-to-end-geth-matrix: # Keeping name short because GitHub UI does not handle long names well name: e2e ${{ matrix.name }} - runs-on: ["self-hosted", "monorepo-node18"] + runs-on: ['self-hosted', 'monorepo-node18'] timeout-minutes: 60 needs: [install-dependencies, lint-checks, contractkit-tests] if: | - contains(needs.install-dependencies.outputs.all_modified_files, 'packages/protocol') || + github.base_ref == 'master' || contains(github.base_ref, 'staging') || contains(github.base_ref, 'production') || contains(needs.install-dependencies.outputs.all_modified_files, 'packages/celotool') || - contains(needs.install-dependencies.outputs.all_modified_files, 'packages/sdk/contractkit') || + contains(needs.install-dependencies.outputs.all_modified_files, 'packages/protocol') || + contains(needs.install-dependencies.outputs.all_modified_files, 'packages/dev-utils') || + contains(needs.install-dependencies.outputs.all_modified_files, 'packages/sdk') || + contains(needs.install-dependencies.outputs.all_modified_files, 'packages/typescript') || contains(needs.install-dependencies.outputs.all_modified_files, ',package.json') || contains(needs.install-dependencies.outputs.all_modified_files, ',yarn.lock') || false @@ -652,20 +620,20 @@ jobs: export PATH="/usr/local/go/bin:$HOME/.cargo/bin:${PATH}" cd packages/celotool ./ci_test_sync.sh checkout ${CELO_BLOCKCHAIN_BRANCH_TO_TEST} + - name: CIP35 eth compatibility test + command: | + set -e + export PATH="/usr/local/go/bin:$HOME/.cargo/bin:${PATH}" + cd packages/celotool + echo "Test is skipped because migrations somehow fail" + # ./ci_test_cip35.sh checkout ${CELO_BLOCKCHAIN_BRANCH_TO_TEST} - name: Validator order test command: | set -e export PATH="/usr/local/go/bin:$HOME/.cargo/bin:${PATH}" cd packages/celotool + ./ci_test_validator_order.sh checkout ${CELO_BLOCKCHAIN_BRANCH_TO_TEST} - - name: CIP35 eth compatibility test - command: | - echo "Test skipped, see issue https://github.com/celo-org/celo-monorepo/issues/10556" - - # set -e - # export PATH="/usr/local/go/bin:$HOME/.cargo/bin:${PATH}" - # cd packages/celotool - # ./ci_test_cip35.sh checkout ${CELO_BLOCKCHAIN_BRANCH_TO_TEST} steps: - uses: actions/cache/restore@v3 id: cache_git @@ -682,59 +650,30 @@ jobs: rebuild-package: 'true' - name: Setup tmate session uses: mxschmitt/action-tmate@v3 - timeout-minutes: 120 - if: contains(matrix.command, 'ci_test_cip35.sh') + timeout-minutes: 20 + if: contains(matrix.command, 'ci_test_transfers.sh') && false with: limit-access-to-actor: true - name: Execute matrix command for test uses: nick-fields/retry@v2 with: - timeout_minutes: 60 + timeout_minutes: 30 max_attempts: 3 command: | ${{ matrix.command }} - odis-test: - name: ODIS test - runs-on: ["self-hosted", "monorepo-node18"] - timeout-minutes: 30 - needs: [install-dependencies, lint-checks] - if: | - contains(needs.install-dependencies.outputs.all_modified_files, 'packages/phone-number-privacy') || - contains(needs.install-dependencies.outputs.all_modified_files, ',package.json') || - contains(needs.install-dependencies.outputs.all_modified_files, ',yarn.lock') || - false - steps: - - uses: actions/cache/restore@v3 - id: cache_git - with: - path: .git - key: git-${{ github.ref }} - - uses: actions/checkout@v3 - - name: Sync workspace - uses: ./.github/actions/sync-workspace - with: - package-json-checksum: ${{ needs.install-dependencies.outputs.package-json-checksum }} - - name: Run Tests for common package - run: | - yarn --cwd=packages/phone-number-privacy/common test:coverage - - name: Run Tests for combiner - run: | - yarn --cwd=packages/phone-number-privacy/combiner test:coverage - - name: Run Tests for signer - run: | - yarn --cwd=packages/phone-number-privacy/signer test:coverage - # NOTE: This has not been fully tested as we don't have a license for certora certora-test: name: Certora test ${{ matrix.name }} - runs-on: ["self-hosted", "monorepo-node18"] + runs-on: ['self-hosted', 'monorepo-node18'] timeout-minutes: 30 needs: [install-dependencies, lint-checks] # Disable as certora license is not active if: | false && ( + github.base_ref == 'master' || contains(github.base_ref, 'staging') || contains(github.base_ref, 'production') || contains(needs.install-dependencies.outputs.all_modified_files, 'packages/protocol') || + contains(needs.install-dependencies.outputs.all_modified_files, 'packages/sdk') || contains(needs.install-dependencies.outputs.all_modified_files, ',package.json') || contains(needs.install-dependencies.outputs.all_modified_files, ',yarn.lock') ) diff --git a/.github/workflows/container-all-monorepo.yml b/.github/workflows/container-all-monorepo.yml new file mode 100644 index 00000000000..682f64d733b --- /dev/null +++ b/.github/workflows/container-all-monorepo.yml @@ -0,0 +1,42 @@ +--- +name: Build celo-monorepo container + +on: + push: + paths: + - 'dockerfiles/all-monorepo/**' + branches: + - master + pull_request: + paths: + - 'dockerfiles/all-monorepo/**' + workflow_dispatch: + +jobs: + celomonorepo-build-dev: + uses: celo-org/reusable-workflows/.github/workflows/container-cicd.yaml@v1.8 + name: Build us-west1-docker.pkg.dev/devopsre/dev-images/monorepo:${{ github.sha }} + if: | + github.ref != 'refs/heads/master' + with: + workload-id-provider: projects/1094498259535/locations/global/workloadIdentityPools/gh-celo-monorepo/providers/github-by-repos + service-account: 'celo-monorepo-dev@devopsre.iam.gserviceaccount.com' + artifact-registry: us-west1-docker.pkg.dev/devopsre/dev-images/monorepo + tag: ${{ github.sha }} + context: . + file: dockerfiles/all-monorepo/Dockerfile + trivy: true + + celomonorepo-build: + uses: celo-org/reusable-workflows/.github/workflows/container-cicd.yaml@v1.8 + name: Build us-west1-docker.pkg.dev/devopsre/celo-monorepo/monorepo:${{ github.sha }} + if: | + github.ref == 'refs/heads/master' + with: + workload-id-provider: projects/1094498259535/locations/global/workloadIdentityPools/gh-celo-monorepo-master/providers/github-by-repos + service-account: 'celo-monorepo@devopsre.iam.gserviceaccount.com' + artifact-registry: us-west1-docker.pkg.dev/devopsre/celo-monorepo/monorepo + tag: ${{ github.sha }} + context: . + file: dockerfiles/all-monorepo/Dockerfile + trivy: true diff --git a/.github/workflows/container-celotool.yml b/.github/workflows/container-celotool.yml index 8f57593c570..63e121c840f 100644 --- a/.github/workflows/container-celotool.yml +++ b/.github/workflows/container-celotool.yml @@ -1,5 +1,5 @@ --- -name: Build celo-monorepo container +name: Build celotool container on: push: @@ -15,26 +15,28 @@ on: jobs: celotool-build-dev: uses: celo-org/reusable-workflows/.github/workflows/container-cicd.yaml@v1.8 - name: Build us-west1-docker.pkg.dev/devopsre/dev-images/celotool:testing + name: Build us-west1-docker.pkg.dev/devopsre/dev-images/celotool:${{ github.sha }} if: | github.ref != 'refs/heads/master' with: workload-id-provider: projects/1094498259535/locations/global/workloadIdentityPools/gh-celo-monorepo/providers/github-by-repos service-account: 'celo-monorepo-dev@devopsre.iam.gserviceaccount.com' artifact-registry: us-west1-docker.pkg.dev/devopsre/dev-images/celotool - tag: testing + tag: ${{ github.sha }} context: . file: dockerfiles/celotool/Dockerfile + trivy: true celotool-build: uses: celo-org/reusable-workflows/.github/workflows/container-cicd.yaml@v1.8 - name: Build us-west1-docker.pkg.dev/devopsre/celo-monorepo/celotool:latest + name: Build us-west1-docker.pkg.dev/devopsre/celo-monorepo/celotool:${{ github.sha }} if: | github.ref == 'refs/heads/master' with: workload-id-provider: projects/1094498259535/locations/global/workloadIdentityPools/gh-celo-monorepo-master/providers/github-by-repos service-account: 'celo-monorepo@devopsre.iam.gserviceaccount.com' artifact-registry: us-west1-docker.pkg.dev/devopsre/celo-monorepo/celotool - tag: latest + tag: ${{ github.sha }} context: . file: dockerfiles/celotool/Dockerfile + trivy: true diff --git a/.github/workflows/container-circleci.yml b/.github/workflows/container-circleci.yml index b8bc254a6e3..3ff53c3f26d 100644 --- a/.github/workflows/container-circleci.yml +++ b/.github/workflows/container-circleci.yml @@ -47,6 +47,7 @@ jobs: artifact-registry: us-west1-docker.pkg.dev/devopsre/dev-images/circleci-geth tag: testing context: dockerfiles/circleci + trivy: true geth-build: uses: celo-org/reusable-workflows/.github/workflows/container-cicd.yaml@v1.8 @@ -61,6 +62,7 @@ jobs: artifact-registry: us-west1-docker.pkg.dev/devopsre/celo-monorepo/circleci-geth tag: latest context: dockerfiles/circleci + trivy: true node12-build-dev: uses: celo-org/reusable-workflows/.github/workflows/container-cicd.yaml@v1.8 @@ -75,6 +77,7 @@ jobs: artifact-registry: us-west1-docker.pkg.dev/devopsre/dev-images/circleci-node12 tag: testing context: dockerfiles/circleci + trivy: true node12-build: uses: celo-org/reusable-workflows/.github/workflows/container-cicd.yaml@v1.8 @@ -89,4 +92,5 @@ jobs: artifact-registry: us-west1-docker.pkg.dev/devopsre/celo-monorepo/circleci-node12 tag: latest context: dockerfiles/circleci + trivy: true diff --git a/.github/workflows/container-cli.yml b/.github/workflows/container-cli.yml index 84a4d794cca..12a2994421e 100644 --- a/.github/workflows/container-cli.yml +++ b/.github/workflows/container-cli.yml @@ -25,6 +25,7 @@ jobs: tag: testing context: . file: dockerfiles/cli-standalone/Dockerfile + trivy: true celocli-build: uses: celo-org/reusable-workflows/.github/workflows/container-cicd.yaml@v1.8 @@ -38,3 +39,4 @@ jobs: tag: latest context: . file: dockerfiles/cli-standalone/Dockerfile + trivy: true diff --git a/.github/workflows/cron-npm-install.yml b/.github/workflows/cron-npm-install.yml index 5ec884dad4b..811c3cd17de 100644 --- a/.github/workflows/cron-npm-install.yml +++ b/.github/workflows/cron-npm-install.yml @@ -15,7 +15,7 @@ jobs: name: ${{ matrix.package }} NPM package install runs-on: ubuntu-latest container: - image: node:14-bullseye + image: node:16-bullseye strategy: fail-fast: false matrix: @@ -29,7 +29,7 @@ jobs: if: matrix.package == '@celo/celocli' run: | apt update - apt install -y libusb-1.0-0-dev + apt install -y libusb-1.0-0-dev libudev-dev - name: Installing npm package run: yarn add ${{ matrix.package }} - name: Test celocli command diff --git a/.github/workflows/cron-protocol-test-with-coverage.yml b/.github/workflows/cron-protocol-test-with-coverage.yml index 4fab42b220e..e25e4ecd71b 100644 --- a/.github/workflows/cron-protocol-test-with-coverage.yml +++ b/.github/workflows/cron-protocol-test-with-coverage.yml @@ -4,8 +4,8 @@ name: Protocol tests with coverage # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule on: schedule: - # daily at 13:00 UTC - - cron: 0 13 * * * + # Every Saturday at 13:00 UTC + - cron: 0 13 * * 6 workflow_dispatch: jobs: diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 00000000000..df7dce4fad7 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,52 @@ +name: Release Packages + +on: + push: + branches: + - main + - 'prerelease/*' + +concurrency: ${{ github.workflow }}-${{ github.ref }} + +jobs: + release: + name: Release to NPM + runs-on: ['self-hosted', 'org', 'npm-publish'] + permissions: + contents: write + id-token: write + pull-requests: write + repository-projects: write + steps: + - name: Checkout Repo + uses: actions/checkout@v3 + + - name: Akeyless Get Secrets + id: get_auth_token + uses: + docker://us-west1-docker.pkg.dev/devopsre/akeyless-public/akeyless-action:latest + with: + api-url: https://api.gateway.akeyless.celo-networks-dev.org + access-id: p-kf9vjzruht6l + static-secrets: '{"/static-secrets/apps-tooling-circle/npm-publish-token":"NPM_TOKEN"}' + - name: Setup Node.js 18.x + uses: actions/setup-node@v3 + with: + node-version: 18.x + - name: 'Setup yarn' + shell: bash + run: | + npm install --global yarn + source ~/.bashrc + - name: Install Dependencies + shell: bash + run: yarn + - name: Create Release Pull Request or Publish to npm + id: changesets + uses: changesets/action@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ env.NPM_TOKEN }} + with: + # This expects you to have a script called release which does a build for your packages and calls changeset publish + publish: yarn release diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 0e2f1bdb5e8..b82acff108b 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -18,7 +18,7 @@ jobs: pull-requests: write steps: - - uses: actions/stale@v3 + - uses: actions/stale@v8 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-issue-message: 'This issue is stale and will be closed in 30 days without activity' diff --git a/.gitignore b/.gitignore index e16846bfe91..c81a7bffab4 100644 --- a/.gitignore +++ b/.gitignore @@ -96,12 +96,14 @@ packages/docs/_book/ # old packages packages/reserve-site/* packages/blockchain-api/* -packages/komencikit/* packages/mobile/* packages/faucet/* packages/moonpay-auth/* packages/notification-service/* packages/react-components/* +packages/phone-number-privacy/* +packages/sdk/identity/* +packages/sdk/encrypted-backup/* # temp json file for deploy-sdks script scripts/failedSDKs.json diff --git a/.vscode/launch.json b/.vscode/launch.json index f0d42e1317a..79c6a7e4ae8 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -48,22 +48,6 @@ "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "port": 9229 - }, - { - "name": "Debug Komencikit Tests", - "type": "node", - "request": "launch", - "runtimeArgs": [ - "--inspect-brk", - "${workspaceRoot}/node_modules/.bin/jest", - "--rootDir", - "${workspaceFolder}/packages/komencikit", - "--runInBand", - "${workspaceFolder}/packages/komencikit/src/kit.spec.ts", - ], - "console": "integratedTerminal", - "internalConsoleOptions": "neverOpen", - "port": 9229 } ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 8e336f891ec..928574330b1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -43,5 +43,6 @@ "javascript.format.enable": false, "editor.tabSize": 2, "editor.detectIndentation": false, - "tslint.jsEnable": true + "tslint.jsEnable": true, + "typescript.tsdk": "node_modules/typescript/lib" } diff --git a/README.md b/README.md index e1e32c72b26..ea5e9a098e4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@

- celo logo + celo logo

@@ -11,7 +11,7 @@ and other packages. The source code for the Celo Blockchain which operates a nod -[![CircleCI](https://img.shields.io/circleci/build/github/celo-org/celo-monorepo/master)](https://circleci.com/gh/celo-org/celo-monorepo/tree/master) +[![GitHub Actions](https://github.com/celo-org/celo-monorepo/actions/workflows/container-all-monorepo.yml/badge.svg)](https://github.com/celo-org/celo-monorepo/actions/workflows/container-all-monorepo.yml) [![Codecov](https://img.shields.io/codecov/c/github/celo-org/celo-monorepo)](https://codecov.io/gh/celo-org/celo-monorepo) [![GitHub contributors](https://img.shields.io/github/contributors/celo-org/celo-monorepo)](https://github.com/celo-org/celo-monorepo/graphs/contributors) [![GitHub commit activity](https://img.shields.io/github/commit-activity/w/celo-org/celo-monorepo)](https://github.com/celo-org/celo-monorepo/graphs/contributors) @@ -36,8 +36,6 @@ and other packages. The source code for the Celo Blockchain which operates a nod [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr-raw/celo-org/celo-monorepo)](https://github.com/celo-org/celo-monorepo/pulls) [![GitHub Issues](https://img.shields.io/github/issues-raw/celo-org/celo-monorepo.svg)](https://github.com/celo-org/celo-monorepo/issues) [![GitHub issues by-label](https://img.shields.io/github/issues/celo-org/celo-monorepo/1%20hour%20tasks)](https://github.com/celo-org/celo-monorepo/issues?q=is%3Aopen+is%3Aissue+label%3A%221+hour+tasks%22) -[![GitHub issues by-label](https://img.shields.io/github/issues/celo-org/celo-monorepo/betanet-phase-2)](https://github.com/celo-org/celo-monorepo/issues?q=is%3Aopen+is%3Aissue+label%3Abetanet-phase-2) -[![GitHub issues by-label](https://img.shields.io/github/issues/celo-org/celo-monorepo/betanet-phase-3)](https://github.com/celo-org/celo-monorepo/issues?q=is%3Aopen+is%3Aissue+label%3Abetanet-phase-3) Contents: @@ -96,7 +94,7 @@ The Celo stack is structured into the following logical layers: Follow the instructions in [SETUP.md](SETUP.md) to get a development environment set up. -See [Developer's Guide](https://docs.celo.org/) for full details about the design of the Celo protocol and other information about running these projects. +See [Developer's Guide](https://docs.celo.org/developer) for full details about the design of the Celo protocol and other information about running these projects. ## 🙋 Issues @@ -110,11 +108,10 @@ The repository has the following packages (sub projects): - [cli](packages/cli) - tool that uses ContractKit to interact with the Celo protocol ([docs](https://docs.celo.org/command-line-interface/introduction)) - [dev-utils](packages/dev-utils) - a utils package for use as a dev dependency - [docs](packages/docs) - technical documentation for the Celo project ([live](https://docs.celo.org/)) -- [helm-charts](packages/helm-charts) - templatized deployments of entire environments to Kubernetes clusters -- [protocol](packages/protocol) - identity, stability and other smart contracts for the Celo protocol ([docs](https://docs.celo.org/celo-codebase/protocol)) +- [helm-charts](packages/helm-charts) - (DEPRECATED) templatized deployments of entire environments to Kubernetes clusters. Check [celo-org/charts](https://github.com/celo-org/charts) instead. +- [protocol](packages/protocol) - identity, stability and other smart contracts for the Celo protocol ([docs](https://docs.celo.org/protocol)) - [sdk](packages/sdk) - Typescript packages for interacting with Celo, including Contracts, Wallets, Crypto++ -- [contractkit](packages/sdk/contractkit) - library to help developers and validators interact with the protocol and its smart contracts ([docs](https://docs.celo.org/developer-guide/contractkit)) -- [terraform-modules](packages/terraform-modules) - templatized deployments of entire VM-based testnets for Google Cloud Platform +- [contractkit](packages/sdk/contractkit) - library to help developers and validators interact with the protocol and its smart contracts ([docs](https://docs.celo.org/developer/contractkit)) - [typescript](packages/typescript) - no README available (improve?) Code owners for each package can be found in [.github/CODEOWNERS](.github/CODEOWNERS). diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 00000000000..d7d21d06dac --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,59 @@ +# Release Process + +This repo uses changesets to determine what packages need a version bump. + +Each PR MUST be accompanied by a changeset unless it has zero affect on package consumers (ie changing github action workflows). + +To create a changeset run `changeset add` (or `yarn cs`) + +This will bring up an interactive console which asks which packages are affect and if they require minor or major update. + +## Auto Releasing + +The Release.yaml workflow will create a PR called "Version Packages", each time a PR is merged to master with changeset files this PR will be rebased and updated to show what the versions would be of published then. Merging this PR in will lead to packages being built and published to npm and github release notes being published. + +## Manual Releasing (discouraged) + +when time to release new versions of npm package run `changeset version` this will look thru the changeset files that have been generated since last release to bump the versions for package automatically to major if any changesets specify major change minor if only minor and patch if a the package had no changesets of its own but depends on one which will be updated. + +finally `changeset publish` will go thru and publish to npm the packages that need publishing. + +after go ahead and run `git push --follow-tags` to push git tags up to github. + +## Pre Releasing + +changesets has 2 strategies for pre release versions. + +The first is to enter `pre` mode on changesets. [docs here](https://github.com/changesets/changesets/blob/main/docs/prereleases.md) + +``` +yarn changeset pre enter beta +yarn changeset version +git add . +git commit -m "Enter prerelease mode and version packages" +yarn changeset publish +git push --follow-tags +``` + +The other is to append --snapshot. which is great for daily releases. + +``` +yarn changeset version --snapshot canary + +yarn changeset publish --no-git-tag --snapshot + +``` + + + +## Package Versioning + +Based on semantic versioning best practices [semver.org](semver.org) + +Given a version number MAJOR.MINOR.PATCH, increment the: + +- MAJOR version when you make incompatible API changes +- MINOR version when you add functionality in a backward compatible manner +- PATCH version when you make backward compatible bug fixes + +Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format. diff --git a/cloudbuild.yaml b/cloudbuild.yaml index 2ee58d511fa..7c83c9d188c 100644 --- a/cloudbuild.yaml +++ b/cloudbuild.yaml @@ -23,15 +23,6 @@ steps: ] waitFor: ['-'] -- id: "docker:phone-number-privacy-signer" - name: gcr.io/kaniko-project/executor:v0.16.0 - args: [ - "--dockerfile=dockerfiles/phone-number-privacy/Dockerfile", - "--cache=true", - "--destination=us.gcr.io/$PROJECT_ID/celo-monorepo:phone-number-privacy-$COMMIT_SHA" - ] - waitFor: ['-'] - options: machineType: 'N1_HIGHCPU_8' diff --git a/codecov.yml b/codecov.yml index 8e550ab2025..fce14024b46 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,29 +1,23 @@ coverage: status: project: - default: off - mobile: + default: + target: auto + threshold: null + removed_code_behavior: adjust_base + celotool: paths: - - packages/mobile/ - flags: - - mobile - threshold: 5% - target: 70% - # Protocol codecov disabled until coverage tests are fixed. - #protocol: - #paths: 'packages/protocol/' - #flags: protocol - #threshold: 5% - #target: 90% - #if_no_uploads: error - #if_not_found: success - #if_ci_failed: error + - packages/celotool + celocli: + paths: + - packages/cli + odis: + paths: + - packages/phone-number-privacy + protocol: + paths: + - packages/protocol + sdk: + paths: + - packages/sdk patch: off - -flags: - mobile: - paths: - - packages/mobile/ - #protocol: - #paths: - #- packages/protocol/ diff --git a/dependency-graph.json b/dependency-graph.json index fd4f254c6d0..368209a15cc 100644 --- a/dependency-graph.json +++ b/dependency-graph.json @@ -9,9 +9,7 @@ "@celo/dev-utils", "@celo/env-tests", "@celo/explorer", - "@celo/flake-tracker", "@celo/governance", - "@celo/identity", "@celo/network-utils", "@celo/protocol", "@celo/utils" @@ -20,13 +18,13 @@ "@celo/celocli": { "location": "packages/cli", "dependencies": [ + "@celo/base", + "@celo/connect", "@celo/contractkit", "@celo/cryptographic-utils", "@celo/dev-utils", "@celo/explorer", - "@celo/flake-tracker", "@celo/governance", - "@celo/identity", "@celo/phone-utils", "@celo/utils", "@celo/wallet-hsm-azure", @@ -45,15 +43,10 @@ "@celo/connect", "@celo/contractkit", "@celo/cryptographic-utils", - "@celo/identity", "@celo/phone-utils", "@celo/utils" ] }, - "@celo/flake-tracker": { - "location": "packages/flake-tracker", - "dependencies": [] - }, "@celo/metadata-crawler": { "location": "packages/metadata-crawler", "dependencies": [ @@ -68,66 +61,19 @@ "@celo/base", "@celo/connect", "@celo/cryptographic-utils", - "@celo/flake-tracker", "@celo/phone-utils", "@celo/typescript", - "@celo/utils" + "@celo/utils", + "@celo/wallet-local" ] }, "@celo/typescript": { "location": "packages/typescript", "dependencies": [] }, - "@celo/phone-number-privacy-combiner": { - "location": "packages/phone-number-privacy/combiner", - "dependencies": [ - "@celo/contractkit", - "@celo/encrypted-backup", - "@celo/flake-tracker", - "@celo/identity", - "@celo/phone-number-privacy-common", - "@celo/phone-number-privacy-signer", - "@celo/phone-utils", - "@celo/utils" - ] - }, - "@celo/phone-number-privacy-common": { - "location": "packages/phone-number-privacy/common", - "dependencies": [ - "@celo/base", - "@celo/contractkit", - "@celo/phone-utils", - "@celo/utils", - "@celo/wallet-local" - ] - }, - "@celo/phone-number-privacy-monitor": { - "location": "packages/phone-number-privacy/monitor", - "dependencies": [ - "@celo/contractkit", - "@celo/cryptographic-utils", - "@celo/encrypted-backup", - "@celo/identity", - "@celo/phone-number-privacy-common", - "@celo/utils", - "@celo/wallet-local" - ] - }, - "@celo/phone-number-privacy-signer": { - "location": "packages/phone-number-privacy/signer", - "dependencies": [ - "@celo/base", - "@celo/contractkit", - "@celo/flake-tracker", - "@celo/phone-number-privacy-common", - "@celo/utils", - "@celo/wallet-hsm-azure" - ] - }, "@celo/base": { "location": "packages/sdk/base", "dependencies": [ - "@celo/flake-tracker", "@celo/typescript" ] }, @@ -135,7 +81,6 @@ "location": "packages/sdk/connect", "dependencies": [ "@celo/base", - "@celo/flake-tracker", "@celo/utils" ] }, @@ -145,7 +90,6 @@ "@celo/base", "@celo/connect", "@celo/dev-utils", - "@celo/flake-tracker", "@celo/phone-utils", "@celo/protocol", "@celo/utils", @@ -156,22 +100,10 @@ "location": "packages/sdk/cryptographic-utils", "dependencies": [ "@celo/base", - "@celo/flake-tracker", "@celo/typescript", "@celo/utils" ] }, - "@celo/encrypted-backup": { - "location": "packages/sdk/encrypted-backup", - "dependencies": [ - "@celo/base", - "@celo/dev-utils", - "@celo/flake-tracker", - "@celo/identity", - "@celo/phone-number-privacy-common", - "@celo/utils" - ] - }, "@celo/explorer": { "location": "packages/sdk/explorer", "dependencies": [ @@ -191,18 +123,6 @@ "@celo/utils" ] }, - "@celo/identity": { - "location": "packages/sdk/identity", - "dependencies": [ - "@celo/base", - "@celo/contractkit", - "@celo/dev-utils", - "@celo/flake-tracker", - "@celo/phone-number-privacy-common", - "@celo/utils", - "@celo/wallet-local" - ] - }, "@celo/keystores": { "location": "packages/sdk/keystores", "dependencies": [ @@ -213,15 +133,13 @@ "@celo/network-utils": { "location": "packages/sdk/network-utils", "dependencies": [ - "@celo/dev-utils", - "@celo/flake-tracker" + "@celo/dev-utils" ] }, "@celo/phone-utils": { "location": "packages/sdk/phone-utils", "dependencies": [ "@celo/base", - "@celo/flake-tracker", "@celo/typescript", "@celo/utils" ] @@ -232,15 +150,13 @@ "@celo/base", "@celo/connect", "@celo/contractkit", - "@celo/dev-utils", - "@celo/flake-tracker" + "@celo/dev-utils" ] }, "@celo/utils": { "location": "packages/sdk/utils", "dependencies": [ "@celo/base", - "@celo/flake-tracker", "@celo/typescript" ] }, @@ -265,6 +181,7 @@ "@celo/wallet-hsm-azure": { "location": "packages/sdk/wallets/wallet-hsm-azure", "dependencies": [ + "@celo/base", "@celo/connect", "@celo/utils", "@celo/wallet-base", @@ -291,6 +208,7 @@ "@celo/wallet-ledger": { "location": "packages/sdk/wallets/wallet-ledger", "dependencies": [ + "@celo/base", "@celo/connect", "@celo/utils", "@celo/wallet-base", @@ -316,6 +234,7 @@ "@celo/wallet-rpc": { "location": "packages/sdk/wallets/wallet-rpc", "dependencies": [ + "@celo/base", "@celo/connect", "@celo/contractkit", "@celo/dev-utils", diff --git a/dockerfiles/all-monorepo/Dockerfile b/dockerfiles/all-monorepo/Dockerfile new file mode 100644 index 00000000000..1b6a53ff1a2 --- /dev/null +++ b/dockerfiles/all-monorepo/Dockerfile @@ -0,0 +1,26 @@ +FROM node:18 +LABEL org.opencontainers.image.authors="devops@clabs.co" + +WORKDIR /celo-monorepo + +# Needed for gsutil +RUN apt-get update && \ + apt-get install -y lsb-release && \ + apt-get install -y curl build-essential git python3 && \ + export CLOUD_SDK_REPO="cloud-sdk-$(lsb_release -c -s)" && \ + echo "deb http://packages.cloud.google.com/apt $CLOUD_SDK_REPO main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list && \ + curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - && \ + apt-get update -y && \ + apt-get install -y google-cloud-sdk kubectl netcat-openbsd && \ + curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash && \ + rm -rf /var/lib/apt/lists/* + +# ensure yarn.lock is evaluated by kaniko cache diff +COPY . ./ + +RUN yarn install --network-timeout 100000 --frozen-lockfile && yarn cache clean +RUN yarn build + +RUN rm -rf .git +RUN rm -rf .gitmodules + diff --git a/dockerfiles/celotool/Dockerfile b/dockerfiles/celotool/Dockerfile index 9cb74212fb5..80f1b556801 100644 --- a/dockerfiles/celotool/Dockerfile +++ b/dockerfiles/celotool/Dockerfile @@ -1,4 +1,6 @@ FROM node:18 +LABEL org.opencontainers.image.authors="devops@clabs.co" + WORKDIR /celo-monorepo # Needed for gsutil @@ -9,7 +11,7 @@ RUN apt-get update && \ echo "deb http://packages.cloud.google.com/apt $CLOUD_SDK_REPO main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list && \ curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - && \ apt-get update -y && \ - apt-get install -y google-cloud-sdk kubectl netcat && \ + apt-get install -y google-cloud-sdk kubectl netcat-openbsd && \ curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash && \ rm -rf /var/lib/apt/lists/* @@ -18,8 +20,6 @@ COPY lerna.json package.json yarn.lock ./ COPY packages/celotool/package.json packages/celotool/ COPY packages/dev-utils/package.json packages/dev-utils/ COPY packages/env-tests/package.json packages/env-tests/package.json -COPY packages/flake-tracker/package.json packages/flake-tracker/package.json -COPY packages/phone-number-privacy/common/package.json packages/phone-number-privacy/common/package.json COPY packages/protocol/package.json packages/protocol/ COPY packages/sdk/base/package.json packages/sdk/base/ COPY packages/sdk/connect/package.json packages/sdk/connect/ @@ -27,7 +27,6 @@ COPY packages/sdk/contractkit/package.json packages/sdk/contractkit/ COPY packages/sdk/cryptographic-utils/package.json packages/sdk/cryptographic-utils/ COPY packages/sdk/explorer/package.json packages/sdk/explorer/ COPY packages/sdk/governance/package.json packages/sdk/governance/ -COPY packages/sdk/identity/package.json packages/sdk/identity/ COPY packages/sdk/network-utils/package.json packages/sdk/network-utils/ COPY packages/sdk/utils/package.json packages/sdk/utils/ COPY packages/sdk/wallets/wallet-base/package.json packages/sdk/wallets/wallet-base/ @@ -37,17 +36,15 @@ COPY patches/ patches/ COPY scripts/ scripts/ COPY packages/sdk/phone-utils/package.json packages/sdk/phone-utils/package.json # Makes build fail if it doesn't copy git, will be removed after build -COPY .git .git -COPY .gitmodules .gitmodules +COPY .git .git +COPY .gitmodules .gitmodules RUN yarn install --network-timeout 100000 --frozen-lockfile && yarn cache clean COPY packages/celotool packages/celotool/ COPY packages/dev-utils packages/dev-utils/ COPY packages/env-tests packages/env-tests -COPY packages/flake-tracker packages/flake-tracker COPY packages/helm-charts packages/helm-charts -COPY packages/phone-number-privacy/common packages/phone-number-privacy/common COPY packages/protocol packages/protocol/ COPY packages/sdk/base packages/sdk/base/ COPY packages/sdk/connect packages/sdk/connect/ @@ -55,7 +52,6 @@ COPY packages/sdk/contractkit packages/sdk/contractkit/ COPY packages/sdk/cryptographic-utils packages/sdk/cryptographic-utils/ COPY packages/sdk/explorer packages/sdk/explorer/ COPY packages/sdk/governance packages/sdk/governance/ -COPY packages/sdk/identity packages/sdk/identity/ COPY packages/sdk/network-utils packages/sdk/network-utils/ COPY packages/sdk/utils packages/sdk/utils/ COPY packages/sdk/wallets/wallet-base packages/sdk/wallets/wallet-base/ diff --git a/dockerfiles/cli-standalone/Dockerfile b/dockerfiles/cli-standalone/Dockerfile index 3570f75885b..89a3759f06e 100644 --- a/dockerfiles/cli-standalone/Dockerfile +++ b/dockerfiles/cli-standalone/Dockerfile @@ -3,7 +3,8 @@ # Example build command: # # VERSION=x.y.z; docker build . --build-arg VERSION=$VERSION -t gcr.io/celo-testnet/celocli-standalone:$VERSION -FROM node:12.22.12-alpine3.15 +FROM node:12-alpine +LABEL org.opencontainers.image.authors="devops@clabs.co" # Install cli install dependencies. RUN apk add --no-cache python3 git make gcc g++ bash libusb-dev linux-headers eudev-dev diff --git a/dockerfiles/cli/Dockerfile b/dockerfiles/cli/Dockerfile index f50b70e9a5d..1f713913177 100644 --- a/dockerfiles/cli/Dockerfile +++ b/dockerfiles/cli/Dockerfile @@ -47,6 +47,7 @@ RUN npm install @celo/celocli # Build the combined image FROM node:12-alpine as final_image +LABEL org.opencontainers.image.authors="devops@clabs.co" ARG network_name="alfajores" ARG network_id="44787" diff --git a/dockerfiles/leaderboard/Dockerfile b/dockerfiles/leaderboard/Dockerfile index e862488fc63..2d8240ea85f 100644 --- a/dockerfiles/leaderboard/Dockerfile +++ b/dockerfiles/leaderboard/Dockerfile @@ -17,7 +17,6 @@ COPY packages/sdk/wallets/wallet-local/package.json packages/sdk/wallets/wallet- COPY packages/protocol/package.json packages/protocol/ COPY packages/sdk/contractkit/package.json packages/sdk/contractkit/ COPY packages/leaderboard/package.json packages/leaderboard/ -COPY packages/flake-tracker/package.json packages/flake-tracker/package.json RUN yarn install --frozen-lockfile --network-timeout 100000 && yarn cache clean @@ -32,7 +31,6 @@ COPY packages/sdk/wallets/wallet-local packages/sdk/wallets/wallet-local COPY packages/protocol packages/protocol/ COPY packages/sdk/contractkit packages/sdk/contractkit/ COPY packages/leaderboard packages/leaderboard/ -COPY packages/flake-tracker packages/flake-tracker # build all RUN yarn build diff --git a/lerna.json b/lerna.json index 3be4bcdab0a..6186a6723fa 100644 --- a/lerna.json +++ b/lerna.json @@ -2,7 +2,6 @@ "lerna": "2.10.1", "packages": [ "packages/*", - "packages/phone-number-privacy/*", "packages/sdk/*", "packages/sdk/wallets/*" ], diff --git a/package.json b/package.json index d9583d26ce3..383e2520367 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "license": "SEE LICENSE IN SUB-PACKAGES", "private": true, "scripts": { + "preinstall": "git config --global url.\"https://\".insteadOf ssh://", "install-pkg": "yarn install --link-duplicates", "lint": "yarn lerna run lint && yarn run --silent lint:do-not-merge", "lint:do-not-merge": "! git grep -E 'DO[ _]*NOT[ _]*MERGE'", @@ -19,6 +20,7 @@ "build": "yarn run lerna run build", "clean": "yarn run lerna run clean", "docs": "yarn run lerna run docs", + "cs" : "yarn changeset", "check-licenses": "yarn licenses list --prod | grep '\\(─ GPL\\|─ (GPL-[1-9]\\.[0-9]\\+ OR GPL-[1-9]\\.[0-9]\\+)\\)' && echo 'Found GPL license(s). Use 'yarn licenses list --prod' to look up the offending package' || echo 'No GPL licenses found'", "report-coverage": "yarn run lerna run test-coverage", "test:watch": "node node_modules/jest/bin/jest.js --watch", @@ -26,10 +28,10 @@ "keys:decrypt": "bash scripts/key_placer.sh decrypt", "keys:encrypt": "bash scripts/key_placer.sh encrypt", "check:packages": "node ./scripts/check-packages.js", + "release": "yarn build && yarn cs publish", "celotool": "yarn --cwd packages/celotool run --silent cli", "celocli": "yarn --cwd packages/cli run --silent celocli", "update-dependency-graph": "ts-node ./scripts/update_dependency_graph.ts", - "deploy-sdks": "ts-node ./scripts/deploy-sdks.ts", "deprecate-sdks": "ts-node ./scripts/unpublish-sdks.ts" }, "husky": { @@ -41,14 +43,12 @@ "workspaces": { "packages": [ "packages/*", - "packages/phone-number-privacy/*", "packages/sdk/*", "packages/sdk/wallets/*" ], "nohoist": [ "**/openzeppelin-solidity", - "**/solidity-bytes-utils", - "**/@apollo/react-testing" + "**/solidity-bytes-utils" ] }, "devDependencies": { @@ -84,8 +84,12 @@ }, "resolutions": { "ganache": "npm:@soloseng/ganache@7.8.0-beta.1", + "bip39": "https://github.com/bitcoinjs/bip39#d8ea080a18b40f301d4e2219a2991cd2417e83c2", + "blind-threshold-bls": "npm:@celo/blind-threshold-bls@1.0.0-beta", "@types/bn.js": "4.11.6", "bignumber.js": "9.0.0" + }, + "dependencies": { + "@changesets/cli": "^2.26.2" } -} - +} \ No newline at end of file diff --git a/packages/celotool/.mocharc.js b/packages/celotool/.mocharc.js deleted file mode 100644 index c4e396952c3..00000000000 --- a/packages/celotool/.mocharc.js +++ /dev/null @@ -1,3 +0,0 @@ -const flakeTrackingConfig = require('@celo/flake-tracker/src/mocha/config.js') - -module.exports = flakeTrackingConfig diff --git a/packages/celotool/CHANGELOG.md b/packages/celotool/CHANGELOG.md new file mode 100644 index 00000000000..abdd907442d --- /dev/null +++ b/packages/celotool/CHANGELOG.md @@ -0,0 +1,45 @@ +# @celo/celotool + +## 1.0.1 + +### Patch Changes + +- Updated dependencies [d48c68afc] +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [d48c68afc] +- Updated dependencies [d48c68afc] +- Updated dependencies [d48c68afc] + - @celo/contractkit@5.1.0 + - @celo/connect@5.1.0 + - @celo/cryptographic-utils@5.0.5 + - @celo/governance@5.0.5 + - @celo/explorer@5.0.5 + - @celo/utils@5.0.5 + - @celo/base@5.0.5 + - @celo/network-utils@5.0.5 + - @celo/env-tests@1.0.1 + +## 1.0.1-beta.0 + +### Patch Changes + +- Updated dependencies [d48c68afc] +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [d48c68afc] +- Updated dependencies [d48c68afc] +- Updated dependencies [d48c68afc] + - @celo/contractkit@5.1.0-beta.0 + - @celo/connect@5.1.0-beta.0 + - @celo/cryptographic-utils@5.0.5-beta.0 + - @celo/governance@5.0.5-beta.0 + - @celo/explorer@5.0.5-beta.0 + - @celo/utils@5.0.5-beta.0 + - @celo/base@5.0.5-beta.0 + - @celo/network-utils@5.0.5-beta.0 + - @celo/env-tests@1.0.1-beta.0 diff --git a/packages/celotool/ci_test_cip35.sh b/packages/celotool/ci_test_cip35.sh index 7d2c8ff9d3a..5001c52c586 100755 --- a/packages/celotool/ci_test_cip35.sh +++ b/packages/celotool/ci_test_cip35.sh @@ -17,5 +17,5 @@ if [ "${1}" == "checkout" ]; then elif [ "${1}" == "local" ]; then export GETH_DIR="${2}" echo "Testing using local geth dir ${GETH_DIR}..." - ./node_modules/.bin/mocha --node-option loader=ts-node/esm src/e2e-tests/cip35_tests.ts --localgeth ${GETH_DIR} + ./node_modules/.bin/mocha --node-option loader=ts-node/esm src/e2e-tests/cip35_tests.ts --localgeth ${GETH_DIR} fi diff --git a/packages/celotool/package.json b/packages/celotool/package.json index 716ff778ffc..0a66c063486 100644 --- a/packages/celotool/package.json +++ b/packages/celotool/package.json @@ -1,23 +1,22 @@ { "name": "@celo/celotool", - "version": "1.0.0", + "version": "1.0.1", "description": "Celotool is our hub for all scripts that people need to run within the monorepo", "main": "index.js", "author": "Celo", "license": "Apache-2.0", "dependencies": { - "@celo/base": "4.1.1-dev", - "@celo/connect": "4.1.1-dev", - "@celo/cryptographic-utils": "4.1.1-dev", - "@celo/contractkit": "4.1.1-dev", - "@celo/env-tests": "1.0.0", - "@celo/explorer": "4.1.1-dev", - "@celo/governance": "4.1.1-dev", - "@celo/identity": "4.1.1-dev", - "@celo/network-utils": "4.1.1-dev", - "@celo/utils": "4.1.1-dev", + "@celo/base": "^5.0.5", + "@celo/connect": "^5.1.0", + "@celo/cryptographic-utils": "^5.0.5", + "@celo/contractkit": "^5.1.0", + "@celo/env-tests": "1.0.1", + "@celo/explorer": "^5.0.5", + "@celo/governance": "^5.0.5", + "@celo/network-utils": "^5.0.5", + "@celo/utils": "^5.0.5", "@ethereumjs/util": "8.0.5", - "@ethereumjs/rlp":"4.0.1", + "@ethereumjs/rlp": "4.0.1", "@google-cloud/monitoring": "0.7.1", "@google-cloud/pubsub": "^0.28.1", "@google-cloud/secret-manager": "3.0.0", @@ -43,12 +42,11 @@ "tiny-secp256k1": "2.2.1", "web3": "1.10.0", "web3-eth-admin": "1.0.0-beta.55", - "yargs": "14.0.0" + "yargs": "17.7.2" }, "devDependencies": { - "@celo/dev-utils": "0.0.1-dev", - "@celo/flake-tracker": "0.0.1-dev", - "@celo/protocol": "*", + "@celo/dev-utils": "0.0.1", + "@celo/protocol": "1.0.1", "@types/bunyan": "1.8.8", "@types/chai": "^4.1.3", "@types/dotenv": "^8.2.0", diff --git a/packages/celotool/src/cmds/deploy/destroy/cluster.ts b/packages/celotool/src/cmds/deploy/destroy/cluster.ts index b0c76a7dcee..b4cb62058d4 100644 --- a/packages/celotool/src/cmds/deploy/destroy/cluster.ts +++ b/packages/celotool/src/cmds/deploy/destroy/cluster.ts @@ -1,6 +1,6 @@ import { printReleases } from 'src/cmds/deploy/list' import { deleteCluster, getNonSystemHelmReleases, switchToClusterFromEnv } from 'src/lib/cluster' -import { EnvTypes, envVar, fetchEnv } from 'src/lib/env-utils' +import { envTypes, envVar, fetchEnv } from 'src/lib/env-utils' import { exitIfCelotoolHelmDryRun } from 'src/lib/helm_deploy' import { DestroyArgv } from '../../deploy/destroy' @@ -13,7 +13,7 @@ export const builder = {} export const handler = async (argv: DestroyArgv) => { exitIfCelotoolHelmDryRun() const envType = fetchEnv(envVar.ENV_TYPE) - if (envType !== EnvTypes.DEVELOPMENT) { + if (envType !== envTypes.DEVELOPMENT) { console.error('You can only delete dev clusters') process.exit(1) } diff --git a/packages/celotool/src/cmds/deploy/destroy/eksportisto.ts b/packages/celotool/src/cmds/deploy/destroy/eksportisto.ts deleted file mode 100644 index 5ab1f7ce709..00000000000 --- a/packages/celotool/src/cmds/deploy/destroy/eksportisto.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { switchToClusterFromEnv } from 'src/lib/cluster' -import { removeHelmRelease } from 'src/lib/eksportisto' -import { exitIfCelotoolHelmDryRun } from 'src/lib/helm_deploy' -import { DestroyArgv } from '../../deploy/destroy' - -export const command = 'eksportisto' - -export const describe = 'destroy the eksportisto deploy' - -export const builder = {} - -export const handler = async (argv: DestroyArgv) => { - exitIfCelotoolHelmDryRun() - await switchToClusterFromEnv(argv.celoEnv) - await removeHelmRelease(argv.celoEnv) -} diff --git a/packages/celotool/src/cmds/deploy/destroy/forno.ts b/packages/celotool/src/cmds/deploy/destroy/forno.ts deleted file mode 100644 index 69ceda4e343..00000000000 --- a/packages/celotool/src/cmds/deploy/destroy/forno.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { DestroyArgv } from 'src/cmds/deploy/destroy' -import { destroyForno } from 'src/lib/forno' -import { exitIfCelotoolHelmDryRun } from 'src/lib/helm_deploy' - -export const command = 'forno' - -export const describe = 'Destroy forno for an environment' - -type FullNodeInitialArgv = DestroyArgv - -export const handler = async (argv: FullNodeInitialArgv) => { - exitIfCelotoolHelmDryRun() - await destroyForno(argv.celoEnv) -} diff --git a/packages/celotool/src/cmds/deploy/destroy/komenci.ts b/packages/celotool/src/cmds/deploy/destroy/komenci.ts deleted file mode 100644 index 85cc44189b9..00000000000 --- a/packages/celotool/src/cmds/deploy/destroy/komenci.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { addContextMiddleware, ContextArgv, switchToContextCluster } from 'src/lib/context-utils' -import { exitIfCelotoolHelmDryRun } from 'src/lib/helm_deploy' -import { removeHelmRelease } from 'src/lib/komenci' -import { DestroyArgv } from '../destroy' - -export const command = 'komenci' - -export const describe = 'destroy the komenci package' - -type KomenciDestroyArgv = DestroyArgv & ContextArgv - -export const builder = addContextMiddleware - -export const handler = async (argv: KomenciDestroyArgv) => { - exitIfCelotoolHelmDryRun() - await switchToContextCluster(argv.celoEnv, argv.context) - await removeHelmRelease(argv.celoEnv, argv.context) -} diff --git a/packages/celotool/src/cmds/deploy/destroy/testnet.ts b/packages/celotool/src/cmds/deploy/destroy/testnet.ts index 1970025c181..7d531742a53 100644 --- a/packages/celotool/src/cmds/deploy/destroy/testnet.ts +++ b/packages/celotool/src/cmds/deploy/destroy/testnet.ts @@ -1,5 +1,4 @@ import { switchToClusterFromEnv } from 'src/lib/cluster' -import { failIfVmBased } from 'src/lib/env-utils' import { deleteFromCluster, deleteStaticIPs, exitIfCelotoolHelmDryRun } from 'src/lib/helm_deploy' import { DestroyArgv } from '../../deploy/destroy' @@ -10,7 +9,6 @@ export const builder = {} export const handler = async (argv: DestroyArgv) => { exitIfCelotoolHelmDryRun() - failIfVmBased() await switchToClusterFromEnv(argv.celoEnv) diff --git a/packages/celotool/src/cmds/deploy/destroy/vm-testnet.ts b/packages/celotool/src/cmds/deploy/destroy/vm-testnet.ts deleted file mode 100644 index 0c6b1d5e9a0..00000000000 --- a/packages/celotool/src/cmds/deploy/destroy/vm-testnet.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { switchToClusterFromEnv } from 'src/lib/cluster' -import { exitIfCelotoolHelmDryRun } from 'src/lib/helm_deploy' -import { removePrometheus } from 'src/lib/prometheus' -import { destroy } from 'src/lib/vm-testnet-utils' -import { DestroyArgv } from '../../deploy/destroy' - -export const command = 'vm-testnet' -export const describe = 'destroy an existing VM-based testnet' -export const builder = {} - -export const handler = async (argv: DestroyArgv) => { - exitIfCelotoolHelmDryRun() - await switchToClusterFromEnv(argv.celoEnv) - await destroy(argv.celoEnv) - await removePrometheus() -} diff --git a/packages/celotool/src/cmds/deploy/initial/eksportisto.ts b/packages/celotool/src/cmds/deploy/initial/eksportisto.ts deleted file mode 100644 index bd2115459d9..00000000000 --- a/packages/celotool/src/cmds/deploy/initial/eksportisto.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { switchToClusterFromEnv } from 'src/lib/cluster' -import { installHelmChart } from 'src/lib/eksportisto' -import { InitialArgv } from '../../deploy/initial' - -export const command = 'eksportisto' - -export const describe = 'deploy eksportisto' - -export const builder = {} - -export const handler = async (argv: InitialArgv) => { - await switchToClusterFromEnv(argv.celoEnv) - await installHelmChart(argv.celoEnv) -} diff --git a/packages/celotool/src/cmds/deploy/initial/forno.ts b/packages/celotool/src/cmds/deploy/initial/forno.ts deleted file mode 100644 index a3109b128d8..00000000000 --- a/packages/celotool/src/cmds/deploy/initial/forno.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { InitialArgv } from 'src/cmds/deploy/initial' -import { deployForno } from 'src/lib/forno' -import { exitIfCelotoolHelmDryRun } from 'src/lib/helm_deploy' - -export const command = 'forno' - -export const describe = 'Deploy forno for an environment' - -type FullNodeInitialArgv = InitialArgv - -export const handler = async (argv: FullNodeInitialArgv) => { - exitIfCelotoolHelmDryRun() - await deployForno(argv.celoEnv) -} diff --git a/packages/celotool/src/cmds/deploy/initial/komenci.ts b/packages/celotool/src/cmds/deploy/initial/komenci.ts deleted file mode 100644 index a008b1fb55d..00000000000 --- a/packages/celotool/src/cmds/deploy/initial/komenci.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { InitialArgv } from 'src/cmds/deploy/initial' -import { addContextMiddleware, ContextArgv, switchToContextCluster } from 'src/lib/context-utils' -import { exitIfCelotoolHelmDryRun } from 'src/lib/helm_deploy' -import { installHelmChart } from 'src/lib/komenci' -import yargs from 'yargs' - -export const command = 'komenci' - -export const describe = 'deploy the komenci for the specified network' - -type KomenciInitialArgv = InitialArgv & - ContextArgv & { - useForno: boolean - } - -export const builder = (argv: yargs.Argv) => { - return addContextMiddleware(argv).option('useForno', { - description: 'Uses forno for RPCs from the komenci clients', - default: false, - type: 'boolean', - }) -} - -export const handler = async (argv: KomenciInitialArgv) => { - // Do not allow --helmdryrun because komenciIdentityHelmParameters function. It could be refactored to allow - exitIfCelotoolHelmDryRun() - await switchToContextCluster(argv.celoEnv, argv.context) - await installHelmChart(argv.celoEnv, argv.context, argv.useForno) -} diff --git a/packages/celotool/src/cmds/deploy/initial/kong.ts b/packages/celotool/src/cmds/deploy/initial/kong.ts deleted file mode 100644 index daac49b877f..00000000000 --- a/packages/celotool/src/cmds/deploy/initial/kong.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { InitialArgv } from 'src/cmds/deploy/initial' -import { switchToClusterFromEnvOrContext } from 'src/lib/cluster' -import { addContextMiddleware, ContextArgv } from 'src/lib/context-utils' -import { installKong, installKonga } from 'src/lib/kong' - -export const command = 'kong' - -export const describe = 'deploy Kong and Konga packages' - -export type KongInitialArgv = InitialArgv & ContextArgv - -export const builder = (argv: KongInitialArgv) => { - return addContextMiddleware(argv) -} - -export const handler = async (argv: KongInitialArgv) => { - await switchToClusterFromEnvOrContext(argv, true) - await installKong(argv.celoEnv) - await installKonga(argv.celoEnv) -} diff --git a/packages/celotool/src/cmds/deploy/initial/setup-cluster.ts b/packages/celotool/src/cmds/deploy/initial/setup-cluster.ts deleted file mode 100644 index 187dcc43f37..00000000000 --- a/packages/celotool/src/cmds/deploy/initial/setup-cluster.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { switchToClusterFromEnvOrContext } from 'src/lib/cluster' -import { addContextMiddleware, ContextArgv } from 'src/lib/context-utils' -import { exitIfCelotoolHelmDryRun } from 'src/lib/helm_deploy' -import { InitialArgv } from '../initial' - -export const command = 'setup-cluster' - -export const describe = 'Create K8s cluster and deploy common tools' - -export type SetupClusterInitialArgv = InitialArgv & ContextArgv - -export const builder = (argv: SetupClusterInitialArgv) => { - return addContextMiddleware(argv) -} - -export const handler = async (argv: SetupClusterInitialArgv) => { - exitIfCelotoolHelmDryRun() - await switchToClusterFromEnvOrContext(argv, false) -} diff --git a/packages/celotool/src/cmds/deploy/initial/testnet.ts b/packages/celotool/src/cmds/deploy/initial/testnet.ts index 0ae62d6285a..5374986ea93 100644 --- a/packages/celotool/src/cmds/deploy/initial/testnet.ts +++ b/packages/celotool/src/cmds/deploy/initial/testnet.ts @@ -1,5 +1,4 @@ import { createClusterIfNotExists, setupCluster, switchToClusterFromEnv } from 'src/lib/cluster' -import { failIfVmBased } from 'src/lib/env-utils' import { createStaticIPs, installHelmChart, @@ -34,8 +33,6 @@ export const builder = (argv: yargs.Argv) => { } export const handler = async (argv: TestnetInitialArgv) => { - failIfVmBased() - const createdCluster = await createClusterIfNotExists() await switchToClusterFromEnv(argv.celoEnv) diff --git a/packages/celotool/src/cmds/deploy/initial/vm-testnet.ts b/packages/celotool/src/cmds/deploy/initial/vm-testnet.ts deleted file mode 100644 index 8fc3159d6a8..00000000000 --- a/packages/celotool/src/cmds/deploy/initial/vm-testnet.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { createClusterIfNotExists, setupCluster, switchToClusterFromEnv } from 'src/lib/cluster' -import { exitIfCelotoolHelmDryRun } from 'src/lib/helm_deploy' -import { installPrometheusIfNotExists } from 'src/lib/prometheus' -import { deploy } from 'src/lib/vm-testnet-utils' -import yargs from 'yargs' -import { InitialArgv } from '../../deploy/initial' - -export const command = 'vm-testnet' -export const describe = 'upgrade a testnet on a VM' - -type VmTestnetArgv = InitialArgv & { - skipSecretGeneration: boolean - useExistingGenesis: boolean -} - -export const builder = (argv: yargs.Argv) => { - return argv - .option('skipSecretGeneration', { - describe: - 'Skips the generation of secrets. Use sparingly, this is intended to save deploy time if you are certain no secrets will have changed.', - default: false, - type: 'boolean', - }) - .option('useExistingGenesis', { - type: 'boolean', - description: 'Instead of generating a new genesis, use an existing genesis in GCS', - default: false, - }) -} - -export const handler = async (argv: VmTestnetArgv) => { - exitIfCelotoolHelmDryRun() - // deploy VM testnet with Terraform - await deploy(argv.celoEnv, !argv.skipSecretGeneration, argv.useExistingGenesis) - - // set up Kubernetes cluster that will have prometheus to stackdriver statefulset - const createdCluster = await createClusterIfNotExists() - await switchToClusterFromEnv(argv.celoEnv) - await setupCluster(argv.celoEnv, createdCluster) - await installPrometheusIfNotExists() -} diff --git a/packages/celotool/src/cmds/deploy/upgrade/eksportisto.ts b/packages/celotool/src/cmds/deploy/upgrade/eksportisto.ts deleted file mode 100644 index 3c757935d31..00000000000 --- a/packages/celotool/src/cmds/deploy/upgrade/eksportisto.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { switchToClusterFromEnv } from 'src/lib/cluster' -import { upgradeHelmChart } from 'src/lib/eksportisto' -import { UpgradeArgv } from '../../deploy/upgrade' - -export const command = 'eksportisto' - -export const describe = 'upgrade the eksportisto deploy' - -export const builder = {} - -export const handler = async (argv: UpgradeArgv) => { - await switchToClusterFromEnv(argv.celoEnv) - await upgradeHelmChart(argv.celoEnv) -} diff --git a/packages/celotool/src/cmds/deploy/upgrade/forno.ts b/packages/celotool/src/cmds/deploy/upgrade/forno.ts deleted file mode 100644 index 57d3a3a6557..00000000000 --- a/packages/celotool/src/cmds/deploy/upgrade/forno.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { UpgradeArgv } from 'src/cmds/deploy/upgrade' -import { deployForno } from 'src/lib/forno' -import { exitIfCelotoolHelmDryRun } from 'src/lib/helm_deploy' - -export const command = 'forno' - -export const describe = 'Upgrade forno for an environment' - -type FullNodeInitialArgv = UpgradeArgv - -export const handler = async (argv: FullNodeInitialArgv) => { - exitIfCelotoolHelmDryRun() - await deployForno(argv.celoEnv) -} diff --git a/packages/celotool/src/cmds/deploy/upgrade/komenci.ts b/packages/celotool/src/cmds/deploy/upgrade/komenci.ts deleted file mode 100644 index 153505c414d..00000000000 --- a/packages/celotool/src/cmds/deploy/upgrade/komenci.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { UpgradeArgv } from 'src/cmds/deploy/upgrade' -import { addContextMiddleware, ContextArgv, switchToContextCluster } from 'src/lib/context-utils' -import { exitIfCelotoolHelmDryRun } from 'src/lib/helm_deploy' -import { upgradeKomenciChart } from 'src/lib/komenci' -import yargs from 'yargs' - -export const command = 'komenci' - -export const describe = 'upgrade komenci on an AKS cluster' - -type OracleUpgradeArgv = UpgradeArgv & - ContextArgv & { - useForno: boolean - } - -export const builder = (argv: yargs.Argv) => { - return addContextMiddleware(argv).option('useForno', { - description: 'Uses forno for RPCs from the komenci clients', - default: false, - type: 'boolean', - }) -} - -export const handler = async (argv: OracleUpgradeArgv) => { - // Do not allow --helmdryrun because komenciIdentityHelmParameters function. It could be refactored to allow - exitIfCelotoolHelmDryRun() - await switchToContextCluster(argv.celoEnv, argv.context) - await upgradeKomenciChart(argv.celoEnv, argv.context, argv.useForno) -} diff --git a/packages/celotool/src/cmds/deploy/upgrade/kong.ts b/packages/celotool/src/cmds/deploy/upgrade/kong.ts deleted file mode 100644 index 497324c4cd6..00000000000 --- a/packages/celotool/src/cmds/deploy/upgrade/kong.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { UpgradeArgv } from 'src/cmds/deploy/upgrade' -import { switchToClusterFromEnvOrContext } from 'src/lib/cluster' -import { addContextMiddleware, ContextArgv } from 'src/lib/context-utils' -import { upgradeKong, upgradeKonga } from 'src/lib/kong' - -export const command = 'kong' - -export const describe = 'upgrade Kong and Konga packages' - -export type KongUpgradeArgv = UpgradeArgv & ContextArgv - -export const builder = (argv: KongUpgradeArgv) => { - return addContextMiddleware(argv) -} - -export const handler = async (argv: KongUpgradeArgv) => { - await switchToClusterFromEnvOrContext(argv, true) - await upgradeKong(argv.celoEnv) - await upgradeKonga(argv.celoEnv) -} diff --git a/packages/celotool/src/cmds/deploy/upgrade/testnet.ts b/packages/celotool/src/cmds/deploy/upgrade/testnet.ts index 60f041d3044..213d22aeaeb 100644 --- a/packages/celotool/src/cmds/deploy/upgrade/testnet.ts +++ b/packages/celotool/src/cmds/deploy/upgrade/testnet.ts @@ -1,5 +1,4 @@ import { switchToClusterFromEnv } from 'src/lib/cluster' -import { failIfVmBased } from 'src/lib/env-utils' import { isCelotoolHelmDryRun, resetAndUpgradeHelmChart, @@ -36,8 +35,6 @@ export const builder = (argv: yargs.Argv) => { } export const handler = async (argv: TestnetArgv) => { - failIfVmBased() - await switchToClusterFromEnv(argv.celoEnv) await upgradeStaticIPs(argv.celoEnv) diff --git a/packages/celotool/src/cmds/deploy/upgrade/vm-testnet.ts b/packages/celotool/src/cmds/deploy/upgrade/vm-testnet.ts deleted file mode 100644 index 79ea6309a0e..00000000000 --- a/packages/celotool/src/cmds/deploy/upgrade/vm-testnet.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { switchToClusterFromEnv } from 'src/lib/cluster' -import { exitIfCelotoolHelmDryRun } from 'src/lib/helm_deploy' -import { upgradePrometheus } from 'src/lib/prometheus' -import { deploy, taintTestnet, untaintTestnet } from 'src/lib/vm-testnet-utils' -import yargs from 'yargs' -import { UpgradeArgv } from '../../deploy/upgrade' - -export const command = 'vm-testnet' -export const describe = 'upgrade a testnet on a VM' - -type VmTestnetArgv = UpgradeArgv & { - reset: boolean - skipSecretGeneration: boolean - useExistingGenesis: boolean -} - -export const builder = (argv: yargs.Argv) => { - return argv - .option('reset', { - describe: 'recreates all nodes and deletes any chain data in persistent disks', - default: false, - type: 'boolean', - }) - .option('skipSecretGeneration', { - describe: - 'Skips the generation of secrets. Use sparingly, this is intended to save deploy time if you are certain no secrets will have changed.', - default: false, - type: 'boolean', - }) - .option('useExistingGenesis', { - type: 'boolean', - description: 'Instead of generating a new genesis, use an existing genesis in GCS', - default: false, - }) -} - -export const handler = async (argv: VmTestnetArgv) => { - exitIfCelotoolHelmDryRun() - await switchToClusterFromEnv(argv.celoEnv) - - let onDeployFailed = () => Promise.resolve() - if (argv.reset === true) { - onDeployFailed = () => untaintTestnet(argv.celoEnv) - await taintTestnet(argv.celoEnv) - } - await deploy(argv.celoEnv, !argv.skipSecretGeneration, argv.useExistingGenesis, onDeployFailed) - await upgradePrometheus() -} diff --git a/packages/celotool/src/cmds/gcp.ts b/packages/celotool/src/cmds/gcp.ts deleted file mode 100644 index 438093b2fa9..00000000000 --- a/packages/celotool/src/cmds/gcp.ts +++ /dev/null @@ -1,7 +0,0 @@ -import yargs from 'yargs' - -export const command = 'gcp ' - -export const describe = 'commands for interacting with GCP' - -export const builder = (argv: yargs.Argv) => argv.commandDir('gcp', { extensions: ['ts'] }) diff --git a/packages/celotool/src/cmds/ssh-vm-node.ts b/packages/celotool/src/cmds/ssh-vm-node.ts deleted file mode 100644 index 9592584ce74..00000000000 --- a/packages/celotool/src/cmds/ssh-vm-node.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { addCeloEnvMiddleware, CeloEnvArgv } from 'src/lib/env-utils' -import { getNodeVmName, getVmSshCommand, indexCoercer } from 'src/lib/vm-testnet-utils' -import yargs from 'yargs' - -export const command = 'ssh-vm-node [nodeIndex]' - -export const describe = - 'Generates a command to ssh into a vm-testnet node. To execute the ssh command, run `eval $()`' - -interface SshVmNodeArgv extends CeloEnvArgv { - nodeType: 'validator' | 'tx-node' | 'tx-node-private' | 'bootnode' | 'proxy' - nodeIndex?: number -} - -interface CheckArgs { - 'celo-env': unknown - nodeType: string | undefined - nodeIndex?: string -} - -export const builder = (argv: yargs.Argv) => { - const choices: readonly string[] = [ - 'validator', - 'tx-node', - 'tx-node-private', - 'bootnode', - 'proxy', - ] - return addCeloEnvMiddleware(argv) - .positional('nodeType', { - describe: 'Type of node', - choices, - }) - .positional('nodeIndex', { - describe: 'Index of the node. Only needed for validator or tx-node', - type: 'string', - coerce: indexCoercer, - }) - .check((checkArgv: CheckArgs) => { - const requiresIndex = checkArgv.nodeType !== 'bootnode' - if (requiresIndex && checkArgv.nodeIndex === undefined) { - return new Error(`nodeIndex is required for nodeType ${checkArgv.nodeType}`) - } - return true - }) -} - -export const handler = async (argv: SshVmNodeArgv) => { - const instanceName = await getNodeVmName(argv.celoEnv, argv.nodeType, argv.nodeIndex) - console.info(getVmSshCommand(instanceName)) -} diff --git a/packages/celotool/src/cmds/vm-exec.ts b/packages/celotool/src/cmds/vm-exec.ts deleted file mode 100644 index 160f1f75b68..00000000000 --- a/packages/celotool/src/cmds/vm-exec.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { execCmd } from 'src/lib/cmd-utils' -import { - addCeloEnvMiddleware, - CeloEnvArgv, - envVar, - failIfNotVmBased, - fetchEnv, -} from 'src/lib/env-utils' -import { getProxiesPerValidator } from 'src/lib/testnet-utils' -import { getNodeVmName, getVmSshCommand, indexCoercer, ProxyIndex } from 'src/lib/vm-testnet-utils' -import yargs from 'yargs' - -export const command = 'vm-exec' - -export const describe = 'SSH and exec commands on all or individual nodes in a VM-based env' - -interface ValidatorsExecArgv extends CeloEnvArgv { - nodeType: string - docker: string - cmd: string - only: number | ProxyIndex - from: number | ProxyIndex - to: number | ProxyIndex -} - -export const builder = (argv: yargs.Argv) => { - return addCeloEnvMiddleware(argv) - .option('nodeType', { - describe: 'Type of node', - choices: ['validator', 'tx-node', 'tx-node-private', 'bootnode', 'proxy'], - type: 'string', - }) - .option('docker', { - type: 'string', - description: 'Operation to run on the docker container {start|stop|restart}', - default: 'restart', - }) - .option('cmd', { - type: 'string', - description: 'Arbitrary command to exec', - default: null, - }) - .option('only', { - type: 'string', - description: - 'Index of the only node to exec on. If the node is a proxy, the validator and proxy indices must both be specified as `:`', - default: null, - coerce: indexCoercer, - }) - .option('from', { - type: 'string', - description: - 'Index of the node to start on when exec-ing over a range. If the node is a proxy, the validator and proxy indices must both be specified as `:`', - default: '0', - coerce: indexCoercer, - }) - .option('to', { - type: 'string', - description: - 'Index of the node to end on when exec-ing over a range (not inclusive). If the node is a proxy, the validator and proxy indices must both be specified as `:`. Defaults to the max index for the nodeType.', - default: '-1', - coerce: indexCoercer, - }) -} - -export const handler = async (argv: ValidatorsExecArgv) => { - failIfNotVmBased() - - const project = fetchEnv(envVar.TESTNET_PROJECT_NAME) - const zone = fetchEnv(envVar.KUBERNETES_CLUSTER_ZONE) - - const cmd = argv.cmd === null ? `sudo docker ${argv.docker} geth` : argv.cmd - - console.info( - `Running on validators.\n` + - `Cmd: ${cmd}\n` + - `Env: ${argv.celoEnv}\n` + - `Project: ${project}\n` + - `Zone: ${zone}\n` + - `Node Type: ${argv.nodeType}` - ) - - // For proxy / tx-nodes that have random suffixes, we are forced to run a - // gcloud command and await it in order to get the full instance name. - // Because of this, we end up calling the SSH command, and then moving on to get the - // next instance name, which takes time, so the previous SSH command is nearly finished. - // By doing this in two steps, we more closely make the exec across all instances - // happen in parallel - const instanceNames: string[] = [] - if (argv.only === null) { - let to: number | ProxyIndex = argv.to - - if (typeof to === 'number' && to < 0) { - to = getMaxNodeIndex(argv.nodeType) - } - - console.info('Max Node Index:', getMaxNodeIndex(argv.nodeType)) - console.info('From Index:', argv.from) - console.info('To Index:', to) - - const indexIterator = createIndexIterator(argv.from, to) - let index = indexIterator.next() - while (!index.done) { - const instanceName = await getNodeVmName(argv.celoEnv, argv.nodeType, index.value) - instanceNames.push(instanceName) - index = indexIterator.next() - } - } else { - console.info(`Only Index: ${argv.only}`) - const instanceName = await getNodeVmName(argv.celoEnv, argv.nodeType, argv.only) - instanceNames.push(instanceName) - } - - const runCmds = [] - for (const instanceName of instanceNames) { - runCmds.push(runSshCommand(instanceName, cmd)) - } - - await Promise.all(runCmds) - - console.info('Done.') -} - -async function runSshCommand(instanceName: string, cmd: string) { - const bareSshCmd = getVmSshCommand(instanceName) - const fullCmd = `${bareSshCmd} --command "${cmd}"` - console.info(`Running ${fullCmd}`) - return execCmd(fullCmd, {}, false, true) -} - -function getMaxNodeIndex(nodeType: string): number | ProxyIndex { - switch (nodeType) { - case 'validator': - return parseInt(fetchEnv(envVar.VALIDATORS), 10) - case 'tx-node': - return parseInt(fetchEnv(envVar.TX_NODES), 10) - case 'tx-node-private': - return parseInt(fetchEnv(envVar.PRIVATE_TX_NODES), 10) - case 'bootnode': - return 1 - case 'proxy': - const proxiesPerValidator = getProxiesPerValidator() - if (!proxiesPerValidator.length) { - return { - validatorIndex: 0, - proxyIndex: 0, - } - } - return { - validatorIndex: proxiesPerValidator.length - 1, - proxyIndex: proxiesPerValidator[proxiesPerValidator.length - 1], - } - default: - throw new Error('Invalid node type') - } -} - -function* createIndexIterator(from: number | ProxyIndex, to: number | ProxyIndex) { - if (typeof from !== typeof to) { - throw Error('From and to indices should be of the same type') - } - if (typeof from === 'number') { - // iterate through numeric indices - for (let i = from; i < to; i++) { - yield i - } - } else { - const proxyFrom = from as ProxyIndex - const proxyTo = to as ProxyIndex - // iterate through proxy indices - const proxiesPerValidator = getProxiesPerValidator() - const minValidatorIndex = Math.min(proxiesPerValidator.length, proxyTo.validatorIndex) - for (let valIndex = proxyFrom.validatorIndex; valIndex <= minValidatorIndex; valIndex++) { - const maxProxyIndex = - valIndex === proxyTo.validatorIndex ? proxyTo.proxyIndex : proxiesPerValidator[valIndex] - for (let proxyIndex = from.proxyIndex; proxyIndex < maxProxyIndex; proxyIndex++) { - const index: ProxyIndex = { - validatorIndex: valIndex, - proxyIndex, - } - yield index - } - } - } -} diff --git a/packages/celotool/src/e2e-tests/cip35_tests.ts b/packages/celotool/src/e2e-tests/cip35_tests.ts index 25b3830db04..a93fb2bec39 100644 --- a/packages/celotool/src/e2e-tests/cip35_tests.ts +++ b/packages/celotool/src/e2e-tests/cip35_tests.ts @@ -141,6 +141,7 @@ function getGethRunConfig(withDonut: boolean, withEspresso: boolean): GethRunCon churritoBlock: 0, donutBlock: withDonut ? 0 : null, espressoBlock: withEspresso ? 0 : null, + gingerbreadBlock: null, }, instances: [ { diff --git a/packages/celotool/src/e2e-tests/slashing_tests.ts b/packages/celotool/src/e2e-tests/slashing_tests.ts index d6e3bcbd233..25303a7834e 100644 --- a/packages/celotool/src/e2e-tests/slashing_tests.ts +++ b/packages/celotool/src/e2e-tests/slashing_tests.ts @@ -18,17 +18,38 @@ const TMP_PATH = '/tmp/e2e' const safeMarginBlocks = 4 function headerArray(block: any) { + if (!block.nonce) { + // Before Gingerbread fork + return [ + block.parentHash, + block.miner, + block.stateRoot, + block.transactionsRoot, + block.receiptsRoot, + block.logsBloom, + block.number, + block.gasUsed, + block.timestamp, + block.extraData, + ] + } return [ block.parentHash, + block.sha3Uncles, block.miner, block.stateRoot, block.transactionsRoot, block.receiptsRoot, block.logsBloom, + new BigNumber(block.difficulty).toNumber(), block.number, + block.gasLimit, block.gasUsed, block.timestamp, block.extraData, + block.mixHash, + block.nonce, + block.baseFee, ] } diff --git a/packages/celotool/src/lib/aws.ts b/packages/celotool/src/lib/aws.ts deleted file mode 100644 index 02e611bdc44..00000000000 --- a/packages/celotool/src/lib/aws.ts +++ /dev/null @@ -1,172 +0,0 @@ -import { execCmd, execCmdAndParseJson } from './cmd-utils' -import { AwsClusterConfig } from './k8s-cluster/aws' - -export async function getKeyArnFromAlias(alias: string, region: string) { - const fullAliasName = `alias/${alias}` - /** - * Expected output example: - * [ - * { - * "AliasName": "alias/test-ecc-key", - * "AliasArn": "arn:aws:kms:eu-central-1:243983831780:alias/test-ecc-key", - * "TargetKeyId": "1d6db902-9a45-4dd5-bd1e-7250b2306f18" - * } - * ] - */ - const [parsed] = await execCmdAndParseJson( - `aws kms list-aliases --region ${region} --query 'Aliases[?AliasName == \`${fullAliasName}\`]' --output json` - ) - if (!parsed) { - throw Error(`Could not find key with alias ${alias} and region ${region}`) - } - return parsed.AliasArn.replace(fullAliasName, `key/${parsed.TargetKeyId}`) -} - -export function deleteRole(roleName: string) { - return execCmd(`aws iam delete-role --role-name ${roleName}`) -} - -export function detachPolicyIdempotent(roleName: string, policyArn: string) { - return execCmd(`aws iam detach-role-policy --role-name ${roleName} --policy-arn ${policyArn}`) -} - -/** - * Deletes all policy versions and the policy itself - */ -export async function deletePolicy(policyArn: string) { - // First, delete all non-default policy versions - const policyVersions = await getPolicyVersions(policyArn) - await Promise.all( - policyVersions - .filter((version: any) => !version.IsDefaultVersion) // cannot delete the default version - .map((version: any) => deletePolicyVersion(policyArn, version.VersionId)) - ) - return execCmd(`aws iam delete-policy --policy-arn ${policyArn}`) -} - -function deletePolicyVersion(policyArn: string, versionId: string) { - return execCmd( - `aws iam delete-policy-version --policy-arn ${policyArn} --version-id ${versionId}` - ) -} - -async function getPolicyVersions(policyArn: string) { - return execCmdAndParseJson( - `aws iam list-policy-versions --policy-arn ${policyArn} --query 'Versions' --output json` - ) -} - -export async function getPolicyArn(policyName: string) { - const [policy] = await execCmdAndParseJson( - `aws iam list-policies --query 'Policies[?PolicyName == \`${policyName}\`]' --output json` - ) - if (!policy) { - return undefined - } - return policy.Arn -} - -/** - * Given a cluster name, finds the NodeInstanceRole that's used by the nodes. - * There's no easy way to query this directly, so this command searches through - * roles and finds the correct one. - */ -export async function getEKSNodeInstanceGroupRoleArn(clusterName: string) { - const existingRoles = await execCmdAndParseJson( - `aws iam list-roles --query 'Roles' --output json` - ) - const potentialRoles = existingRoles.filter((role: any) => { - // The role name doesn't necessarily include the cluster name, but it will include - // 'NodeInstanceRole'. - const re = new RegExp(`.+-NodeInstanceRole-.+`) - return re.test(role.RoleName) - }) - let roleArn: string | undefined - for (const role of potentialRoles) { - const [clusterNameTag] = await execCmdAndParseJson( - `aws iam list-role-tags --role-name ${role.RoleName} --query 'Tags[?Key == \`alpha.eksctl.io/cluster-name\`]'` - ) - if (clusterNameTag && clusterNameTag.Value === clusterName) { - roleArn = role.Arn - break - } - } - if (!roleArn) { - throw Error(`Could not find NodeInstanceRole for cluster ${clusterName}`) - } - return roleArn -} - -export function attachPolicyIdempotent(roleName: string, policyArn: string) { - return execCmd(`aws iam attach-role-policy --role-name ${roleName} --policy-arn ${policyArn}`) -} - -export async function createRoleIdempotent(roleName: string, policyDocumentJson: string) { - const [existing] = await execCmdAndParseJson( - `aws iam list-roles --query 'Roles[?RoleName == \`${roleName}\`]' --output json` - ) - if (existing) { - console.info(`Role ${roleName} exists`) - return existing.Arn - } - console.info(`Creating role ${roleName}`) - const [outputRaw] = await execCmd( - `aws iam create-role --role-name ${roleName} --assume-role-policy-document '${policyDocumentJson}' --query 'Role.Arn' --output text` - ) - return outputRaw.trim() -} - -export async function createPolicyIdempotent(policyName: string, policyDocumentJson: string) { - const [existing] = await execCmdAndParseJson( - `aws iam list-policies --query 'Policies[?PolicyName == \`${policyName}\`]' --output json` - ) - if (existing) { - console.info(`Policy ${policyName} exists`) - return existing.Arn - } - console.info(`Creating policy ${policyName}`) - const [output] = await execCmd( - `aws iam create-policy --policy-name ${policyName} --policy-document '${policyDocumentJson}' --query 'Policy.Arn' --output text` - ) - return output.trim() -} - -/** - * A cluster will have a security group that applies to all nodes (ie VMs) in the cluster. - * This returns a description of that security group. - */ -export function getClusterSharedNodeSecurityGroup(clusterConfig: AwsClusterConfig) { - return execCmdAndParseJson( - `aws ec2 describe-security-groups --filters "Name=tag:aws:cloudformation:logical-id,Values=ClusterSharedNodeSecurityGroup" "Name=tag:eksctl.cluster.k8s.io/v1alpha1/cluster-name,Values=${clusterConfig.clusterName}" --query "SecurityGroups[0]" --output json` - ) -} - -/** - * For a given security group, authorizes ingress traffic on a provided port - * for a given protocol and CIDR range. - */ -export function authorizeSecurityGroupIngress( - groupID: string, - port: number, - protocol: string, - cidrRange: string -) { - return execCmd( - `aws ec2 authorize-security-group-ingress --group-id ${groupID} --ip-permissions IpProtocol=${protocol},FromPort=${port},ToPort=${port},IpRanges='[{CidrIp=${cidrRange}}]'` - ) -} - -/** - * For a given security group, revokes authorized ingress traffic on a provided port - * for a given protocol and CIDR range. - */ -export function revokeSecurityGroupIngress( - groupID: string, - port: number, - protocol: string, - cidrRange: string -) { - return execCmd( - `aws ec2 revoke-security-group-ingress --group-id ${groupID} --ip-permissions IpProtocol=${protocol},FromPort=${port},ToPort=${port},IpRanges='[{CidrIp=${cidrRange}}]'` - ) -} diff --git a/packages/celotool/src/lib/cluster.ts b/packages/celotool/src/lib/cluster.ts index 7baed1ad446..95f6661cb75 100644 --- a/packages/celotool/src/lib/cluster.ts +++ b/packages/celotool/src/lib/cluster.ts @@ -1,7 +1,7 @@ import sleep from 'sleep-promise' import { execCmd, execCmdWithExitOnFailure } from './cmd-utils' import { getClusterConfigForContext, switchToContextCluster } from './context-utils' -import { doCheckOrPromptIfStagingOrProduction, EnvTypes, envVar, fetchEnv } from './env-utils' +import { doCheckOrPromptIfStagingOrProduction, envTypes, envVar, fetchEnv } from './env-utils' import { checkHelmVersion, createAndUploadBackupSecretIfNotExists, @@ -11,10 +11,10 @@ import { installCertManagerAndNginx, installGCPSSDStorageClass, isCelotoolHelmDryRun, + networkName, } from './helm_deploy' import { createServiceAccountIfNotExists } from './service-account-utils' import { outputIncludes, switchToProjectFromEnv } from './utils' -import { networkName } from './vm-testnet-utils' const SYSTEM_HELM_RELEASES = [ 'nginx-ingress-release', @@ -137,7 +137,7 @@ export async function setupCluster(celoEnv: string, createdCluster: boolean) { await installCertManagerAndNginx(celoEnv) - if (envType !== EnvTypes.DEVELOPMENT) { + if (envType !== envTypes.DEVELOPMENT) { console.info('Installing metric tools installation') await installAndEnableMetricsDeps(true) } else { @@ -199,7 +199,7 @@ export async function setClusterLabels(celoEnv: string) { ) } await labelfn('environment', envType) - await labelfn('envtype', envType === EnvTypes.PRODUCTION ? 'production' : 'nonproduction') + await labelfn('envtype', envType === envTypes.PRODUCTION ? 'production' : 'nonproduction') await labelfn('envinstance', celoEnv) } diff --git a/packages/celotool/src/lib/context-utils.ts b/packages/celotool/src/lib/context-utils.ts index 2e0c143dedf..f14d9eb2c07 100644 --- a/packages/celotool/src/lib/context-utils.ts +++ b/packages/celotool/src/lib/context-utils.ts @@ -8,7 +8,6 @@ import { getDynamicEnvVarValue, } from './env-utils' import { AksClusterConfig } from './k8s-cluster/aks' -import { AwsClusterConfig } from './k8s-cluster/aws' import { BaseClusterConfig, BaseClusterManager, CloudProvider } from './k8s-cluster/base' import { GCPClusterConfig } from './k8s-cluster/gcp' import { getClusterManager } from './k8s-cluster/utils' @@ -26,17 +25,6 @@ const contextAksClusterConfigDynamicEnvVars: { regionName: DynamicEnvVar.AZURE_REGION_NAME, } -/** - * Env vars corresponding to each value for the AwsClusterConfig for a particular context - */ -const contextAwsClusterConfigDynamicEnvVars: { - [k in keyof Omit]: DynamicEnvVar -} = { - clusterName: DynamicEnvVar.KUBERNETES_CLUSTER_NAME, - clusterRegion: DynamicEnvVar.AWS_CLUSTER_REGION, - resourceGroupTag: DynamicEnvVar.AWS_RESOURCE_GROUP_TAG, -} - /** * Env vars corresponding to each value for the GCPClusterConfig for a particular context */ @@ -51,7 +39,6 @@ const contextGCPClusterConfigDynamicEnvVars: { const clusterConfigGetterByCloudProvider: { [key in CloudProvider]: (context: string) => BaseClusterConfig } = { - [CloudProvider.AWS]: getAwsClusterConfig, [CloudProvider.AZURE]: getAksClusterConfig, [CloudProvider.GCP]: getGCPClusterConfig, } @@ -85,24 +72,7 @@ export function getAksClusterConfig(context: string): AksClusterConfig { /** * Fetches the env vars for a particular context * @param context the context to use - * @return an AwsClusterConfig for the context - */ -export function getAwsClusterConfig(context: string): AwsClusterConfig { - const awsDynamicEnvVars = getContextDynamicEnvVarValues( - contextAwsClusterConfigDynamicEnvVars, - context - ) - const clusterConfig: AwsClusterConfig = { - cloudProvider: CloudProvider.AZURE, - ...awsDynamicEnvVars, - } - return clusterConfig -} - -/** - * Fetches the env vars for a particular context - * @param context the context to use - * @return an AwsClusterConfig for the context + * @return an GCPClusterConfig for the context */ export function getGCPClusterConfig(context: string): GCPClusterConfig { const gcpDynamicEnvVars = getContextDynamicEnvVarValues( @@ -166,7 +136,7 @@ export function getContextDynamicEnvVarValues( } /** - * Reads the context and switches to the appropriate Azure or AWS Cluster + * Reads the context and switches to the appropriate Azure Cluster */ export async function switchToContextCluster( celoEnv: string, diff --git a/packages/celotool/src/lib/eksportisto.ts b/packages/celotool/src/lib/eksportisto.ts deleted file mode 100644 index 48ad00dcd18..00000000000 --- a/packages/celotool/src/lib/eksportisto.ts +++ /dev/null @@ -1,233 +0,0 @@ -import fs from 'fs' -import { execCmd, execCmdAndParseJson } from 'src/lib/cmd-utils' -import { envVar, fetchEnv, fetchEnvOrFallback } from 'src/lib/env-utils' -import { - getConfigMapHashes, - HelmAction, - installGenericHelmChart, - removeGenericHelmChart, - upgradeGenericHelmChart, -} from 'src/lib/helm_deploy' -import { - createServiceAccountIfNotExists, - getServiceAccountEmail, - getServiceAccountKey, -} from 'src/lib/service-account-utils' -import { switchToProjectFromEnv } from 'src/lib/utils' - -const yaml = require('js-yaml') - -const chartDir = '../helm-charts/eksportisto' - -function baseName(suffix: string) { - if (suffix.length > 0) { - return `eksportisto-${suffix}` - } else { - return 'eksportisto' - } -} - -function releaseName(celoEnv: string, suffix: string) { - return `${celoEnv}-${baseName(suffix)}` -} - -interface Context { - releaseName: string - suffix: string - celoEnv: string -} - -function buildContext(celoEnv: string): Context { - const suffix = fetchEnvOrFallback(envVar.EKSPORTISTO_SUFFIX, '') - - return { - releaseName: releaseName(celoEnv, suffix), - celoEnv, - suffix, - } -} - -export async function installHelmChart(celoEnv: string) { - const context = buildContext(celoEnv) - const params = await helmParameters(context) - - await installGenericHelmChart({ - namespace: context.celoEnv, - releaseName: context.releaseName, - chartDir, - parameters: params.concat( - `--set configHash="${await getConfigMapHash(context, params, 'install')}"` - ), - }) -} - -export async function upgradeHelmChart(celoEnv: string) { - const context = buildContext(celoEnv) - const params = await helmParameters(context) - - await upgradeGenericHelmChart({ - namespace: context.celoEnv, - releaseName: context.releaseName, - chartDir, - parameters: params.concat( - `--set configHash="${await getConfigMapHash(context, params, 'upgrade')}"` - ), - }) -} - -export async function removeHelmRelease(celoEnv: string) { - const suffix = fetchEnvOrFallback(envVar.EKSPORTISTO_SUFFIX, '') - await removeGenericHelmChart(releaseName(celoEnv, suffix), celoEnv) -} - -async function getServiceAccountKeyBase64FromHelm(celoEnv: string) { - const suffix = fetchEnvOrFallback(envVar.EKSPORTISTO_SUFFIX, '') - const relName = releaseName(celoEnv, suffix) - const installedCharts = await execCmdAndParseJson(`helm list --short -o json -n ${celoEnv}`) - const chartInstalled = installedCharts.includes(relName) - if (chartInstalled) { - const [output] = await execCmd(`helm get values -n ${celoEnv} ${relName}`) - const values: any = yaml.safeLoad(output) - return values.serviceAccountBase64 - } -} - -function getServiceAccountName(celoEnv: string) { - const suffix = fetchEnvOrFallback(envVar.EKSPORTISTO_SUFFIX, '') - return releaseName(celoEnv, suffix) -} - -async function getServiceAccountKeyBase64(celoEnv: string) { - // First check if value already exist in helm release. If so we pass the same value - // and we avoid creating a new key for the service account - const serviceAccountKeyBase64 = await getServiceAccountKeyBase64FromHelm(celoEnv) - if (serviceAccountKeyBase64) { - return serviceAccountKeyBase64 - } else { - // We do not have the service account key in helm so we need to create the SA (if it does not exist) - // and create a new key for the service account in any case - await switchToProjectFromEnv() - const serviceAccountName = getServiceAccountName(celoEnv) - await createServiceAccountIfNotExists(serviceAccountName) - const serviceAccountEmail = await getServiceAccountEmail(serviceAccountName) - const serviceAccountKeyPath = `/tmp/gcloud-key-${serviceAccountName}.json` - await getServiceAccountKey(serviceAccountEmail, serviceAccountKeyPath) - return fs.readFileSync(serviceAccountKeyPath).toString('base64') - } -} - -async function allowServiceAccountToWriteToBigquery(serviceAccountEmail: string) { - // This should be less broad but I couldn't figure out how to do it - const project = fetchEnv(envVar.TESTNET_PROJECT_NAME) - const [output] = await execCmd( - `gcloud projects get-iam-policy ${project} --format json`, - {}, - false, - false - ) - const policy = JSON.parse(output) as { bindings: Array<{ role: string; members: string[] }> } - - for (const binding of policy.bindings) { - if (binding.role === 'roles/bigquery.dataOwner') { - if ( - binding.members.find((m) => m === `serviceAccount:${serviceAccountEmail}`) !== undefined - ) { - console.info('Service account already has permissions, skipping policy update') - return - } else { - binding.members = binding.members.concat(`serviceAccount:${serviceAccountEmail}`) - } - } - } - - const fn = `/tmp/updated-policy.json` - fs.writeFileSync(fn, JSON.stringify(policy)) - await execCmd(`gcloud projects set-iam-policy ${project} ${fn}`, {}, false, true) -} - -async function getConfigMapHash(context: Context, params: string[], action: HelmAction) { - const hashes = await getConfigMapHashes( - context.celoEnv, - context.releaseName, - chartDir, - params, - action - ) - - return hashes['eksportisto/templates/configmap.yaml'] -} - -interface NodeInfo { - ip: string - labels: Record - status: string -} - -interface CeloNodes { - tip: NodeInfo | undefined - backfill: NodeInfo[] -} - -export async function getInternalTxNodeIps(context: Context): Promise { - const project = fetchEnv(envVar.TESTNET_PROJECT_NAME) - const instanceGroupURIs: string[] = await execCmdAndParseJson( - `gcloud compute instance-groups list --project '${project}' --filter="name~'${context.celoEnv}-tx-node-lb-internal-group'" --format json --uri` - ) - - if (instanceGroupURIs.length !== 1) { - throw Error('Expecting one (and only one) instance group to match filter') - } - - const instanceGroupURI = instanceGroupURIs[0] - const instanceURIs: string[] = await execCmdAndParseJson( - `gcloud compute instance-groups list-instances ${instanceGroupURI} --format json --uri` - ) - - const runningNodes = ( - await Promise.all( - instanceURIs.map(async (instanceURI) => { - const details = await execCmdAndParseJson( - `gcloud compute instances describe ${instanceURI} --format json` - ) - return { - ip: details.networkInterfaces[0].networkIP, - status: details.status, - labels: details.labels ?? {}, - } - }) - ) - ).filter((node) => node.status === 'RUNNING') - - const tipNodeLabel = `${baseName(context.suffix)}-tip` - const backfillNodeLabel = `${baseName(context.suffix)}-backfill` - - return { - tip: runningNodes.find((node) => node.labels[tipNodeLabel] === 'true'), - backfill: runningNodes.filter((node) => node.labels[backfillNodeLabel] === 'true'), - } -} - -async function helmParameters(context: Context) { - const { celoEnv } = context - const suffix = fetchEnvOrFallback(envVar.EKSPORTISTO_SUFFIX, '') - const params = [ - `--namespace ${celoEnv}`, - `--set environment="${celoEnv}"`, - `--set imageRepository="${fetchEnv(envVar.EKSPORTISTO_DOCKER_IMAGE_REPOSITORY)}"`, - `--set imageTag="${fetchEnv(envVar.EKSPORTISTO_DOCKER_IMAGE_TAG)}"`, - `--set deploymentSuffix=${suffix}`, - ] - - const serviceAccountKeyBase64 = await getServiceAccountKeyBase64(celoEnv) - const serviceAccountEmail = await getServiceAccountEmail(getServiceAccountName(celoEnv)) - await allowServiceAccountToWriteToBigquery(serviceAccountEmail) - params.push( - `--set bigquery.dataset=${celoEnv}_eksportisto`, - `--set serviceAccountBase64="${serviceAccountKeyBase64}"` - ) - - params.push(`--set celoTipNodeIP="tx-nodes"`) - params.push(`--set celoBackfillNodeIPs[0]="tx-nodes"`) - - return params -} diff --git a/packages/celotool/src/lib/endpoints.ts b/packages/celotool/src/lib/endpoints.ts index a0d899c3685..6cf910396aa 100644 --- a/packages/celotool/src/lib/endpoints.ts +++ b/packages/celotool/src/lib/endpoints.ts @@ -39,3 +39,11 @@ export function getFullNodeHttpRpcInternalUrl(celoEnv: string) { export function getFullNodeWebSocketRpcInternalUrl(celoEnv: string) { return `ws://${celoEnv}-fullnodes-rpc.${celoEnv}.svc.cluster.local:8546` } + +export function getLightNodeHttpRpcInternalUrl(celoEnv: string) { + return `http://${celoEnv}-lightnodes-rpc.${celoEnv}.svc.cluster.local:8545` +} + +export function getLightNodeWebSocketRpcInternalUrl(celoEnv: string) { + return `ws://${celoEnv}-lightnodes-rpc.${celoEnv}.svc.cluster.local:8546` +} diff --git a/packages/celotool/src/lib/env-utils.ts b/packages/celotool/src/lib/env-utils.ts index d57f4c19f0a..7d6898a17eb 100644 --- a/packages/celotool/src/lib/env-utils.ts +++ b/packages/celotool/src/lib/env-utils.ts @@ -40,9 +40,6 @@ export enum envVar { CONTEXTS = 'CONTEXTS', DONUT_BLOCK = 'DONUT_BLOCK', E2E_TESTS_FORCE_USE_MYCELO = 'E2E_TESTS_FORCE_USE_MYCELO', - EKSPORTISTO_DOCKER_IMAGE_REPOSITORY = 'EKSPORTISTO_DOCKER_IMAGE_REPOSITORY', - EKSPORTISTO_DOCKER_IMAGE_TAG = 'EKSPORTISTO_DOCKER_IMAGE_TAG', - EKSPORTISTO_SUFFIX = 'EKSPORTISTO_SUFFIX', ENV_TYPE = 'ENV_TYPE', EPOCH = 'EPOCH', ESPRESSO_BLOCK = 'ESPRESSO_BLOCK', @@ -76,11 +73,6 @@ export enum envVar { GRAFANA_LOCAL_OAUTH2_CLIENT_SECRET = 'GRAFANA_LOCAL_OAUTH2_CLIENT_SECRET', IN_MEMORY_DISCOVERY_TABLE = 'IN_MEMORY_DISCOVERY_TABLE', ISTANBUL_REQUEST_TIMEOUT_MS = 'ISTANBUL_REQUEST_TIMEOUT_MS', - KOMENCI_DOCKER_IMAGE_REPOSITORY = 'KOMENCI_DOCKER_IMAGE_REPOSITORY', - KOMENCI_DOCKER_IMAGE_TAG = 'KOMENCI_DOCKER_IMAGE_TAG', - KOMENCI_RULE_CONFIG_CAPTCHA_BYPASS_TOKEN = 'KOMENCI_RULE_CONFIG_CAPTCHA_BYPASS_TOKEN', - KOMENCI_SHOULD_SEND_REWARDS = 'KOMENCI_SHOULD_SEND_REWARDS', - KOMENCI_UNUSED_KOMENCI_ADDRESSES = 'KOMENCI_UNUSED_KOMENCI_ADDRESSES', KUBECONFIG = 'KUBECONFIG', KUBERNETES_CLUSTER_NAME = 'KUBERNETES_CLUSTER_NAME', KUBERNETES_CLUSTER_ZONE = 'KUBERNETES_CLUSTER_ZONE', @@ -111,10 +103,10 @@ export enum envVar { ODIS_SIGNER_BLOCKCHAIN_PROVIDER = 'ODIS_SIGNER_BLOCKCHAIN_PROVIDER', ODIS_SIGNER_DOMAINS_API_ENABLED = 'ODIS_SIGNER_DOMAINS_API_ENABLED', ODIS_SIGNER_PNP_API_ENABLED = 'ODIS_SIGNER_PNP_API_ENABLED', - ODIS_SIGNER_LEGACY_PNP_API_ENABLED = 'ODIS_SIGNER_LEGACY_PNP_API_ENABLED', ORACLE_DOCKER_IMAGE_REPOSITORY = 'ORACLE_DOCKER_IMAGE_REPOSITORY', ORACLE_DOCKER_IMAGE_TAG = 'ORACLE_DOCKER_IMAGE_TAG', ORACLE_UNUSED_ORACLE_ADDRESSES = 'ORACLE_UNUSED_ORACLE_ADDRESSES', + ORACLE_FX_ADAPTERS_API_KEYS = 'ORACLE_FX_ADAPTERS_API_KEYS', PRIVATE_NODE_DISK_SIZE_GB = 'PRIVATE_NODE_DISK_SIZE_GB', PRIVATE_TX_NODES = 'PRIVATE_TX_NODES', PROMETHEUS_DISABLE_STACKDRIVER_SIDECAR = 'PROMETHEUS_DISABLE_STACKDRIVER_SIDECAR', @@ -143,7 +135,6 @@ export enum envVar { VALIDATOR_ZERO_GENESIS_BALANCE = 'VALIDATOR_ZERO_GENESIS_BALANCE', VALIDATORS = 'VALIDATORS', VALIDATORS_ROLLING_UPDATE_PARTITION = 'VALIDATORS_ROLLING_UPDATE_PARTITION', - VM_BASED = 'VM_BASED', VOTING_BOT_BALANCE = 'VOTING_BOT_BALANCE', VOTING_BOT_CHANGE_BASELINE = 'VOTING_BOT_CHANGE_BASELINE', VOTING_BOT_CRON_SCHEDULE = 'VOTING_BOT_CRON_SCHEDULE', @@ -163,8 +154,6 @@ export enum envVar { */ export enum DynamicEnvVar { - AWS_CLUSTER_REGION = '{{ context }}_AWS_KUBERNETES_CLUSTER_REGION', - AWS_RESOURCE_GROUP_TAG = '{{ context }}_AWS_KUBERNETES_RESOURCE_GROUP', AZURE_SUBSCRIPTION_ID = '{{ context }}_AZURE_SUBSCRIPTION_ID', AZURE_KUBERNETES_RESOURCE_GROUP = '{{ context }}_AZURE_KUBERNETES_RESOURCE_GROUP', AZURE_REGION_NAME = '{{ context }}_AZURE_REGION_NAME', @@ -181,23 +170,6 @@ export enum DynamicEnvVar { GCP_PROJECT_NAME = '{{ context }}_GCP_PROJECT_NAME', GCP_ZONE = '{{ context }}_GCP_ZONE', KUBERNETES_CLUSTER_NAME = '{{ context }}_KUBERNETES_CLUSTER_NAME', - KOMENCI_ADDRESS_AZURE_KEY_VAULTS = '{{ context }}_KOMENCI_ADDRESS_AZURE_KEY_VAULTS', - KOMENCI_ADDRESSES_FROM_MNEMONIC_COUNT = '{{ context }}_KOMENCI_ADDRESSES_FROM_MNEMONIC_COUNT', - KOMENCI_CELOLABS_REWARDS_ADDRESS_AZURE_KEY_VAULTS = '{{ context }}_KOMENCI_CELOLABS_REWARDS_ADDRESS_AZURE_KEY_VAULTS', - KOMENCI_FOUNDATION_REWARDS_ADDRESS_AZURE_KEY_VAULTS = '{{ context }}_KOMENCI_FOUNDATION_REWARDS_ADDRESS_AZURE_KEY_VAULTS', - KOMENCI_REWARD_SERVICE_INSTANCE_COUNT = '{{ context }}_KOMENCI_REWARD_SERVICE_INSTANCE_COUNT', - KOMENCI_DB_HOST = '{{ context }}_KOMENCI_DB_HOST', - KOMENCI_DB_PORT = '{{ context }}_KOMENCI_DB_PORT', - KOMENCI_DB_USERNAME = '{{ context }}_KOMENCI_DB_USERNAME', - KOMENCI_DB_PASSWORD_VAULT_NAME = '{{ context }}_KOMENCI_DB_PASSWORD_VAULT_NAME', - KOMENCI_REWARD_SERVICE_DB_HOST = '{{ context }}_KOMENCI_REWARD_SERVICE_DB_HOST', - KOMENCI_REWARD_SERVICE_DB_PORT = '{{ context }}_KOMENCI_REWARD_SERVICE_DB_PORT', - KOMENCI_REWARD_SERVICE_DB_USERNAME = '{{ context }}_KOMENCI_REWARD_SERVICE_DB_USERNAME', - KOMENCI_REWARD_SERVICE_DB_PASSWORD_VAULT_NAME = '{{ context }}_KOMENCI_REWARD_SERVICE_DB_PASSWORD_VAULT_NAME', - KOMENCI_NETWORK = '{{ context }}_KOMENCI_NETWORK', - KOMENCI_APP_SECRETS_VAULT_NAME = '{{ context }}_KOMENCI_APP_SECRETS_VAULT_NAME', - KOMENCI_RULE_CONFIG_CAPTCHA_BYPASS_ENABLED = '{{ context }}_KOMENCI_RULE_CONFIG_CAPTCHA_BYPASS_ENABLED', - ORACLE_ADDRESS_AWS_KEY_ALIASES = '{{ context }}_{{ currencyPair }}_ORACLE_ADDRESS_AWS_KEY_ALIASES', ORACLE_ADDRESS_AZURE_KEY_VAULTS = '{{ context }}_{{ currencyPair }}_ORACLE_ADDRESS_AZURE_KEY_VAULTS', ORACLE_ADDRESSES_FROM_MNEMONIC_COUNT = '{{ context }}_{{ currencyPair}}_ORACLE_ADDRESSES_FROM_MNEMONIC_COUNT', ODIS_SIGNER_BLOCKCHAIN_API_KEY = '{{ context }}_ODIS_SIGNER_BLOCKCHAIN_API_KEY', @@ -208,7 +180,6 @@ export enum DynamicEnvVar { ODIS_SIGNER_AZURE_KEYVAULT_DOMAINS_KEY_LATEST_VERSION = '{{ context }}_ODIS_SIGNER_AZURE_KEYVAULT_DOMAINS_KEY_LATEST_VERSION', ODIS_SIGNER_DOMAINS_API_ENABLED = '{{ context }}_ODIS_SIGNER_DOMAINS_API_ENABLED', ODIS_SIGNER_PHONE_NUMBER_PRIVACY_API_ENABLED = '{{ context }}_ODIS_SIGNER_PNP_API_ENABLED', - ODIS_SIGNER_LEGACY_PHONE_NUMBER_PRIVACY_API_ENABLED = '{{ context }}_ODIS_SIGNER_LEGACY_PNP_API_ENABLED', ODIS_SIGNER_DB_HOST = '{{ context }}_ODIS_SIGNER_DB_HOST', ODIS_SIGNER_DB_PORT = '{{ context }}_ODIS_SIGNER_DB_PORT', ODIS_SIGNER_DB_USERNAME = '{{ context }}_ODIS_SIGNER_DB_USERNAME', @@ -227,7 +198,7 @@ export enum DynamicEnvVar { PROM_REMOTE_WRITE_URL = '{{ context }}_PROM_REMOTE_WRITE_URL', } -export enum EnvTypes { +export enum envTypes { DEVELOPMENT = 'development', INTEGRATION = 'integration', STAGING = 'staging', @@ -291,7 +262,7 @@ export function validateAndSwitchToEnv(celoEnv: string) { } export function isProduction() { - return fetchEnv(envVar.ENV_TYPE).toLowerCase() === EnvTypes.PRODUCTION + return fetchEnv(envVar.ENV_TYPE).toLowerCase() === envTypes.PRODUCTION } export function isValidCeloEnv(celoEnv: string) { @@ -375,21 +346,3 @@ export function addCeloEnvMiddleware(argv: yargs.Argv) { .middleware([celoEnvMiddleware]) ) } - -export function isVmBased() { - return fetchEnv(envVar.VM_BASED) === 'true' -} - -export function failIfNotVmBased() { - if (!isVmBased()) { - console.error('The celo env is not intended for a VM-based testnet, aborting') - process.exit(1) - } -} - -export function failIfVmBased() { - if (isVmBased()) { - console.error('The celo env is intended for a VM-based testnet, aborting') - process.exit(1) - } -} diff --git a/packages/celotool/src/lib/forno.ts b/packages/celotool/src/lib/forno.ts deleted file mode 100644 index ef97fc7fc8e..00000000000 --- a/packages/celotool/src/lib/forno.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { execCmd } from './cmd-utils' -import { coerceContext, getClusterManagerForContext, readableContext } from './context-utils' -import { envVar, fetchEnv } from './env-utils' -import { CloudProvider } from './k8s-cluster/base' -import { GCPClusterConfig } from './k8s-cluster/gcp' -import { TerraformVars } from './terraform' -import { deployModule, destroyModule } from './vm-testnet-utils' - -const FORNO_TERRAFORM_MODULE_NAME = 'forno' - -export async function deployForno(celoEnv: string) { - const contexts: string[] = fetchEnv(envVar.FORNO_FULL_NODE_CONTEXTS).split(',').map(coerceContext) - console.info('Deploying Forno with full node contexts:', contexts) - const terraformVars: TerraformVars = await getFornoTerraformVars(celoEnv, contexts) - // This prints the global IP address for forno - await deployModule(celoEnv, FORNO_TERRAFORM_MODULE_NAME, terraformVars) - console.info( - 'Note: in order to have an SSL certificate be properly provisioned, DNS entries for the relevant domains must point to the printed IP above.' - ) -} - -export async function destroyForno(celoEnv: string) { - const contexts: string[] = fetchEnv(envVar.FORNO_FULL_NODE_CONTEXTS).split(',').map(coerceContext) - console.info('DESTROYING Forno') - const terraformVars: TerraformVars = await getFornoTerraformVars(celoEnv, contexts) - await destroyModule(celoEnv, FORNO_TERRAFORM_MODULE_NAME, terraformVars) -} - -interface ContextInfoTerraformVars { - service_network_endpoint_group_name: string - zone: string -} - -async function getFornoTerraformVars(celoEnv: string, contexts: string[]): Promise { - let gcloudProject: string | undefined - const getContextInfos = async ( - port: number, - service: string, - namespace: string - ): Promise<{ [context: string]: ContextInfoTerraformVars }> => - contexts.reduce(async (aggPromise, context: string) => { - const agg = await aggPromise - const clusterManager = getClusterManagerForContext(celoEnv, context) - if (clusterManager.cloudProvider !== CloudProvider.GCP) { - throw Error( - `Forno only accepts GCP contexts, context ${context} is ${clusterManager.cloudProvider}` - ) - } - const contextGcloudProject = (clusterManager.clusterConfig as GCPClusterConfig).projectName - // Require all the contexts to have the same project - if (gcloudProject === undefined) { - gcloudProject = contextGcloudProject - } else if (gcloudProject !== contextGcloudProject) { - throw Error(`All contexts must be in the same Google Cloud project`) - } - // Rather than using clusterManager.kubernetesContextName we switch to the - // cluster to account for the case where this user has not gotten the - // context for the cluster yet. - await clusterManager.switchToClusterContext(true) - const [output] = await execCmd( - `kubectl get svc ${service} -n ${namespace} -o jsonpath="{.metadata.annotations.cloud\\.google\\.com/neg-status}"` - ) - if (!output.trim()) { - throw Error(`Expected cloud.google.com/neg-status annotation for service ${service}`) - } - const outputParsed = JSON.parse(output) - if (!outputParsed.network_endpoint_groups[port] || !outputParsed.zones.length) { - throw Error( - `Expected NEG for ${port} and > 0 zones, instead got NEG: ${outputParsed.network_endpoint_groups[port]} and zones ${outputParsed.zones}` - ) - } - return { - ...agg, - [readableContext(context)]: { - service_network_endpoint_group_name: outputParsed.network_endpoint_groups[port], - // Only expect a single zone - zone: outputParsed.zones[0], - }, - } - }, Promise.resolve({})) - - // Make sure each domain ends with a period - const domains = fetchEnv(envVar.FORNO_DOMAINS) - .split(',') - .map((domain: string) => { - if (!domain.endsWith('.')) { - return `${domain}.` - } - return domain - }) - const HTTP_RPC_PORT = 8545 - const WS_RPC_PORT = 8546 - const KONG_RPC_PORT = 80 - const contextInfosHttp = await getContextInfos(HTTP_RPC_PORT, `${celoEnv}-fullnodes-rpc`, celoEnv) - const contextInfosWs = await getContextInfos(WS_RPC_PORT, `${celoEnv}-fullnodes-rpc`, celoEnv) - const contextInfosKong = await getContextInfos(KONG_RPC_PORT, 'kong', 'kong') - const bannedCIDRs = fetchEnv(envVar.FORNO_BANNED_CIDR).split(',') - - return { - backend_max_requests_per_second: '100', - backend_max_requests_per_second_kong: '10000', - celo_env: celoEnv, - context_info_http: JSON.stringify(contextInfosHttp), - context_info_ws: JSON.stringify(contextInfosWs), - context_info_kong: JSON.stringify(contextInfosKong), - gcloud_credentials_path: fetchEnv(envVar.GOOGLE_APPLICATION_CREDENTIALS), - gcloud_project: gcloudProject!, - ssl_cert_domains: JSON.stringify(domains), - banned_cidr: JSON.stringify(bannedCIDRs), - vpc_network_name: fetchEnv(envVar.FORNO_VPC_NETWORK_NAME), - } -} diff --git a/packages/celotool/src/lib/fullnodes.ts b/packages/celotool/src/lib/fullnodes.ts index 824a094bb4e..bdbbae673da 100644 --- a/packages/celotool/src/lib/fullnodes.ts +++ b/packages/celotool/src/lib/fullnodes.ts @@ -1,7 +1,6 @@ import stringHash from 'string-hash' import { getAksClusterConfig, - getAwsClusterConfig, getCloudProviderFromContext, getContextDynamicEnvVarValues, getGCPClusterConfig, @@ -9,7 +8,6 @@ import { import { DynamicEnvVar, envVar, fetchEnv, getDynamicEnvVarValue } from './env-utils' import { CloudProvider } from './k8s-cluster/base' import { AksFullNodeDeploymentConfig } from './k8s-fullnode/aks' -import { AwsFullNodeDeploymentConfig } from './k8s-fullnode/aws' import { BaseFullNodeDeploymentConfig } from './k8s-fullnode/base' import { GCPFullNodeDeploymentConfig } from './k8s-fullnode/gcp' import { getFullNodeDeployer } from './k8s-fullnode/utils' @@ -37,7 +35,6 @@ const contextFullNodeDeploymentEnvVars: { const deploymentConfigGetterByCloudProvider: { [key in CloudProvider]: (context: string) => BaseFullNodeDeploymentConfig } = { - [CloudProvider.AWS]: getAwsFullNodeDeploymentConfig, [CloudProvider.AZURE]: getAksFullNodeDeploymentConfig, [CloudProvider.GCP]: getGCPFullNodeDeploymentConfig, } @@ -174,18 +171,6 @@ function getAksFullNodeDeploymentConfig(context: string): AksFullNodeDeploymentC } } -/** - * For a given context, returns the appropriate AwsFullNodeDeploymentConfig - */ -function getAwsFullNodeDeploymentConfig(context: string): AwsFullNodeDeploymentConfig { - const fullNodeDeploymentConfig: BaseFullNodeDeploymentConfig = - getFullNodeDeploymentConfig(context) - return { - ...fullNodeDeploymentConfig, - clusterConfig: getAwsClusterConfig(context), - } -} - /** * For a given context, returns the appropriate getGCPFullNodeDeploymentConfig */ diff --git a/packages/celotool/src/lib/generate_utils.ts b/packages/celotool/src/lib/generate_utils.ts index 611b6304a93..6da7be1099b 100644 --- a/packages/celotool/src/lib/generate_utils.ts +++ b/packages/celotool/src/lib/generate_utils.ts @@ -398,6 +398,7 @@ export const generateGenesis = ({ churritoBlock, donutBlock, espressoBlock, + gingerbreadBlock, }: GenesisConfig): string => { const genesis: any = { ...TEMPLATE } @@ -414,6 +415,9 @@ export const generateGenesis = ({ if (typeof espressoBlock === 'number') { genesis.config.espressoBlock = espressoBlock } + if (typeof gingerbreadBlock === 'number') { + genesis.config.gingerbreadBlock = gingerbreadBlock + } genesis.config.chainId = chainId @@ -542,6 +546,9 @@ export const generateGenesisWithMigrations = async ({ if (genesisConfig.espressoBlock !== undefined) { mcConfig.hardforks.espressoBlock = genesisConfig.espressoBlock } + if (genesisConfig.gingerbreadBlock !== undefined) { + mcConfig.hardforks.gingerbreadBlock = genesisConfig.gingerbreadBlock + } if (genesisConfig.timestamp !== undefined) { mcConfig.genesisTimestamp = genesisConfig.timestamp } diff --git a/packages/celotool/src/lib/geth.ts b/packages/celotool/src/lib/geth.ts index 96881bdc036..7fdbad2c5e0 100644 --- a/packages/celotool/src/lib/geth.ts +++ b/packages/celotool/src/lib/geth.ts @@ -16,7 +16,7 @@ import Web3 from 'web3' import { Admin } from 'web3-eth-admin' import { spawnCmd, spawnCmdWithExitOnFailure } from './cmd-utils' import { convertToContractDecimals } from './contract-utils' -import { envVar, fetchEnv, isVmBased } from './env-utils' +import { envVar, fetchEnv } from './env-utils' import { AccountType, generateGenesis, @@ -29,7 +29,6 @@ import { retrieveClusterIPAddress, retrieveIPAddress } from './helm_deploy' import { GethInstanceConfig } from './interfaces/geth-instance-config' import { GethRunConfig } from './interfaces/geth-run-config' import { ensure0x } from './utils' -import { getTestnetOutputs } from './vm-testnet-utils' export async function unlockAccount( web3: Web3, @@ -91,29 +90,19 @@ export const getBootnodeEnode = async (namespace: string) => { } export const retrieveBootnodeIPAddress = async (namespace: string) => { - if (isVmBased()) { - const outputs = await getTestnetOutputs(namespace) - return outputs.bootnode_ip_address.value + // Baklava bootnode address comes from VM and has an different name (not possible to update name after creation) + const resourceName = + namespace === 'baklava' ? `${namespace}-bootnode-address` : `${namespace}-bootnode` + if (fetchEnv(envVar.STATIC_IPS_FOR_GETH_NODES) === 'true') { + return retrieveIPAddress(resourceName) } else { - // Baklava bootnode address comes from VM and has an different name (not possible to update name after creation) - const resourceName = - namespace === 'baklava' ? `${namespace}-bootnode-address` : `${namespace}-bootnode` - if (fetchEnv(envVar.STATIC_IPS_FOR_GETH_NODES) === 'true') { - return retrieveIPAddress(resourceName) - } else { - return retrieveClusterIPAddress('service', resourceName, namespace) - } + return retrieveClusterIPAddress('service', resourceName, namespace) } } const retrieveTxNodeAddresses = async (namespace: string, txNodesNum: number) => { - if (isVmBased()) { - const outputs = await getTestnetOutputs(namespace) - return outputs.tx_node_ip_addresses.value - } else { - const txNodesRange = range(0, txNodesNum) - return Promise.all(txNodesRange.map((i) => retrieveIPAddress(`${namespace}-tx-nodes-${i}`))) - } + const txNodesRange = range(0, txNodesNum) + return Promise.all(txNodesRange.map((i) => retrieveIPAddress(`${namespace}-tx-nodes-${i}`))) } const getEnodesWithIpAddresses = async (namespace: string, getExternalIP: boolean) => { @@ -1274,8 +1263,26 @@ export async function startGeth( } } - // Geth startup isn't fully done even when the port is open, so give it another second - await sleep(1000) + // Geth startup isn't fully done even when the port is open, so check until it responds + const maxTries = 5 + let tries = 0 + while (tries < maxTries) { + tries++ + let block = null + try { + block = await new Web3('http://localhost:8545').eth.getBlock('latest') + } catch (e) { + console.log(`Failed to fetch test block: ${e}`) + } + if (block) { + break + } + console.log('Could not fetch test block. Wait one second, then retry.') + await sleep(1000) + } + if (tries === maxTries) { + throw new Error(`Geth did not start within ${tries} seconds`) + } console.log( `${instance.name}: running.`, diff --git a/packages/celotool/src/lib/helm_deploy.ts b/packages/celotool/src/lib/helm_deploy.ts index ed32bb1fd90..ac5975c0964 100644 --- a/packages/celotool/src/lib/helm_deploy.ts +++ b/packages/celotool/src/lib/helm_deploy.ts @@ -15,7 +15,7 @@ import { spawnCmd, spawnCmdWithExitOnFailure, } from './cmd-utils' -import { EnvTypes, envVar, fetchEnv, fetchEnvOrFallback, monorepoRoot } from './env-utils' +import { envTypes, envVar, fetchEnv, fetchEnvOrFallback, monorepoRoot } from './env-utils' import { ensureAuthenticatedGcloudAccount } from './gcloud_utils' import { generateGenesisFromEnv } from './generate_utils' import { @@ -110,7 +110,7 @@ export async function createCloudSQLInstance(celoEnv: string, instanceName: stri } const envType = fetchEnv(envVar.ENV_TYPE) - if (envType !== EnvTypes.DEVELOPMENT) { + if (envType !== envTypes.DEVELOPMENT) { try { await execCmdWithExitOnFailure( `gcloud sql instances create ${instanceName}-replica --master-instance-name=${instanceName} --zone ${fetchEnv( @@ -1398,3 +1398,11 @@ async function generateMyCeloGenesis(): Promise { await spawnCmd('rm', ['-rf', celoBlockchainDir], { silent: true }) return genesisContent } + +function useDefaultNetwork() { + return fetchEnv(envVar.KUBERNETES_CLUSTER_NAME) === 'celo-networks-dev' +} + +export function networkName(celoEnv: string) { + return useDefaultNetwork() ? 'default' : `${celoEnv}-network` +} diff --git a/packages/celotool/src/lib/interfaces/genesis-config.ts b/packages/celotool/src/lib/interfaces/genesis-config.ts index 5e680965924..b59ca86b1e5 100644 --- a/packages/celotool/src/lib/interfaces/genesis-config.ts +++ b/packages/celotool/src/lib/interfaces/genesis-config.ts @@ -15,4 +15,5 @@ export interface GenesisConfig { churritoBlock?: number | null donutBlock?: number | null espressoBlock?: number | null + gingerbreadBlock?: number | null } diff --git a/packages/celotool/src/lib/k8s-cluster/aws.ts b/packages/celotool/src/lib/k8s-cluster/aws.ts deleted file mode 100644 index eda452c94ad..00000000000 --- a/packages/celotool/src/lib/k8s-cluster/aws.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { execCmdWithExitOnFailure } from '../cmd-utils' -import { installGenericHelmChart } from '../helm_deploy' -import { outputIncludes } from '../utils' -import { BaseClusterConfig, BaseClusterManager, CloudProvider } from './base' - -/** - * Basic info for an EKS cluster - */ -export interface AwsClusterConfig extends BaseClusterConfig { - clusterRegion: string - resourceGroupTag: string -} - -export class AwsClusterManager extends BaseClusterManager { - async switchToSubscription() { - // TODO: not supported at the moment - } - - async getAndSwitchToClusterContext() { - await execCmdWithExitOnFailure( - `aws eks --region ${this.clusterConfig.clusterRegion} update-kubeconfig --name ${this.clusterConfig.clusterName} --alias ${this.clusterConfig.clusterName}` - ) - } - - async setupCluster(context?: string) { - await super.setupCluster(context) - await this.installKube2Iam() - } - - // installs kube2iam if it doesn't exist, which allows us to give AWS roles to pods - async installKube2Iam() { - const releaseName = 'kube2iam' - const exists = await outputIncludes( - `helm list`, - releaseName, - `${releaseName} exists, skipping install` - ) - if (!exists) { - console.info(`Installing ${releaseName}`) - await installGenericHelmChart({ - namespace: 'default', - releaseName, - chartDir: 'stable/kube2iam', - parameters: [ - // Modifies node iptables to have AWS api requests be proxied by kube2iam - `--set host.iptables=true`, - // The network interface EKS uses - `--set host.interface="eni+"`, - // enable rbac - `--set rbac.create=true`, - ], - buildDependencies: false, - }) - } - } - - get clusterConfig(): AwsClusterConfig { - return this._clusterConfig as AwsClusterConfig - } - - get kubernetesContextName(): string { - return this.clusterConfig.clusterName - } - - get cloudProvider(): CloudProvider { - return CloudProvider.AWS - } -} diff --git a/packages/celotool/src/lib/k8s-cluster/base.ts b/packages/celotool/src/lib/k8s-cluster/base.ts index 18771caec1c..0a18f49d6f6 100644 --- a/packages/celotool/src/lib/k8s-cluster/base.ts +++ b/packages/celotool/src/lib/k8s-cluster/base.ts @@ -7,7 +7,6 @@ import { } from '../helm_deploy' export enum CloudProvider { - AWS, AZURE, GCP, } diff --git a/packages/celotool/src/lib/k8s-cluster/utils.ts b/packages/celotool/src/lib/k8s-cluster/utils.ts index ab340d376f5..a1584306a80 100644 --- a/packages/celotool/src/lib/k8s-cluster/utils.ts +++ b/packages/celotool/src/lib/k8s-cluster/utils.ts @@ -1,13 +1,10 @@ import { AksClusterConfig, AksClusterManager } from './aks' -import { AwsClusterConfig, AwsClusterManager } from './aws' import { BaseClusterConfig, BaseClusterManager, CloudProvider } from './base' import { GCPClusterConfig, GCPClusterManager } from './gcp' const clusterManagerByCloudProvider: { [key in CloudProvider]: (clusterConfig: BaseClusterConfig, celoEnv: string) => BaseClusterManager } = { - [CloudProvider.AWS]: (clusterConfig: BaseClusterConfig, celoEnv: string) => - new AwsClusterManager(clusterConfig as AwsClusterConfig, celoEnv), [CloudProvider.AZURE]: (clusterConfig: BaseClusterConfig, celoEnv: string) => new AksClusterManager(clusterConfig as AksClusterConfig, celoEnv), [CloudProvider.GCP]: (clusterConfig: BaseClusterConfig, celoEnv: string) => diff --git a/packages/celotool/src/lib/k8s-fullnode/aws.ts b/packages/celotool/src/lib/k8s-fullnode/aws.ts deleted file mode 100644 index 671016104ba..00000000000 --- a/packages/celotool/src/lib/k8s-fullnode/aws.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { - authorizeSecurityGroupIngress, - getClusterSharedNodeSecurityGroup, - revokeSecurityGroupIngress, -} from '../aws' -import { AwsClusterConfig } from '../k8s-cluster/aws' -import { BaseFullNodeDeploymentConfig } from './base' -import { BaseNodePortFullNodeDeployer } from './base-nodeport' - -export interface AwsFullNodeDeploymentConfig extends BaseFullNodeDeploymentConfig { - clusterConfig: AwsClusterConfig -} - -enum Protocols { - tcp = 'tcp', - udp = 'udp', -} - -/** - * At the moment, there is no way to use a Network Load Balancer on EKS - * with ingress TCP & UDP traffic on the same port. Instead, we use NodePort - * services. - */ -export class AwsFullNodeDeployer extends BaseNodePortFullNodeDeployer { - /** - * Gets AWS-specific helm parameters. - */ - async additionalHelmParameters() { - return [ - ...(await super.additionalHelmParameters()), - `--set aws=true`, - `--set storage.storageClass=gp2`, - // A single element because we will be using tcp and udp on a single service - `--set geth.service_protocols='{tcp-and-udp}'`, - ] - } - - /** - * Prints action required to remove the node ports, and removes the chart - */ - async removeChart() { - const nodePortSet = await this.getExistingNodePortSet() - await this.setIngressRulesTCPAndUDP(Array.from(nodePortSet), false) - await super.removeChart() - } - - /** - * When authorize is true, will ensure that the cluster's shared security group - * allows ingress tcp & udp traffic from 0.0.0.0/0 for the specified ports. - * When authorize is false, removes any of the roles corresponding to the ports - * as just described. - */ - async setIngressRulesTCPAndUDP(ports: number[], authorize: boolean) { - const cidrRange = '0.0.0.0/0' - const securityGroup = await getClusterSharedNodeSecurityGroup( - this.deploymentConfig.clusterConfig - ) - - // Record the existing relevant rules on the security group. We want to know - // if both udp and tcp ingress traffic has been enabled for the ports. - const existingRulesByPort: { - [port: number]: { - [protocol in Protocols]: boolean - } - } = {} - for (const rule of securityGroup.IpPermissions) { - // We assume that all rules that have been created by previous full node - // deployments are for a single port, and not port ranges. - // Don't consider rules that do not apply to node port ranges or do not have the - // desired cidr range. - if ( - rule.FromPort !== rule.ToPort || - !this.isNodePort(rule.FromPort) || - !rule.IpRanges.find((rangeSpec: any) => rangeSpec.CidrIp === cidrRange) - ) { - continue - } - const port = rule.FromPort - existingRulesByPort[port] = Object.values(Protocols).reduce( - (obj: any, protocol: Protocols) => ({ - ...obj, - [protocol]: obj[protocol] || rule.IpProtocol === protocol, - }), - existingRulesByPort[port] || {} - ) - } - - // Iterate over all the provided ports and protocols, and either authorize - // or revoke ingress permission. - for (const port of ports) { - for (const protocol of Object.values(Protocols)) { - const infoStr = `${port}/${protocol}` - // If the rule already exists, either skip or revoke - if (existingRulesByPort[port] && existingRulesByPort[port][protocol]) { - if (authorize) { - console.info(`Already authorized ${infoStr}`) - } else { - console.info(`Revoking ${infoStr} authorization`) - await revokeSecurityGroupIngress(securityGroup.GroupId, port, protocol, cidrRange) - } - continue - } else if (authorize) { - console.info(`Authorizing ${infoStr}`) - await authorizeSecurityGroupIngress(securityGroup.GroupId, port, protocol, cidrRange) - } - } - } - } - - get deploymentConfig(): AwsFullNodeDeploymentConfig { - return this._deploymentConfig as AwsFullNodeDeploymentConfig - } -} diff --git a/packages/celotool/src/lib/k8s-fullnode/utils.ts b/packages/celotool/src/lib/k8s-fullnode/utils.ts index 6cb0200d813..67d6e839d65 100644 --- a/packages/celotool/src/lib/k8s-fullnode/utils.ts +++ b/packages/celotool/src/lib/k8s-fullnode/utils.ts @@ -1,6 +1,5 @@ import { CloudProvider } from '../k8s-cluster/base' import { AksFullNodeDeployer, AksFullNodeDeploymentConfig } from './aks' -import { AwsFullNodeDeployer, AwsFullNodeDeploymentConfig } from './aws' import { BaseFullNodeDeployer, BaseFullNodeDeploymentConfig } from './base' import { GCPFullNodeDeployer, GCPFullNodeDeploymentConfig } from './gcp' @@ -10,8 +9,6 @@ const fullNodeDeployerByCloudProvider: { celoEnv: string ) => BaseFullNodeDeployer } = { - [CloudProvider.AWS]: (deploymentConfig: BaseFullNodeDeploymentConfig, celoEnv: string) => - new AwsFullNodeDeployer(deploymentConfig as AwsFullNodeDeploymentConfig, celoEnv), [CloudProvider.AZURE]: (deploymentConfig: BaseFullNodeDeploymentConfig, celoEnv: string) => new AksFullNodeDeployer(deploymentConfig as AksFullNodeDeploymentConfig, celoEnv), [CloudProvider.GCP]: (deploymentConfig: BaseFullNodeDeploymentConfig, celoEnv: string) => diff --git a/packages/celotool/src/lib/k8s-oracle/aws-hsm.ts b/packages/celotool/src/lib/k8s-oracle/aws-hsm.ts deleted file mode 100644 index 0a8a6b09a8f..00000000000 --- a/packages/celotool/src/lib/k8s-oracle/aws-hsm.ts +++ /dev/null @@ -1,161 +0,0 @@ -import { - attachPolicyIdempotent, - createPolicyIdempotent, - createRoleIdempotent, - deletePolicy, - deleteRole, - detachPolicyIdempotent, - getEKSNodeInstanceGroupRoleArn, - getKeyArnFromAlias, - getPolicyArn, -} from '../aws' -import { AwsClusterConfig } from '../k8s-cluster/aws' -import { BaseOracleDeploymentConfig, OracleIdentity } from './base' -import { RbacOracleDeployer } from './rbac' - -/** - * Contains information needed when using Azure HSM signing - */ -export interface AwsHsmOracleIdentity extends OracleIdentity { - keyAlias: string - region: string -} - -export interface AwsHsmOracleDeploymentConfig extends BaseOracleDeploymentConfig { - identities: AwsHsmOracleIdentity[] - clusterConfig: AwsClusterConfig -} - -/** - * AwsHsmOracleDeployer manages deployments for HSM-based oracles on AWS - */ -export class AwsHsmOracleDeployer extends RbacOracleDeployer { - // Explicitly specify this so we enforce AwsHsmOracleDeploymentConfig - constructor(deploymentConfig: AwsHsmOracleDeploymentConfig, celoEnv: string) { - super(deploymentConfig, celoEnv) - } - - async removeChart() { - await super.removeChart() - for (const identity of this.deploymentConfig.identities) { - await this.deleteAwsHsmRoleAndPolicyIdempotent(identity) - } - } - - async helmParameters() { - return [ - ...(await super.helmParameters()), - `--set kube.cloudProvider=aws`, - `--set oracle.walletType=AWS_HSM`, - ] - } - - async oracleIdentityHelmParameters() { - let params = await super.oracleIdentityHelmParameters() - for (let i = 0; i < this.replicas; i++) { - const identity = this.deploymentConfig.identities[i] - const prefix = `--set oracle.identities[${i}]` - const awsRoleArn = await this.createAwsHsmRoleIdempotent(identity) - params = params.concat([`${prefix}.aws.roleArn=${awsRoleArn}`]) - } - return params - } - - /** - * Creates an AWS role for a specific oracle identity with the - * appropriate permissions to use its HSM. - * Idempotent. - */ - async createAwsHsmRoleIdempotent(identity: AwsHsmOracleIdentity) { - // The role that each node (ie VM) uses - const nodeInstanceGroupRoleArn = await getEKSNodeInstanceGroupRoleArn( - this.deploymentConfig.clusterConfig.clusterName - ) - // This is a "trust relationship" that allows the node instance group role - // to assume this role (via kube2iam). - const rolePolicy = { - Version: '2012-10-17', - Statement: [ - { - Sid: '', - Effect: 'Allow', - Principal: { - AWS: nodeInstanceGroupRoleArn, - }, - Action: 'sts:AssumeRole', - }, - ], - } - const roleName = this.awsHsmRoleName(identity) - const roleArn = await createRoleIdempotent(roleName, JSON.stringify(rolePolicy)) - const policyName = this.awsHsmPolicyName(identity) - const keyArn = await getKeyArnFromAlias(identity.keyAlias, identity.region) - const policyArn = await this.createAwsHsmSignPolicyIdempotent(policyName, keyArn) - await attachPolicyIdempotent(roleName, policyArn) - return roleArn - } - - /** - * Creates an AWS policy to allow usage of an HSM. - * Idempotent. - */ - async createAwsHsmSignPolicyIdempotent(policyName: string, keyArn: string) { - const policy = { - Version: '2012-10-17', - Statement: [ - { - Sid: 'VisualEditor0', - Effect: 'Allow', - Action: ['kms:GetPublicKey', 'kms:DescribeKey', 'kms:Sign'], - Resource: keyArn, - }, - { - Sid: 'VisualEditor1', - Effect: 'Allow', - Action: 'kms:ListKeys', - Resource: '*', - }, - ], - } - return createPolicyIdempotent(policyName, JSON.stringify(policy)) - } - - /** - * Deletes both the AWS role and policy for a particular identity. - * Note this assumes that the policy has only been attached to the corresponding - * role and no others. This may not be the case if someone manually attaches - * the policy to a different role in the AWS console. - */ - async deleteAwsHsmRoleAndPolicyIdempotent(identity: AwsHsmOracleIdentity) { - const roleName = this.awsHsmRoleName(identity) - const policyName = this.awsHsmPolicyName(identity) - console.info(`Deleting AWS role ${roleName} and policy ${policyName}`) - const policyArn = await getPolicyArn(policyName) - if (policyArn) { - // Don't throw if it's not attached - try { - await detachPolicyIdempotent(roleName, policyArn) - } catch (e: any) { - console.info(`Could not detatch policy ${policyArn} from role ${roleName}:`, e.message) - } - await deletePolicy(policyArn) - } - try { - await deleteRole(roleName) - } catch (e: any) { - console.info(`Could not delete role ${roleName}:`, e.message) - } - } - - awsHsmRoleName(identity: AwsHsmOracleIdentity) { - return `${identity.keyAlias}-${identity.currencyPair}-${identity.address}`.substring(0, 64) - } - - awsHsmPolicyName(identity: AwsHsmOracleIdentity) { - return `${identity.keyAlias}-${identity.currencyPair}-${identity.address}` - } - - get deploymentConfig(): AwsHsmOracleDeploymentConfig { - return this._deploymentConfig as AwsHsmOracleDeploymentConfig - } -} diff --git a/packages/celotool/src/lib/k8s-oracle/base.ts b/packages/celotool/src/lib/k8s-oracle/base.ts index 557c978db3f..e6ef12c8a0d 100644 --- a/packages/celotool/src/lib/k8s-oracle/base.ts +++ b/packages/celotool/src/lib/k8s-oracle/base.ts @@ -1,8 +1,8 @@ import { getFornoUrl, getFornoWebSocketUrl, - getFullNodeHttpRpcInternalUrl, - getFullNodeWebSocketRpcInternalUrl, + getLightNodeHttpRpcInternalUrl, + getLightNodeWebSocketRpcInternalUrl, } from 'src/lib/endpoints' import { envVar, fetchEnv, fetchEnvOrFallback } from 'src/lib/env-utils' import { @@ -68,11 +68,12 @@ export abstract class BaseOracleDeployer { async helmParameters() { const httpRpcProviderUrl = this.deploymentConfig.useForno ? getFornoUrl(this.celoEnv) - : getFullNodeHttpRpcInternalUrl(this.celoEnv) + : getLightNodeHttpRpcInternalUrl(this.celoEnv) const wsRpcProviderUrl = this.deploymentConfig.useForno ? getFornoWebSocketUrl(this.celoEnv) - : getFullNodeWebSocketRpcInternalUrl(this.celoEnv) + : getLightNodeWebSocketRpcInternalUrl(this.celoEnv) return [ + `--set-literal oracle.api_keys=${fetchEnv(envVar.ORACLE_FX_ADAPTERS_API_KEYS)}`, `--set environment.name=${this.celoEnv}`, `--set image.repository=${fetchEnv(envVar.ORACLE_DOCKER_IMAGE_REPOSITORY)}`, `--set image.tag=${fetchEnv(envVar.ORACLE_DOCKER_IMAGE_TAG)}`, diff --git a/packages/celotool/src/lib/k8s-oracle/rbac.ts b/packages/celotool/src/lib/k8s-oracle/rbac.ts index 4ad47cd5cf1..7d45f97b42b 100644 --- a/packages/celotool/src/lib/k8s-oracle/rbac.ts +++ b/packages/celotool/src/lib/k8s-oracle/rbac.ts @@ -3,7 +3,6 @@ import { removeGenericHelmChart, upgradeGenericHelmChart, } from 'src/lib/helm_deploy' -import { execCmdWithExitOnFailure } from '../cmd-utils' import { BaseOracleDeployer } from './base' // Oracle RBAC------ @@ -16,7 +15,7 @@ const rbacHelmChartPath = '../helm-charts/oracle-rbac' /** * RbacOracleDeployer cloud-agnostically manages deployments for oracles * whose pods must change their metadata in order to accomodate limitations - * in pod identity solutions (like Azure's aad-pod-identity and AWS's kube2iam). + * in pod identity solutions (like Azure's aad-pod-identity). * This will create a k8s service account for each oracle pod that can modify * pod metadata, and will ensure each SA's credentials make their way to the helm chart. */ @@ -63,17 +62,9 @@ export abstract class RbacOracleDeployer extends BaseOracleDeployer { } async rbacServiceAccountSecretNames() { - const names = [...Array(this.replicas).keys()].map((i) => `${this.rbacReleaseName()}-${i}`) - let jsonSecretPath = '"{.items[*].secrets[0][\'name\']}"' - if (names.length === 1) { - jsonSecretPath = '"{.secrets[0][\'name\']}"' - } - const [tokenName] = await execCmdWithExitOnFailure( - `kubectl get serviceaccount --namespace=${this.celoEnv} ${names.join( - ' ' - )} -o=jsonpath=${jsonSecretPath}` - ) - return tokenName.trim().split(' ') + return [...Array(this.replicas).keys()].map((i) => { + return `${this.rbacReleaseName()}-secret-${i}` + }) } rbacReleaseName() { diff --git a/packages/celotool/src/lib/komenci.ts b/packages/celotool/src/lib/komenci.ts deleted file mode 100644 index 7edeab3d0ce..00000000000 --- a/packages/celotool/src/lib/komenci.ts +++ /dev/null @@ -1,537 +0,0 @@ -import { ensureLeading0x, privateKeyToAddress } from '@celo/utils/src/address' -import { execCmdWithExitOnFailure } from 'src/lib/cmd-utils' -import { - getFornoUrl, - getFullNodeHttpRpcInternalUrl, - getFullNodeWebSocketRpcInternalUrl, -} from 'src/lib/endpoints' -import { DynamicEnvVar, envVar, fetchEnv, fetchEnvOrFallback } from 'src/lib/env-utils' -import { AccountType, getPrivateKeysFor } from 'src/lib/generate_utils' -import { - installGenericHelmChart, - removeGenericHelmChart, - upgradeGenericHelmChart, -} from 'src/lib/helm_deploy' -import { createKeyVaultIdentityIfNotExists, deleteAzureKeyVaultIdentity } from './azure' -import { getAksClusterConfig, getContextDynamicEnvVarValues } from './context-utils' -const helmChartPath = '../helm-charts/komenci' -const rbacHelmChartPath = '../helm-charts/komenci-rbac' - -/** - * Contains information needed when using Azure HSM signing - */ -interface KomenciAzureHsmIdentity { - identityName: string - keyVaultName: string - // If a resource group is not specified, it is assumed to be the same - // as the kubernetes cluster resource group specified in the AKSClusterConfig - resourceGroup?: string -} - -/** - * Represents the identity of a single komenci relayer - */ -interface KomenciIdentity { - address: string - // Used if generating komenci relayers from a mnemonic - privateKey?: string - // Used if using Azure HSM signing - azureHsmIdentity?: KomenciAzureHsmIdentity -} - -/** - * Configuration of multiple relayers - */ -interface KomenciConfig { - relayerIdentities: KomenciIdentity[] - // TODO: For Signup rewards - // foundationRewardsIdentities: KomenciIdentity[] - cLabsRewardsIdentities: KomenciIdentity[] -} - -interface KomenciKeyVaultIdentityConfig { - addressAzureKeyVaults: string -} - -interface KomenciMnemonicIdentityConfig { - addressesFromMnemonicCount: string -} - -interface KomenciRewardServiceConfig { - instanceCount: number -} - -interface KomenciDatabaseConfig { - host: string - port: string - username: string - passwordVaultName: string -} - -enum RewardType { - Foundation, - CeloLabs, -} - -/** - * Env vars corresponding to each value for the KomenciKeyVaultIdentityConfig for a particular context - */ -const contextKomenciKeyVaultIdentityConfigDynamicEnvVars: { - [k in keyof KomenciKeyVaultIdentityConfig]: DynamicEnvVar -} = { - addressAzureKeyVaults: DynamicEnvVar.KOMENCI_ADDRESS_AZURE_KEY_VAULTS, -} - -/** - * Env vars corresponding to each value for the KomenciMnemonicIdentityConfig for a particular context - */ -const contextKomenciMnemonicIdentityConfigDynamicEnvVars: { - [k in keyof KomenciMnemonicIdentityConfig]: DynamicEnvVar -} = { - addressesFromMnemonicCount: DynamicEnvVar.KOMENCI_ADDRESSES_FROM_MNEMONIC_COUNT, -} - -/** - * Env vars corresponding to each value for the KomenciFoundationRewardsKeyVaultIdentityConfig for a particular context - */ -const contextKomenciFoundationRewardsKeyVaultIdentityConfigDynamicEnvVars: { - [k in keyof KomenciKeyVaultIdentityConfig]: DynamicEnvVar -} = { - addressAzureKeyVaults: DynamicEnvVar.KOMENCI_FOUNDATION_REWARDS_ADDRESS_AZURE_KEY_VAULTS, -} - -/** - * Env vars corresponding to each value for the KomenciCeloLabsRewardsKeyVaultIdentityConfig for a particular context - */ -const contextKomenciCeloLabsRewardsKeyVaultIdentityConfigDynamicEnvVars: { - [k in keyof KomenciKeyVaultIdentityConfig]: DynamicEnvVar -} = { - addressAzureKeyVaults: DynamicEnvVar.KOMENCI_CELOLABS_REWARDS_ADDRESS_AZURE_KEY_VAULTS, -} - -/** - * Env vars corresponding to each value for the KomenciCeloLabsRewardsKeyVaultIdentityConfig for a particular context - */ -const contextKomenciRewardsServiceConfigDynamicEnvVars: { - [k in keyof KomenciRewardServiceConfig]: DynamicEnvVar -} = { - instanceCount: DynamicEnvVar.KOMENCI_REWARD_SERVICE_INSTANCE_COUNT, -} - -const contextDatabaseConfigDynamicEnvVars: { [k in keyof KomenciDatabaseConfig]: DynamicEnvVar } = { - host: DynamicEnvVar.KOMENCI_DB_HOST, - port: DynamicEnvVar.KOMENCI_DB_PORT, - username: DynamicEnvVar.KOMENCI_DB_USERNAME, - passwordVaultName: DynamicEnvVar.KOMENCI_DB_PASSWORD_VAULT_NAME, -} - -const contextRewardServiceDatabaseConfigDynamicEnvVars: { - [k in keyof KomenciDatabaseConfig]: DynamicEnvVar -} = { - host: DynamicEnvVar.KOMENCI_REWARD_SERVICE_DB_HOST, - port: DynamicEnvVar.KOMENCI_REWARD_SERVICE_DB_PORT, - username: DynamicEnvVar.KOMENCI_REWARD_SERVICE_DB_USERNAME, - passwordVaultName: DynamicEnvVar.KOMENCI_REWARD_SERVICE_DB_PASSWORD_VAULT_NAME, -} - -function releaseName(celoEnv: string) { - return `${celoEnv}-komenci` -} - -export async function installHelmChart(celoEnv: string, context: string, useForno: boolean) { - // First install the komenci-rbac helm chart. - // This must be deployed before so we can use a resulting auth token so that - // komenci pods can reach the K8s API server to change their aad labels - await installKomenciRBACHelmChart(celoEnv, context) - // Then install the komenci helm chart - return installGenericHelmChart({ - namespace: celoEnv, - releaseName: releaseName(celoEnv), - chartDir: helmChartPath, - parameters: await helmParameters(celoEnv, context, useForno), - }) -} - -export async function upgradeKomenciChart(celoEnv: string, context: string, useFullNodes: boolean) { - await upgradeKomenciRBACHelmChart(celoEnv, context) - return upgradeGenericHelmChart({ - namespace: celoEnv, - releaseName: releaseName(celoEnv), - chartDir: helmChartPath, - parameters: await helmParameters(celoEnv, context, useFullNodes), - }) -} - -export async function removeHelmRelease(celoEnv: string, context: string) { - await removeGenericHelmChart(releaseName(celoEnv), celoEnv) - await removeKomenciRBACHelmRelease(celoEnv) - const komenciConfig = getKomenciConfig(context) - for (const identity of komenciConfig.relayerIdentities) { - // If the identity is using Azure HSM signing, clean it up too - if (identity.azureHsmIdentity) { - await deleteAzureKeyVaultIdentity( - context, - identity.azureHsmIdentity.identityName, - identity.azureHsmIdentity.keyVaultName - ) - } - } -} - -async function getKeyVaultSecret(vaultName: string, secretName: string) { - const [secret] = await execCmdWithExitOnFailure( - `az keyvault secret show --name ${secretName} --vault-name ${vaultName} --query value` - ) - return secret -} - -async function getPasswordFromKeyVaultSecret(vaultName: string, secretName: string) { - const password = await getKeyVaultSecret(vaultName, secretName) - return password.replace(/\n|"/g, '') -} - -async function helmParameters(celoEnv: string, context: string, useForno: boolean) { - const komenciConfig = getKomenciConfig(context) - - const onboardingRelayerCount = komenciConfig.relayerIdentities.length - const rewardsRelayerCount = komenciConfig.cLabsRewardsIdentities.length - const kubeServiceAccountSecretNames = await rbacServiceAccountSecretNames( - celoEnv, - '', - onboardingRelayerCount - ) - const kubeRewardsServiceAccountSecretNames = await rbacServiceAccountSecretNames( - celoEnv, - 'rewards-', - rewardsRelayerCount - ) - - const databaseConfig = getContextDynamicEnvVarValues(contextDatabaseConfigDynamicEnvVars, context) - const rewardDatabaseConfig = getContextDynamicEnvVarValues( - contextRewardServiceDatabaseConfigDynamicEnvVars, - context - ) - const vars = getContextDynamicEnvVarValues( - { - network: DynamicEnvVar.KOMENCI_NETWORK, - appSecretsKeyVault: DynamicEnvVar.KOMENCI_APP_SECRETS_VAULT_NAME, - captchaBypassEnabled: DynamicEnvVar.KOMENCI_RULE_CONFIG_CAPTCHA_BYPASS_ENABLED, - }, - context - ) - const httpRpcProviderUrl = useForno - ? getFornoUrl(celoEnv) - : getFullNodeHttpRpcInternalUrl(celoEnv) - // TODO: let forno support websockets - const wsRpcProviderUrl = getFullNodeWebSocketRpcInternalUrl(celoEnv) - const databasePassword = await getPasswordFromKeyVaultSecret( - databaseConfig.passwordVaultName, - 'DB-PASSWORD' - ) - const rewardDatabasePassword = await getPasswordFromKeyVaultSecret( - rewardDatabaseConfig.passwordVaultName, - 'DB-PASSWORD' - ) - const recaptchaToken = await getPasswordFromKeyVaultSecret( - vars.appSecretsKeyVault, - 'RECAPTCHA-SECRET-KEY' - ) - const loggerCredentials = await getPasswordFromKeyVaultSecret( - vars.appSecretsKeyVault, - 'LOGGER-SERVICE-ACCOUNT' - ) - const segmentApiKey = await getPasswordFromKeyVaultSecret( - vars.appSecretsKeyVault, - 'SEGMENT-API-KEY' - ) - const rewardServiceConfig = getContextDynamicEnvVarValues( - contextKomenciRewardsServiceConfigDynamicEnvVars, - context - ) - const clusterConfig = getAksClusterConfig(context) - - return [ - `--set domain.name=${fetchEnv(envVar.CLUSTER_DOMAIN_NAME)}`, - `--set environment.name=${celoEnv}`, - `--set environment.network=${vars.network}`, - `--set environment.cluster.name=${clusterConfig.clusterName}`, - `--set environment.cluster.location=${clusterConfig.regionName}`, - `--set loggingAgent.credentials=${loggerCredentials}`, - `--set image.repository=${fetchEnv(envVar.KOMENCI_DOCKER_IMAGE_REPOSITORY)}`, - `--set image.tag=${fetchEnv(envVar.KOMENCI_DOCKER_IMAGE_TAG)}`, - `--set kube.serviceAccountSecretNames='{${kubeServiceAccountSecretNames.join(',')}}'`, - `--set komenci.azureHsm.initTryCount=5`, - `--set komenci.azureHsm.initMaxRetryBackoffMs=30000`, - `--set onboarding.recaptchaToken=${recaptchaToken}`, - `--set onboarding.replicas=${onboardingRelayerCount}`, - `--set onboarding.relayer.host=${celoEnv + '-relayer'}`, - `--set onboarding.db.host=${databaseConfig.host}`, - `--set onboarding.db.port=${databaseConfig.port}`, - `--set onboarding.db.username=${databaseConfig.username}`, - `--set onboarding.db.password=${databasePassword}`, - `--set onboarding.publicHostname=${getPublicHostname(clusterConfig.regionName, celoEnv)}`, - `--set onboarding.publicUrl=${ - 'https://' + getPublicHostname(clusterConfig.regionName, celoEnv) - }`, - `--set onboarding.ruleConfig.captcha.bypassEnabled=${vars.captchaBypassEnabled}`, - `--set onboarding.ruleConfig.captcha.bypassToken=${fetchEnv( - envVar.KOMENCI_RULE_CONFIG_CAPTCHA_BYPASS_TOKEN - )}`, - `--set relayer.replicas=${onboardingRelayerCount}`, - `--set relayer.rpcProviderUrls.http=${httpRpcProviderUrl}`, - `--set relayer.rpcProviderUrls.ws=${wsRpcProviderUrl}`, - `--set relayer.metrics.enabled=true`, - `--set relayer.metrics.prometheusPort=9090`, - `--set-string relayer.unusedKomenciAddresses='${fetchEnvOrFallback( - envVar.KOMENCI_UNUSED_KOMENCI_ADDRESSES, - '' - ) - .split(',') - .join('\\,')}'`, - `--set rewards.replicas=${rewardServiceConfig.instanceCount}`, - `--set rewards.db.host=${rewardDatabaseConfig.host}`, - `--set rewards.db.port=${rewardDatabaseConfig.port}`, - `--set rewards.db.username=${rewardDatabaseConfig.username}`, - `--set rewards.db.password=${rewardDatabasePassword}`, - `--set rewards.segmentApiKey=${segmentApiKey}`, - `--set rewards.shouldSendRewards=${fetchEnv(envVar.KOMENCI_SHOULD_SEND_REWARDS)}`, - `--set rewards.metrics.enabled=true`, - `--set rewards.metrics.prometheusPort=9090`, - `--set rewards.relayer.replicas=${rewardsRelayerCount}`, - `--set rewards.relayer.rpcProviderUrls.http=${httpRpcProviderUrl}`, - `--set rewards.relayer.rpcProviderUrls.ws=${wsRpcProviderUrl}`, - `--set rewards.relayer.metrics.enabled=true`, - `--set rewards.relayer.metrics.prometheusPort=9090`, - `--set rewards.relayer.host=${celoEnv + '-rewards-relayer'}`, - `--set kube.rewardsServiceAccountSecretNames='{${kubeRewardsServiceAccountSecretNames.join( - ',' - )}}'`, - ] - .concat( - await komenciIdentityHelmParameters(context, komenciConfig.relayerIdentities, 'relayer') - ) - .concat( - await komenciIdentityHelmParameters( - context, - komenciConfig.cLabsRewardsIdentities, - 'rewards.relayer' - ) - ) -} - -function getPublicHostname(regionName: string, celoEnv: string): string { - return regionName + '.komenci.' + celoEnv + '.' + fetchEnv(envVar.CLUSTER_DOMAIN_NAME) + '.org' -} - -/** - * Returns an array of helm command line parameters for the komenci relayer identities. - * Supports both private key and Azure HSM signing. - */ -async function komenciIdentityHelmParameters( - context: string, - relayerIdentities: KomenciIdentity[], - envVarPrefix: string -) { - const replicas = relayerIdentities.length - let params: string[] = [] - for (let i = 0; i < replicas; i++) { - const komenciIdentity = relayerIdentities[i] - const prefix = `--set ${envVarPrefix}.identities[${i}]` - params.push(`${prefix}.address=${komenciIdentity.address}`) - // An komenci identity can specify either a private key or some information - // about an Azure Key Vault that houses an HSM with the address provided. - // We provide the appropriate parameters for both of those types of identities. - if (komenciIdentity.azureHsmIdentity) { - const azureIdentity = await createKeyVaultIdentityIfNotExists( - context, - komenciIdentity.azureHsmIdentity.identityName, - komenciIdentity.azureHsmIdentity.keyVaultName, - komenciIdentity.azureHsmIdentity.resourceGroup, - ['get', 'list', 'sign'], - null - ) - params = params.concat([ - `${prefix}.azure.id=${azureIdentity.id}`, - `${prefix}.azure.clientId=${azureIdentity.clientId}`, - `${prefix}.azure.keyVaultName=${komenciIdentity.azureHsmIdentity.keyVaultName}`, - ]) - } else if (komenciIdentity.privateKey) { - params.push(`${prefix}.privateKey=${komenciIdentity.privateKey}`) - } else { - throw Error(`Incomplete relayer identity: ${komenciIdentity}`) - } - } - return params -} - -/** - * Gives a config for all komenci services for a particular context - */ -function getKomenciConfig(context: string): KomenciConfig { - return { - relayerIdentities: getKomenciRelayerIdentities(context), - cLabsRewardsIdentities: getKomenciRewardIdentities(context, RewardType.CeloLabs), - // foundationRewardsIdentities: getKomenciRewardIdentities(context, RewardType.Foundation), - } -} - -/** - * Returns an array of komenci identities. If the Azure Key Vault env var is specified, - * the identities are created from that. Otherwise, the identities are created - * with private keys generated by the mnemonic. - */ -function getKomenciRelayerIdentities(context: string): KomenciIdentity[] { - const { addressAzureKeyVaults } = getContextDynamicEnvVarValues( - contextKomenciKeyVaultIdentityConfigDynamicEnvVars, - context, - { - addressAzureKeyVaults: '', - } - ) - // Give priority to key vault - if (addressAzureKeyVaults) { - return getAzureHsmKomenciIdentities(addressAzureKeyVaults) - } - - // If key vaults are not set, try from mnemonic - const { addressesFromMnemonicCount } = getContextDynamicEnvVarValues( - contextKomenciMnemonicIdentityConfigDynamicEnvVars, - context, - { - addressesFromMnemonicCount: '', - } - ) - if (addressesFromMnemonicCount) { - const addressesFromMnemonicCountNum = parseInt(addressesFromMnemonicCount, 10) - return getMnemonicBasedKomenciIdentities(addressesFromMnemonicCountNum) - } - - throw Error('No komenci identity env vars specified') -} - -/** - * Returns an array of komenci reward identities. The identities are created from the Azure Key Vault env var. - */ -function getKomenciRewardIdentities(context: string, rewardType: RewardType): KomenciIdentity[] { - const envVars = - rewardType === RewardType.Foundation - ? contextKomenciFoundationRewardsKeyVaultIdentityConfigDynamicEnvVars - : contextKomenciCeloLabsRewardsKeyVaultIdentityConfigDynamicEnvVars - const { addressAzureKeyVaults } = getContextDynamicEnvVarValues(envVars, context, { - addressAzureKeyVaults: '', - }) - - if (addressAzureKeyVaults) { - return getAzureHsmKomenciIdentities(addressAzureKeyVaults) - } - - throw Error('No komenci reward identity env vars specified') -} - -/** - * Given a string addressAzureKeyVaults of the form: - *
:,
: - * eg: 0x0000000000000000000000000000000000000000:keyVault0,0x0000000000000000000000000000000000000001:keyVault1 - * returns an array of KomenciIdentity in the same order - */ -function getAzureHsmKomenciIdentities(addressAzureKeyVaults: string): KomenciIdentity[] { - const identityStrings = addressAzureKeyVaults.split(',') - const identities = [] - for (const identityStr of identityStrings) { - const [address, keyVaultName, resourceGroup] = identityStr.split(':') - // resourceGroup can be undefined - if (!address || !keyVaultName) { - throw Error( - `Address or key vault name is invalid. Address: ${address} Key Vault Name: ${keyVaultName}` - ) - } - identities.push({ - address, - azureHsmIdentity: { - identityName: getKomenciAzureIdentityName(keyVaultName, address), - keyVaultName, - resourceGroup, - }, - }) - } - return identities -} - -/** - * Returns komenci identities with private keys and addresses generated from the mnemonic - */ -function getMnemonicBasedKomenciIdentities(count: number): KomenciIdentity[] { - return getPrivateKeysFor(AccountType.PRICE_ORACLE, fetchEnv(envVar.MNEMONIC), count).map( - (pkey) => ({ - address: privateKeyToAddress(pkey), - privateKey: ensureLeading0x(pkey), - }) - ) -} - -/** - * @return the intended name of an azure identity given a key vault name and address - */ -function getKomenciAzureIdentityName(keyVaultName: string, address: string) { - // from https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/resource-name-rules#microsoftmanagedidentity - const maxIdentityNameLength = 128 - return `${keyVaultName}-${address}`.substring(0, maxIdentityNameLength) -} - -// Komenci RBAC------ -// We need the relayer pods to be able to change their label to accommodate -// limitations in aad-pod-identity & statefulsets (see https://github.com/Azure/aad-pod-identity/issues/237#issuecomment-611672987) -// To do this, we use an auth token that we get using the resources in the `komenci-rbac` chart - -async function installKomenciRBACHelmChart(celoEnv: string, context: string) { - return installGenericHelmChart({ - namespace: celoEnv, - releaseName: rbacReleaseName(celoEnv, ''), - chartDir: rbacHelmChartPath, - parameters: rbacHelmParameters(celoEnv, context), - }) -} - -async function upgradeKomenciRBACHelmChart(celoEnv: string, context: string) { - return upgradeGenericHelmChart({ - namespace: celoEnv, - releaseName: rbacReleaseName(celoEnv, ''), - chartDir: rbacHelmChartPath, - parameters: rbacHelmParameters(celoEnv, context), - }) -} - -function removeKomenciRBACHelmRelease(celoEnv: string) { - return removeGenericHelmChart(rbacReleaseName(celoEnv, ''), celoEnv) -} - -function rbacHelmParameters(celoEnv: string, context: string) { - const komenciConfig = getKomenciConfig(context) - console.info(komenciConfig) - const relayerReplicas = komenciConfig.relayerIdentities.length - const rewardsRelayerReplicas = komenciConfig.cLabsRewardsIdentities.length - return [ - `--set environment.name=${celoEnv}`, - `--set relayer.replicas=${relayerReplicas}`, - `--set rewards.relayer.replicas=${rewardsRelayerReplicas}`, - ] -} - -function rbacReleaseName(celoEnv: string, prefix: string) { - return `${celoEnv}-komenci-${prefix}rbac` -} - -async function rbacServiceAccountSecretNames(celoEnv: string, prefix: string, replicas: number) { - const names = [...Array(replicas).keys()].map((i) => `${rbacReleaseName(celoEnv, prefix)}-${i}`) - let jsonSecretPath = '"{.items[*].secrets[0][\'name\']}"' - if (names.length === 1) { - jsonSecretPath = '"{.secrets[0][\'name\']}"' - } - const [tokenName] = await execCmdWithExitOnFailure( - `kubectl get serviceaccount --namespace=${celoEnv} ${names.join( - ' ' - )} -o=jsonpath=${jsonSecretPath}` - ) - const tokenNames = tokenName.trim().split(' ') - return tokenNames -} diff --git a/packages/celotool/src/lib/kong.ts b/packages/celotool/src/lib/kong.ts deleted file mode 100644 index 2f191ceae94..00000000000 --- a/packages/celotool/src/lib/kong.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { readFileSync, writeFileSync } from 'fs' -import { execCmdWithExitOnFailure } from 'src/lib/cmd-utils' -import { outputIncludes } from 'src/lib/utils' -import { createNamespaceIfNotExists } from './cluster' -import { installGenericHelmChart, retrieveIPAddress, upgradeGenericHelmChart } from './helm_deploy' - -const kongChartPath = '../helm-charts/kong' -const kongaChartPath = '../helm-charts/konga' - -// One unique kong/a deployment per cluster -const kongReleaseName = 'kong' -const kongNamespace = 'kong' -const kongaReleaseName = 'konga' -const kongaNamespace = 'kong' - -export async function installKong(celoEnv: string) { - await createNamespaceIfNotExists(kongNamespace) - await createUpdateKongConfigMap(celoEnv) - // Update values in values-clabs.yaml file - return installGenericHelmChart({ - namespace: kongNamespace, - releaseName: kongReleaseName, - chartDir: kongChartPath, - parameters: await kongHelmParamenters(celoEnv), - buildDependencies: true, - valuesOverrideFile: 'values-clabs.yaml', - }) -} - -export async function upgradeKong(celoEnv: string) { - await createUpdateKongConfigMap(celoEnv) - return upgradeGenericHelmChart({ - namespace: kongNamespace, - releaseName: kongReleaseName, - chartDir: kongChartPath, - parameters: await kongHelmParamenters(celoEnv), - buildDependencies: true, - valuesOverrideFile: 'values-clabs.yaml', - }) -} - -export async function installKonga(celoEnv: string) { - await createNamespaceIfNotExists(kongaNamespace) - // Update values in values.yaml file - return installGenericHelmChart({ - namespace: kongaNamespace, - releaseName: kongaReleaseName, - chartDir: kongaChartPath, - parameters: kongaHelmParamenters(celoEnv), - }) -} - -export async function upgradeKonga(celoEnv: string) { - return upgradeGenericHelmChart({ - namespace: kongaNamespace, - releaseName: kongaReleaseName, - chartDir: kongaChartPath, - parameters: kongaHelmParamenters(celoEnv), - }) -} - -export async function destroyKongAndKonga() { - await execCmdWithExitOnFailure(`kubectl delete ns ${kongNamespace} ${kongaNamespace}`) -} - -async function kongHelmParamenters(celoEnv: string) { - // GCP Internal infra ips - let trustedIPs = '130.211.0.0/22,35.191.0.0/16' - const fornoPublicGlobalIp = await retrieveIPAddress(`${celoEnv}-forno-global-address`, 'global') - trustedIPs = `${trustedIPs},${fornoPublicGlobalIp}/32` - return [ - `--set kong.extraEnvVars[0].name=KONG_TRUSTED_IPS`, - `--set kong.extraEnvVars[0].value='${trustedIPs.replace(/,/g, '\\,')}'`, - ] -} - -function kongaHelmParamenters(celoEnv: string) { - return [`--set geth_rpc_service=${celoEnv}-fullnodes-rpc.${celoEnv}`] -} - -/** - * Creates a configMap with the kong configuration - * Configuration is read from a kong config file inside the kong chart folder - */ -export async function createUpdateKongConfigMap(celoEnv: string) { - const kongConfig = readFileSync(`${kongChartPath}/kong.conf`).toString() - // We need to patch this file with the forno public ip as this ip will forward - // the requests and need to put in the config file so kong/nginx can consider - // that ip as internal - let trustedIPs = '130.211.0.0/22,35.191.0.0/16' - const fornoPublicGlobalIp = await retrieveIPAddress(`${celoEnv}-forno-global-address`, 'global') - trustedIPs = `${trustedIPs},${fornoPublicGlobalIp}/32` - const re = '/^trusted_ips = .+$/g' - kongConfig.replace(re, `trusted_ips = ${trustedIPs}`) - const kongConfigTmpFile = '/tmp/kong.conf' - writeFileSync(kongConfigTmpFile, kongConfig) - const configMapExists = await outputIncludes( - `kubectl get cm -n ${kongNamespace} kong-config || true`, - 'kong-config' - ) - if (configMapExists) { - await execCmdWithExitOnFailure( - `kubectl create cm kong-config -n ${kongNamespace} --from-file ${kongConfigTmpFile} -o yaml --dry-run | kubectl replace -f -` - ) - } else { - await execCmdWithExitOnFailure( - `kubectl create cm kong-config -n ${kongNamespace} --from-file ${kongConfigTmpFile}` - ) - } -} diff --git a/packages/celotool/src/lib/mock-oracle.ts b/packages/celotool/src/lib/mock-oracle.ts index 83fefa45736..778bb206cf6 100644 --- a/packages/celotool/src/lib/mock-oracle.ts +++ b/packages/celotool/src/lib/mock-oracle.ts @@ -1,7 +1,6 @@ -import { envVar, fetchEnv, isVmBased } from 'src/lib/env-utils' +import { envVar, fetchEnv } from 'src/lib/env-utils' import { getPrivateTxNodeClusterIP } from 'src/lib/geth' import { installGenericHelmChart, removeGenericHelmChart } from 'src/lib/helm_deploy' -import { getInternalTxNodeLoadBalancerIP } from 'src/lib/vm-testnet-utils' const helmChartPath = '../helm-charts/mock-oracle' @@ -18,9 +17,7 @@ export async function removeHelmRelease(celoEnv: string) { } async function helmParameters(celoEnv: string) { - const nodeIp = isVmBased() - ? await getInternalTxNodeLoadBalancerIP(celoEnv) - : await getPrivateTxNodeClusterIP(celoEnv) + const nodeIp = await getPrivateTxNodeClusterIP(celoEnv) const nodeUrl = `http://${nodeIp}:8545` return [ `--set celotool.image.repository=${fetchEnv(envVar.CELOTOOL_DOCKER_IMAGE_REPOSITORY)}`, diff --git a/packages/celotool/src/lib/odis.ts b/packages/celotool/src/lib/odis.ts index 6bed913c055..8a27e704738 100644 --- a/packages/celotool/src/lib/odis.ts +++ b/packages/celotool/src/lib/odis.ts @@ -169,7 +169,6 @@ async function helmParameters(celoEnv: string, context: string) { `--set keystore.pnpKeyLatestVersion=${keyVaultConfig.pnpKeyLatestVersion}`, `--set keystore.domainsKeyLatestVersion=${keyVaultConfig.domainsKeyLatestVersion}`, `--set api.pnpAPIEnabled=${fetchEnv(envVar.ODIS_SIGNER_PNP_API_ENABLED)}`, - `--set api.legacyPnpAPIEnabled=${fetchEnv(envVar.ODIS_SIGNER_LEGACY_PNP_API_ENABLED)}`, `--set api.domainsAPIEnabled=${fetchEnv(envVar.ODIS_SIGNER_DOMAINS_API_ENABLED)}`, `--set blockchainProvider=${fetchEnv(envVar.ODIS_SIGNER_BLOCKCHAIN_PROVIDER)}`, `--set blockchainApiKey=${blockchainConfig.blockchainApiKey}`, diff --git a/packages/celotool/src/lib/oracle.ts b/packages/celotool/src/lib/oracle.ts index dfac13dcd77..1b8d20e9abd 100644 --- a/packages/celotool/src/lib/oracle.ts +++ b/packages/celotool/src/lib/oracle.ts @@ -4,18 +4,12 @@ import yargs from 'yargs' import { getCloudProviderFromContext, getDynamicEnvVarValues } from './context-utils' import { getOraclePrivateKeysFor, privateKeyToAddress } from './generate_utils' import { AksClusterConfig } from './k8s-cluster/aks' -import { AwsClusterConfig } from './k8s-cluster/aws' import { BaseClusterManager, CloudProvider } from './k8s-cluster/base' import { AksHsmOracleDeployer, AksHsmOracleDeploymentConfig, AksHsmOracleIdentity, } from './k8s-oracle/aks-hsm' -import { - AwsHsmOracleDeployer, - AwsHsmOracleDeploymentConfig, - AwsHsmOracleIdentity, -} from './k8s-oracle/aws-hsm' import { BaseOracleDeployer, CurrencyPair } from './k8s-oracle/base' import { PrivateKeyOracleDeployer, @@ -36,7 +30,6 @@ const hsmOracleDeployerGetterByCloudProvider: { clusterManager: BaseClusterManager ) => BaseOracleDeployer } = { - [CloudProvider.AWS]: getAwsHsmOracleDeployer, [CloudProvider.AZURE]: getAksHsmOracleDeployer, } @@ -170,89 +163,12 @@ const aksHsmOracleIdentityConfigDynamicEnvVars: { addressKeyVaults: DynamicEnvVar.ORACLE_ADDRESS_AZURE_KEY_VAULTS, } -/** - * ----------- AwsHsmOracleDeployer helpers ----------- - */ - -/** - * Gets an AwsHsmOracleDeployer by looking at env var values - */ -function getAwsHsmOracleDeployer( - celoEnv: string, - context: string, - currencyPair: CurrencyPair, - useForno: boolean, - clusterManager: BaseClusterManager -) { - const { addressKeyAliases } = getDynamicEnvVarValues( - awsHsmOracleIdentityConfigDynamicEnvVars, - { context, currencyPair }, - { - addressKeyAliases: '', - } - ) - - const identities = getAwsHsmOracleIdentities(addressKeyAliases, currencyPair) - const deploymentConfig: AwsHsmOracleDeploymentConfig = { - context, - clusterConfig: clusterManager.clusterConfig as AwsClusterConfig, - currencyPair, - identities, - useForno, - } - return new AwsHsmOracleDeployer(deploymentConfig, celoEnv) -} - -/** - * Given a string addressKeyAliases containing comma separated info of the form: - *
:: - * eg: 0x0000000000000000000000000000000000000000:keyAlias0,0x0000000000000000000000000000000000000001:keyAlias1:region1 - * returns an array of AwsHsmOracleIdentity in the same order - */ -export function getAwsHsmOracleIdentities( - addressKeyAliases: string, - currencyPair: CurrencyPair -): AwsHsmOracleIdentity[] { - const identityStrings = addressKeyAliases.split(',') - const identities = [] - for (const identityStr of identityStrings) { - const [address, keyAlias, region] = identityStr.split(':') - // region can be undefined - if (!address || !keyAlias) { - throw Error(`Address or key alias is invalid. Address: ${address} Key Alias: ${keyAlias}`) - } - identities.push({ - address, - currencyPair, - keyAlias, - region, - }) - } - return identities -} - -/** - * Config values pulled from env vars used for generating an AwsHsmOracleIdentity - */ -interface AwsHsmOracleIdentityConfig { - addressKeyAliases: string -} - -/** - * Env vars corresponding to each value for the AwsHsmOracleIdentityConfig for a particular context - */ -const awsHsmOracleIdentityConfigDynamicEnvVars: { - [k in keyof AwsHsmOracleIdentityConfig]: DynamicEnvVar -} = { - addressKeyAliases: DynamicEnvVar.ORACLE_ADDRESS_AWS_KEY_ALIASES, -} - /** * ----------- PrivateKeyOracleDeployer helpers ----------- */ /** - * Gets an AwsHsmOracleDeployer by looking at env var values and generating private keys + * Gets an PrivateKeyOracleDeployer by looking at env var values and generating private keys * from the mnemonic */ function getPrivateKeyOracleDeployer( @@ -303,7 +219,19 @@ const mnemonicBasedOracleIdentityConfigDynamicEnvVars: { */ export function addCurrencyPairMiddleware(argv: yargs.Argv) { return argv.option('currencyPair', { - choices: ['CELOUSD', 'CELOEUR', 'CELOBRL', 'USDCUSD', 'USDCEUR', 'USDCBRL'], + choices: [ + 'CELOUSD', + 'CELOEUR', + 'CELOBRL', + 'USDCUSD', + 'USDCEUR', + 'USDCBRL', + 'CELOXOF', + 'XOFEUR', + 'EUROCEUR', + 'EURXOF', + 'EUROCXOF', + ], description: 'Oracle deployment to target based on currency pair', demandOption: true, type: 'string', diff --git a/packages/celotool/src/lib/port_forward.ts b/packages/celotool/src/lib/port_forward.ts index a0aac538557..297a326fab0 100644 --- a/packages/celotool/src/lib/port_forward.ts +++ b/packages/celotool/src/lib/port_forward.ts @@ -1,7 +1,6 @@ /* tslint:disable: no-console */ import { ChildProcess, spawnSync } from 'child_process' import { execBackgroundCmd, execCmd } from './cmd-utils' -import { envVar, fetchEnv, isVmBased } from './env-utils' function sleep(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)) @@ -13,19 +12,7 @@ const PORT_CONTROL_CMD = 'nc -z 127.0.0.1 8545' const DEFAULT_COMPONENT = 'validators' async function getPortForwardCmd(celoEnv: string, component?: string, ports = defaultPortsString) { - if (isVmBased()) { - return Promise.resolve(getVmPortForwardCmd(celoEnv, component, ports)) - } else { - return getKubernetesPortForwardCmd(celoEnv, component, ports) - } -} - -function getVmPortForwardCmd(celoEnv: string, machine = 'validator-0', ports = defaultPortsString) { - const zone = fetchEnv(envVar.KUBERNETES_CLUSTER_ZONE) - // this command expects port mappings to be of the form `[localPort]:localhost:[remotePort]` - const portMappings = ports.replace(/:/g, ':localhost:').split(' ') - const portsWithFlags = portMappings.map((mapping) => `-L ${mapping}`).join(' ') - return `gcloud compute ssh --zone ${zone} ${celoEnv}-${machine} -- -N ${portsWithFlags}` + return getKubernetesPortForwardCmd(celoEnv, component, ports) } async function getKubernetesPortForwardCmd( diff --git a/packages/celotool/src/lib/prometheus.ts b/packages/celotool/src/lib/prometheus.ts index 0d3462a8225..704b6caa2e3 100644 --- a/packages/celotool/src/lib/prometheus.ts +++ b/packages/celotool/src/lib/prometheus.ts @@ -290,7 +290,6 @@ async function createPrometheusGcloudServiceAccount( function getCloudProviderPrefix(clusterConfig: BaseClusterConfig) { const prefixByCloudProvider: { [key in CloudProvider]: string } = { - [CloudProvider.AWS]: 'aws', [CloudProvider.AZURE]: 'aks', [CloudProvider.GCP]: 'gcp', } diff --git a/packages/celotool/src/lib/promtail.ts b/packages/celotool/src/lib/promtail.ts index 2101b2a924c..3a4144684ff 100644 --- a/packages/celotool/src/lib/promtail.ts +++ b/packages/celotool/src/lib/promtail.ts @@ -91,11 +91,6 @@ async function helmParameters(clusterConfig?: BaseClusterConfig) { `--set extraArgs[0]='-client.external-labels=cluster_name=${clusterConfig?.clusterName}'` ) break - - case CloudProvider.AWS: - default: - console.error(`Unrecognised or unsupported cloud provider: ${cloudProvider}`) - process.exit(1) } const user = fetchEnv(envVar.LOKI_USERNAME) diff --git a/packages/celotool/src/lib/terraform.ts b/packages/celotool/src/lib/terraform.ts deleted file mode 100644 index 57c90fdfc9c..00000000000 --- a/packages/celotool/src/lib/terraform.ts +++ /dev/null @@ -1,206 +0,0 @@ -import fs from 'fs' -import path from 'path' -import sleep from 'sleep-promise' -import { execCmd } from './cmd-utils' - -const terraformModulesPath = path.join(__dirname, '../../../terraform-modules') - -export interface TerraformVars { - [key: string]: string -} - -// Terraform requires the `backend-config` options to configure a remote backend -// with dynamic values. Sends stdout to /dev/null. -export async function initTerraformModule( - moduleName: string, - vars: TerraformVars, - backendConfigVars: TerraformVars -) { - const modulePath = getModulePath(moduleName) - return buildAndExecTerraformCmd( - 'init', - modulePath, - modulePath, - getVarOptions(vars), - getVarOptions(backendConfigVars, 'backend-config'), - '-reconfigure', - '> /dev/null' - ) -} - -export function planTerraformModule( - moduleName: string, - vars: TerraformVars, - destroy: boolean = false -) { - const planPath = getPlanPath(moduleName) - // Terraform requires an out directory to exist - const planDir = path.dirname(planPath) - if (!fs.existsSync(planDir)) { - fs.mkdirSync(planDir) - } - const modulePath = getModulePath(moduleName) - return buildAndExecTerraformCmd( - 'plan', - modulePath, - modulePath, - `-out=${planPath}`, - getVarOptions(vars), - destroy ? '-destroy' : '' - ) -} - -export function applyTerraformModule(moduleName: string) { - return buildAndExecTerraformCmd('apply', getModulePath(moduleName), getPlanPath(moduleName)) -} - -export function destroyTerraformModule(moduleName: string, vars: TerraformVars) { - return buildAndExecTerraformCmd( - 'destroy', - getModulePath(moduleName), - getVarOptions(vars), - '-force' - ) -} - -// Taints a resource or multiple resources with the same prefix if the resource name -// ends with '.*' -export function taintTerraformModuleResource(moduleName: string, resourceName: string) { - if (resourceName.endsWith('.*')) { - return taintEveryResourceWithPrefix(moduleName, resourceName.replace('.*', '')) - } else { - return taintResource(moduleName, resourceName) - } -} - -// Untaints a resource or multiple resources with the same prefix if the resource name -// ends with '.*' -export function untaintTerraformModuleResource(moduleName: string, resourceName: string) { - if (resourceName.endsWith('.*')) { - return untaintEveryResourceWithPrefix(moduleName, resourceName.replace('.*', '')) - } else { - return untaintResource(moduleName, resourceName) - } -} - -async function taintEveryResourceWithPrefix(moduleName: string, resourceName: string) { - const matches = await getEveryResourceWithPrefix(moduleName, resourceName) - for (const match of matches) { - await taintResource(moduleName, match) - // To avoid hitting rate limits - await sleep(100) - } -} - -async function untaintEveryResourceWithPrefix(moduleName: string, resourceName: string) { - const matches = await getEveryResourceWithPrefix(moduleName, resourceName) - for (const match of matches) { - await untaintResource(moduleName, match) - // To avoid hitting rate limits - await sleep(100) - } -} - -async function getEveryResourceWithPrefix(moduleName: string, resourcePrefix: string) { - const resources = await getTerraformModuleResourceNames(moduleName) - return resources.filter((resource: string) => resource.startsWith(resourcePrefix)) -} - -// Allow failures -function taintResource(moduleName: string, resourceName: string) { - try { - // escape quotes - const escapedResourceName = resourceName.replace(/"/g, '\\"') - return execTerraformCmd( - `terraform taint ${escapedResourceName}`, - getModulePath(moduleName), - false - ) - } catch (e) { - console.info(`Could not taint ${resourceName}`, e) - return Promise.resolve() - } -} - -// Allow failures -function untaintResource(moduleName: string, resourceName: string) { - try { - // escape quotes - const escapedResourceName = resourceName.replace(/"/g, '\\"') - return execTerraformCmd( - `terraform untaint ${escapedResourceName}`, - getModulePath(moduleName), - false - ) - } catch (e) { - console.info(`Could not taint ${resourceName}`, e) - return Promise.resolve() - } -} - -// pulls remote state -function refreshTerraformModule(moduleName: string, vars: TerraformVars) { - return buildAndExecTerraformCmd('refresh', getModulePath(moduleName), getVarOptions(vars)) -} - -export async function getTerraformModuleOutputs(moduleName: string, vars: TerraformVars) { - await refreshTerraformModule(moduleName, vars) - const modulePath = getModulePath(moduleName) - const [output] = await execCmd(`cd ${modulePath} && terraform output -json`) - return JSON.parse(output) -} - -// returns an array of resource and data names in the current state -export async function getTerraformModuleResourceNames(moduleName: string) { - const [output] = await execTerraformCmd(`terraform state list`, getModulePath(moduleName), false) - return output.split('\n') -} - -export function showTerraformModulePlan(moduleName: string) { - return execTerraformCmd( - `terraform show ${getPlanPath(moduleName)}`, - getModulePath(moduleName), - true - ) -} - -function getModulePath(moduleName: string) { - return path.join(terraformModulesPath, moduleName) -} - -function getPlanPath(moduleName: string) { - return path.join(terraformModulesPath, 'plan', moduleName) -} - -// Uses a TerraformVars object to generate command line var options for Terraform -function getVarOptions(vars: TerraformVars, optionName: string = 'var') { - const nameValuePairs = Object.keys(vars).map( - (varName) => `-${optionName}='${varName}=${vars[varName]}'` - ) - return nameValuePairs.join(' ') -} - -function execTerraformCmd(command: string, modulePath: string, pipeOutput: boolean) { - // use the middle two default arguments - return execCmd(`cd ${modulePath} && ${command}`, {}, false, pipeOutput) -} - -// `modulePath` is the path to the module that will be cd'd into. We change -// directories for each module so that module-specific configurations -// that are stored in the local .terraform directories do not conflict. -// `cmdPath` is the path to be provided to the terraform command -function buildAndExecTerraformCmd( - commandName: string, - modulePath: string, - cmdPath: string, - ...options: string[] -) { - const terraformCmd = buildTerraformCmd(commandName, cmdPath, ...options) - return execTerraformCmd(terraformCmd, modulePath, true) -} - -function buildTerraformCmd(command: string, cmdPath: string, ...options: string[]) { - const optionsStr = options ? options.join(' ') : '' - const applyPlanDirStr = command === 'apply' ? cmdPath : '' - return `terraform ${command} -input=false ${optionsStr} ${applyPlanDirStr}` -} diff --git a/packages/celotool/src/lib/transaction-metrics-exporter.ts b/packages/celotool/src/lib/transaction-metrics-exporter.ts index 7ca1356011b..4b79a2325eb 100644 --- a/packages/celotool/src/lib/transaction-metrics-exporter.ts +++ b/packages/celotool/src/lib/transaction-metrics-exporter.ts @@ -1,10 +1,9 @@ -import { envVar, fetchEnv, fetchEnvOrFallback, isVmBased } from 'src/lib/env-utils' +import { envVar, fetchEnv, fetchEnvOrFallback } from 'src/lib/env-utils' import { installGenericHelmChart, removeGenericHelmChart, upgradeGenericHelmChart, } from 'src/lib/helm_deploy' -import { getInternalTxNodeLoadBalancerIP } from 'src/lib/vm-testnet-utils' const chartDir = '../helm-charts/transaction-metrics-exporter/' @@ -58,8 +57,5 @@ async function helmParameters(celoEnv: string) { '' )}`, ] - if (isVmBased()) { - params.push(`--set web3Provider="ws://${await getInternalTxNodeLoadBalancerIP(celoEnv)}:8546"`) - } return params } diff --git a/packages/celotool/src/lib/utils.ts b/packages/celotool/src/lib/utils.ts index 3db31d6a569..40e4bf74beb 100644 --- a/packages/celotool/src/lib/utils.ts +++ b/packages/celotool/src/lib/utils.ts @@ -2,9 +2,8 @@ import sleep from 'sleep-promise' import yargs from 'yargs' import { switchToClusterFromEnv } from './cluster' import { execCmdWithExitOnFailure } from './cmd-utils' -import { envVar, fetchEnv, isVmBased } from './env-utils' +import { envVar, fetchEnv } from './env-utils' import { retrieveIPAddress } from './helm_deploy' -import { getTestnetOutputs } from './vm-testnet-utils' export async function outputIncludes(cmd: string, matchString: string, matchMessage?: string) { const [stdout] = await execCmdWithExitOnFailure(cmd) @@ -18,12 +17,7 @@ export async function outputIncludes(cmd: string, matchString: string, matchMess } export async function retrieveTxNodeIpAddress(celoEnv: string, txNodeIndex: number) { - if (isVmBased()) { - const outputs = await getTestnetOutputs(celoEnv) - return outputs.tx_node_ip_addresses.value[txNodeIndex] - } else { - return retrieveIPAddress(`${celoEnv}-tx-nodes-${txNodeIndex}`) - } + return retrieveIPAddress(`${celoEnv}-tx-nodes-${txNodeIndex}`) } export async function getVerificationPoolConfig(celoEnv: string) { diff --git a/packages/celotool/src/lib/vm-testnet-utils.ts b/packages/celotool/src/lib/vm-testnet-utils.ts deleted file mode 100644 index 935181b784c..00000000000 --- a/packages/celotool/src/lib/vm-testnet-utils.ts +++ /dev/null @@ -1,559 +0,0 @@ -import sleep from 'sleep-promise' -import { execCmd } from './cmd-utils' -import { confirmAction, envVar, fetchEnv, fetchEnvOrFallback } from './env-utils' -import { - AccountType, - generateGenesisFromEnv, - generatePrivateKey, - generatePublicKey, - getAddressFromEnv, - privateKeyToAddress, - privateKeyToPublicKey, -} from './generate_utils' -import { - applyTerraformModule, - destroyTerraformModule, - getTerraformModuleOutputs, - initTerraformModule, - planTerraformModule, - showTerraformModulePlan, - taintTerraformModuleResource, - TerraformVars, - untaintTerraformModuleResource, -} from './terraform' -import { - getGenesisBlockFromGoogleStorage, - getProxiesPerValidator, - getProxyName, - uploadDataToGoogleStorage, - uploadGenesisBlockToGoogleStorage, - uploadTestnetInfoToGoogleStorage, -} from './testnet-utils' - -export interface ProxyIndex { - validatorIndex: number - proxyIndex: number -} - -// Keys = gcloud project name -const projectConfig = { - 'celo-testnet': { - secretsBucketName: 'celo-testnet-secrets', - stateBucketName: 'celo_tf_state', - }, - 'celo-testnet-production': { - secretsBucketName: 'celo-testnet-secrets-prod', - stateBucketName: 'celo_tf_state_prod', - }, -} - -const testnetTerraformModule = 'testnet' -const testnetNetworkTerraformModule = 'testnet-network' - -interface NodeSecrets { - ACCOUNT_ADDRESS: string - BOOTNODE_ENODE_ADDRESS: string - PRIVATE_KEY: string - PROXIED_VALIDATOR_ADDRESS?: string - PROXY_ENODE_ADDRESSES?: string - [envVar.GETH_ACCOUNT_SECRET]: string - [envVar.MNEMONIC]: string -} - -// The keys correspond to the variable names that Terraform expects and -// the values correspond to the names of the appropriate env variables -const testnetEnvVars: TerraformVars = { - block_time: envVar.BLOCK_TIME, - celo_env: envVar.CELOTOOL_CELOENV, - gcloud_credentials_path: envVar.GOOGLE_APPLICATION_CREDENTIALS, - gcloud_project: envVar.TESTNET_PROJECT_NAME, - geth_verbosity: envVar.GETH_VERBOSITY, - geth_bootnode_docker_image_repository: envVar.GETH_BOOTNODE_DOCKER_IMAGE_REPOSITORY, - geth_bootnode_docker_image_tag: envVar.GETH_BOOTNODE_DOCKER_IMAGE_TAG, - geth_metrics: envVar.GETH_ENABLE_METRICS, - geth_node_docker_image_repository: envVar.GETH_NODE_DOCKER_IMAGE_REPOSITORY, - geth_node_docker_image_tag: envVar.GETH_NODE_DOCKER_IMAGE_TAG, - in_memory_discovery_table: envVar.IN_MEMORY_DISCOVERY_TABLE, - istanbul_request_timeout_ms: envVar.ISTANBUL_REQUEST_TIMEOUT_MS, - network_id: envVar.NETWORK_ID, - private_tx_node_count: envVar.PRIVATE_TX_NODES, - node_disk_size_gb: envVar.NODE_DISK_SIZE_GB, - private_node_disk_size_gb: envVar.PRIVATE_NODE_DISK_SIZE_GB, - tx_node_count: envVar.TX_NODES, - validator_count: envVar.VALIDATORS, -} - -const testnetNetworkEnvVars: TerraformVars = { - celo_env: envVar.CELOTOOL_CELOENV, - gcloud_credentials_path: envVar.GOOGLE_APPLICATION_CREDENTIALS, - gcloud_project: envVar.TESTNET_PROJECT_NAME, -} - -// Resources that are tainted when upgrade-resetting -const testnetResourcesToReset = [ - // bootnode - 'module.bootnode.google_compute_instance.bootnode', - // validators - 'module.validator.google_compute_instance.validator.*', - 'module.validator.google_compute_disk.validator.*', - // validator proxies - 'module.validator.module.proxy.random_id.full_node.*', - 'module.validator.module.proxy.google_compute_instance.full_node.*', - 'module.validator.module.proxy.random_id.full_node_disk.*', - 'module.validator.module.proxy.google_compute_disk.full_node.*', - // tx-nodes - 'module.tx_node.random_id.full_node.*', - 'module.tx_node.google_compute_instance.full_node.*', - 'module.tx_node.random_id.full_node_disk.*', - 'module.tx_node.google_compute_disk.full_node.*', - // private tx-nodes - 'module.tx_node_private.random_id.full_node.*', - 'module.tx_node_private.google_compute_instance.full_node.*', - 'module.tx_node_private.random_id.full_node_disk.*', - 'module.tx_node_private.google_compute_disk.full_node.*', - // tx-node load balancer instance group - 'module.tx_node_lb.random_id.external', - 'module.tx_node_lb.google_compute_instance_group.external', - 'module.tx_node_lb.random_id.internal', - 'module.tx_node_lb.google_compute_instance_group.internal', -] - -export async function deploy( - celoEnv: string, - generateSecrets: boolean, - useExistingGenesis: boolean, - onConfirmFailed?: () => Promise -) { - // If we are not using the default network, we want to create/upgrade our network - if (!useDefaultNetwork()) { - console.info('First deploying the testnet VPC network') - - const networkVars: TerraformVars = getTestnetNetworkVars(celoEnv) - await deployModule(celoEnv, testnetNetworkTerraformModule, networkVars, onConfirmFailed) - } - - const testnetVars: TerraformVars = await getTestnetVars(celoEnv, useExistingGenesis) - await deployModule(celoEnv, testnetTerraformModule, testnetVars, onConfirmFailed, async () => { - if (generateSecrets) { - console.info('Generating and uploading secrets env files to Google Storage...') - await generateAndUploadSecrets(celoEnv) - } - }) - await uploadTestnetInfoToGoogleStorage(celoEnv) -} - -export async function deployModule( - celoEnv: string, - terraformModule: string, - vars: TerraformVars, - onConfirmFailed?: () => Promise, - onConfirmSuccess?: () => Promise -) { - const backendConfigVars: TerraformVars = getTerraformBackendConfigVars(celoEnv, terraformModule) - - const envType = fetchEnv(envVar.ENV_TYPE) - console.info(` - Deploying: - Terraform Module: ${terraformModule} - Celo Env: ${celoEnv} - Environment: ${envType} - `) - - console.info('Initializing...') - await initTerraformModule(terraformModule, vars, backendConfigVars) - - console.info('Planning...') - await planTerraformModule(terraformModule, vars) - - // await showTerraformModulePlan(terraformModule) - - await confirmAction( - `Are you sure you want to perform the above plan for Celo env ${celoEnv} in environment ${envType}?`, - onConfirmFailed, - onConfirmSuccess - ) - - console.info('Applying...') - await applyTerraformModule(terraformModule) -} - -export async function destroy(celoEnv: string) { - const testnetVars: TerraformVars = await getTestnetVars(celoEnv, true) - - await destroyModule(celoEnv, testnetTerraformModule, testnetVars) - - // If we are not using the default network, we want to destroy our network - if (!useDefaultNetwork()) { - console.info('Destroying the testnet VPC network') - - const networkVars: TerraformVars = getTestnetNetworkVars(celoEnv) - await destroyModule(celoEnv, testnetNetworkTerraformModule, networkVars) - } -} - -export async function destroyModule( - celoEnv: string, - terraformModule: string, - vars: TerraformVars = {} -) { - const backendConfigVars: TerraformVars = getTerraformBackendConfigVars(celoEnv, terraformModule) - - const envType = fetchEnv(envVar.ENV_TYPE) - console.info(` - Destroying: - Terraform Module: ${terraformModule} - Celo Env: ${celoEnv} - Environment: ${envType} - `) - - console.info('Initializing...') - await initTerraformModule(terraformModule, vars, backendConfigVars) - - console.info('Planning...') - await planTerraformModule(terraformModule, vars, true) - - await showTerraformModulePlan(terraformModule) - - await confirmAction(`Are you sure you want to destroy ${celoEnv} in environment ${envType}?`) - - await destroyTerraformModule(terraformModule, vars) -} - -// force the recreation of various resources upon the next deployment -export async function taintTestnet(celoEnv: string) { - console.info('Tainting testnet...') - const vars: TerraformVars = await getTestnetVars(celoEnv, true) - const backendConfigVars: TerraformVars = getTerraformBackendConfigVars( - celoEnv, - testnetTerraformModule - ) - await initTerraformModule(testnetTerraformModule, vars, backendConfigVars) - - for (const resource of testnetResourcesToReset) { - console.info(`Tainting ${resource}`) - await taintTerraformModuleResource(testnetTerraformModule, resource) - // To avoid getting errors for too many gcloud storage API requests - await sleep(2000) - } -} - -export async function untaintTestnet(celoEnv: string) { - console.info('Untainting testnet...') - const vars: TerraformVars = await getTestnetVars(celoEnv, true) - const backendConfigVars: TerraformVars = getTerraformBackendConfigVars( - celoEnv, - testnetTerraformModule - ) - await initTerraformModule(testnetTerraformModule, vars, backendConfigVars) - - for (const resource of testnetResourcesToReset) { - console.info(`Untainting ${resource}`) - await untaintTerraformModuleResource(testnetTerraformModule, resource) - // To avoid getting errors for too many gcloud storage API requests - await sleep(2000) - } -} - -export async function getTestnetOutputs(celoEnv: string) { - const vars: TerraformVars = await getTestnetVars(celoEnv, true) - const backendConfigVars: TerraformVars = getTerraformBackendConfigVars( - celoEnv, - testnetTerraformModule - ) - await initTerraformModule(testnetTerraformModule, vars, backendConfigVars) - return getTerraformModuleOutputs(testnetTerraformModule, vars) -} - -export async function getInternalTxNodeLoadBalancerIP(celoEnv: string) { - const fullCmd = getInternalTxNodeLoadBalancerIpCommand(celoEnv) - const [output] = await execCmd(fullCmd) - return output.trim() -} - -export async function getInternalValidatorIPs(celoEnv: string) { - const outputs = await getTestnetOutputs(celoEnv) - return outputs.validator_internal_ip_addresses.value -} - -export async function getInternalProxyIPs(celoEnv: string) { - const outputs = await getTestnetOutputs(celoEnv) - return outputs.proxy_internal_ip_addresses.value -} - -export async function getInternalTxNodeIPs(celoEnv: string) { - const outputs = await getTestnetOutputs(celoEnv) - return outputs.tx_node_internal_ip_addresses.value -} - -export function getTerraformBackendConfigVars(celoEnv: string, terraformModule: string) { - return { - bucket: stateBucketName(), - prefix: `${celoEnv}/${terraformModule}`, - } -} - -async function getTestnetVars(celoEnv: string, useExistingGenesis: boolean) { - let genesisContent: string = '' - if (useExistingGenesis) { - genesisContent = await getGenesisBlockFromGoogleStorage(celoEnv) - } else { - generateGenesisFromEnv() - await uploadGenesisBlockToGoogleStorage(genesisContent, celoEnv) - } - - const genesisBuffer = Buffer.from(genesisContent) - const domainName = fetchEnv(envVar.CLUSTER_DOMAIN_NAME) - return { - ...getEnvVarValues(testnetEnvVars), - // Cloud DNS for our domains only lives in celo-testnet - dns_gcloud_project: 'celo-testnet', - dns_zone_name: dnsZoneName(domainName), - ethstats_host: `${celoEnv}-ethstats.${domainName}.org`, - forno_host: `${celoEnv}-forno.${domainName}.org`, - gcloud_secrets_bucket: secretsBucketName(), - gcloud_secrets_base_path: secretsBasePath(celoEnv), - // only able to view objects for accessing secrets & modify ssl certs for forno setup - gcloud_vm_service_account_email: `terraform-testnet@${fetchEnv( - envVar.TESTNET_PROJECT_NAME - )}.iam.gserviceaccount.com`, - genesis_content_base64: genesisBuffer.toString('base64'), - // forno is the name for our setup that has tx-nodes reachable via a domain name - letsencrypt_email: 'n@celo.org', - network_name: networkName(celoEnv), - proxies_per_validator: JSON.stringify(getProxiesPerValidator()), - } -} - -function getTestnetNetworkVars(celoEnv: string): TerraformVars { - return { - ...getEnvVarValues(testnetNetworkEnvVars), - network_name: networkName(celoEnv), - } -} - -function getEnvVarValues(terraformEnvVars: TerraformVars) { - const vars: { [key: string]: string } = {} - for (const key of Object.keys(terraformEnvVars)) { - vars[key] = fetchEnv(terraformEnvVars[key]) - } - return vars -} - -export async function generateAndUploadSecrets(celoEnv: string) { - // Bootnode - const bootnodeSecrets = generateBootnodeSecretEnvVars() - await uploadSecrets(celoEnv, bootnodeSecrets, 'bootnode') - // Tx Nodes - const txNodeCount = parseInt(fetchEnv(envVar.TX_NODES), 10) - for (let i = 0; i < txNodeCount; i++) { - const secrets = generateNodeSecretEnvVars(AccountType.TX_NODE, i) - await uploadSecrets(celoEnv, secrets, `tx-node-${i}`) - } - // Private tx Nodes - const privateTxNodeCount = parseInt(fetchEnv(envVar.PRIVATE_TX_NODES), 10) - for (let i = 0; i < privateTxNodeCount; i++) { - // Ensure there is no overlap with tx node keys - const secrets = generateNodeSecretEnvVars(AccountType.TX_NODE, i, 1000 + i) - await uploadSecrets(celoEnv, secrets, `tx-node-private-${i}`) - } - // Validators - const validatorCount = parseInt(fetchEnv(envVar.VALIDATORS), 10) - for (let i = 0; i < validatorCount; i++) { - const secrets = generateNodeSecretEnvVars(AccountType.VALIDATOR, i) - await uploadSecrets(celoEnv, secrets, `validator-${i}`) - } - // Proxies - const proxiesPerValidator = getProxiesPerValidator() - let validatorIndex = 0 - for (const proxyCount of proxiesPerValidator) { - for (let i = 0; i < proxyCount; i++) { - const secrets = generateProxySecretEnvVars(validatorIndex, i) - await uploadSecrets(celoEnv, secrets, `validator-${validatorIndex}-proxy-${i}`) - } - validatorIndex++ - } -} - -function uploadSecrets(celoEnv: string, secrets: string, resourceName: string) { - const cloudStorageFileName = `${secretsBasePath(celoEnv)}/.env.${resourceName}` - return uploadDataToGoogleStorage( - secrets, - secretsBucketName(), - cloudStorageFileName, - false, - 'text/plain' - ) -} - -function generateBootnodeSecretEnvVars() { - const mnemonic = fetchEnv(envVar.MNEMONIC) - return formatEnvVars({ - NODE_KEY: generatePrivateKey(mnemonic, AccountType.BOOTNODE, 0), - }) -} - -function generateNodeSecretEnvVars( - accountType: AccountType, - index: number, - keyIndex: number = index -) { - const mnemonic = fetchEnv(envVar.MNEMONIC) - const privateKey = generatePrivateKey(mnemonic, accountType, keyIndex) - const secrets = getNodeSecrets(privateKey) - // If this is meant to be a proxied validator, also generate the enode of its proxy - if (accountType === AccountType.VALIDATOR) { - const proxiesPerValidator = getProxiesPerValidator() - if (index < proxiesPerValidator.length) { - const proxyEnodeAddresses = [] - for (let proxyIndex = 0; proxyIndex < proxiesPerValidator[index]; proxyIndex++) { - proxyEnodeAddresses.push(privateKeyToPublicKey(generateProxyPrivateKey(index, proxyIndex))) - } - secrets.PROXY_ENODE_ADDRESSES = proxyEnodeAddresses.join(',') - } - } - return formatEnvVars(secrets) -} - -function generateProxySecretEnvVars(validatorIndex: number, proxyIndex: number) { - const privateKey = generateProxyPrivateKey(validatorIndex, proxyIndex) - const secrets = getNodeSecrets(privateKey) - secrets.PROXIED_VALIDATOR_ADDRESS = getAddressFromEnv(AccountType.VALIDATOR, validatorIndex) - return formatEnvVars(secrets) -} - -function generateProxyPrivateKey(validatorIndex: number, proxyIndex: number) { - const mnemonic = fetchEnv(envVar.MNEMONIC) - // To allow a validator to have many proxies and to be able to easily - // adjust the number of proxies it has, the following index is calculated - const index = validatorIndex * 10000 + proxyIndex - return generatePrivateKey(mnemonic, AccountType.PROXY, index) -} - -function getNodeSecrets(privateKey: string): NodeSecrets { - const mnemonic = fetchEnv(envVar.MNEMONIC) - return { - ACCOUNT_ADDRESS: privateKeyToAddress(privateKey), - BOOTNODE_ENODE_ADDRESS: generatePublicKey(mnemonic, AccountType.BOOTNODE, 0), - PRIVATE_KEY: privateKey, - [envVar.GETH_ACCOUNT_SECRET]: fetchEnv(envVar.GETH_ACCOUNT_SECRET), - [envVar.MNEMONIC]: mnemonic, - } -} - -// Formats an object into a multi-line string with each line as KEY=VALUE -function formatEnvVars(envVars: { [key: string]: any }) { - return Object.keys(envVars) - .map((key) => `${key}='${envVars[key]}'`) - .join('\n') -} - -function secretsBasePath(celoEnv: string) { - return `vm/${celoEnv}` -} - -function useDefaultNetwork() { - return ( - fetchEnvOrFallback(envVar.VM_BASED, 'false') !== 'true' || - fetchEnv(envVar.KUBERNETES_CLUSTER_NAME) === 'celo-networks-dev' - ) -} - -export function networkName(celoEnv: string) { - return useDefaultNetwork() ? 'default' : `${celoEnv}-network` -} - -function secretsBucketName() { - const config = configForProject() - return config.secretsBucketName -} - -function stateBucketName() { - const config = configForProject() - return config.stateBucketName -} - -function configForProject() { - const project = fetchEnv(envVar.TESTNET_PROJECT_NAME) - if (!projectConfig.hasOwnProperty(project)) { - throw new Error(`No config for project ${project}`) - } - // @ts-ignore - we check above to see if the property exists - return projectConfig[project] -} - -// name of the DNS zone in Google Cloud for a particular domain -function dnsZoneName(domain: string) { - return `${domain}-org` -} - -export function getVmSshCommand(instanceName: string) { - const project = fetchEnv(envVar.TESTNET_PROJECT_NAME) - const zone = fetchEnv(envVar.KUBERNETES_CLUSTER_ZONE) - return `gcloud beta compute --project '${project}' ssh --zone '${zone}' ${instanceName} --tunnel-through-iap` -} - -export function getInternalTxNodeLoadBalancerIpCommand(celoEnv: string) { - const project = fetchEnv(envVar.TESTNET_PROJECT_NAME) - return `gcloud compute forwarding-rules list --project '${project}' --filter="name~'${celoEnv}-tx-node-lb-internal-fwd-rule'" --format='get(IPAddress)'` -} - -export async function getNodeVmName( - celoEnv: string, - nodeType: string, - index?: number | ProxyIndex -) { - const nodeTypesWithRandomSuffixes = ['tx-node', 'tx-node-private', 'proxy'] - const nodeTypesWithNoIndex = ['bootnode'] - let instanceName - if (nodeTypesWithRandomSuffixes.includes(nodeType)) { - instanceName = await getNodeVmNameWithRandomSuffix(celoEnv, nodeType, index || 0) - } else { - instanceName = `${celoEnv}-${nodeType}` - if (!nodeTypesWithNoIndex.includes(nodeType) && index !== undefined) { - instanceName += `-${index}` - } - } - return instanceName -} - -// Some VM names have a randomly generated suffix. This returns the full name -// of the instance given only the celoEnv and index. -async function getNodeVmNameWithRandomSuffix( - celoEnv: string, - nodeType: string, - index: number | ProxyIndex -) { - const project = fetchEnv(envVar.TESTNET_PROJECT_NAME) - - const baseName = - typeof index === 'number' - ? `${celoEnv}-${nodeType}-${index}` - : getProxyName(celoEnv, index.validatorIndex, index.proxyIndex) - - const [nodeName] = await execCmd( - `gcloud compute instances list --project '${project}' --filter="NAME ~ ${baseName}-.*" --format get\\(NAME\\)` - ) - return nodeName.trim() -} - -// indexCoercer is a yargs coercer that parses numeric indices and colon-separated -// indices (:) into a ProxyIndex type. -export function indexCoercer(value: string) { - if (!value) { - return value - } - const splitValues = value.split(':').filter((v) => v) - // Then it's just a single index number - if (splitValues.length === 1) { - return parseInt(value, 10) - } else if (splitValues.length === 2) { - const parsedValues = splitValues.map((v) => parseInt(v, 10)) - const proxyIndex: ProxyIndex = { - validatorIndex: parsedValues[0], - proxyIndex: parsedValues[1], - } - return proxyIndex - } else { - throw new Error('Incorrect index') - } -} diff --git a/packages/celotool/src/types.d.ts b/packages/celotool/src/types.d.ts index 8d14dd77d24..cb40e33c989 100644 --- a/packages/celotool/src/types.d.ts +++ b/packages/celotool/src/types.d.ts @@ -1,5 +1,8 @@ declare module 'web3-utils' declare module 'country-data' +declare module 'bip39' { + function mnemonicToSeedSync(mnemonic: string): Buffer +} declare module 'read-last-lines' { namespace readLastLines { function read(inputFilePath: string, maxLineCount: number, encoding?: string): Promise diff --git a/packages/celotool/tslint.json b/packages/celotool/tslint.json index b1e6a53d435..ae0e82b60fb 100644 --- a/packages/celotool/tslint.json +++ b/packages/celotool/tslint.json @@ -4,6 +4,12 @@ "exclude": ["**/__mocks__/**", "**/lcov-report/**"] }, "rules": { + "no-implicit-dependencies": [ + false, + [ + "src" + ] + ], "no-relative-imports": false, "max-classes-per-file": [true, 2], "no-global-arrow-functions": false, diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 8e0decd2c12..3d3a120348f 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,67 +1,146 @@ # Changelog -All notable changes to the [celo cli package](https://www.npmjs.com/package/@celo/celocli) will be documented in this file. -This package will follow the release process outlined [here](https://docs.celo.org/community/release-process). +## 3.0.2 + +### Patch Changes + +- d48c68afc: Speeds up governance:show command by parallelize async calls, and reducing data fetched +- cb7b4c538: Fixes type warnings +- Updated dependencies [d48c68afc] +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [d48c68afc] +- Updated dependencies [d48c68afc] +- Updated dependencies [d48c68afc] + - @celo/contractkit@5.1.0 + - @celo/connect@5.1.0 + - @celo/wallet-hsm-azure@5.1.0 + - @celo/wallet-ledger@5.1.0 + - @celo/wallet-local@5.1.0 + - @celo/cryptographic-utils@5.0.5 + - @celo/phone-utils@5.0.5 + - @celo/governance@5.0.5 + - @celo/explorer@5.0.5 + - @celo/utils@5.0.5 + - @celo/base@5.0.5 + +## 3.0.2-beta.1 + +### Patch Changes + +- cb7b4c538: Fixes type warnings + +## 3.0.2-beta.0 + +### Patch Changes + +- d48c68afc: Speeds up governance:show command by parallelize async calls, and reducing data fetched +- Updated dependencies [d48c68afc] +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [d48c68afc] +- Updated dependencies [d48c68afc] +- Updated dependencies [d48c68afc] + - @celo/contractkit@5.1.0-beta.0 + - @celo/connect@5.1.0-beta.0 + - @celo/wallet-hsm-azure@5.1.0-beta.0 + - @celo/wallet-ledger@5.1.0-beta.0 + - @celo/wallet-local@5.1.0-beta.0 + - @celo/cryptographic-utils@5.0.5-beta.0 + - @celo/phone-utils@5.0.5-beta.0 + - @celo/governance@5.0.5-beta.0 + - @celo/explorer@5.0.5-beta.0 + - @celo/utils@5.0.5-beta.0 + - @celo/base@5.0.5-beta.0 + All notable changes to the [celo cli package](https://www.npmjs.com/package/@celo/celocli) will be documented in this file. +This package will follow the release process outlined [here](https://docs.celo.org/community/release-process). ## Development (not published yet) + ### **[1.2.1--dev]** + Features + - [one-line summary] - [link PR] Bug Fixes + - [one-line summary] - [link PR] Other Changes + - [one-line summary] - [link PR] + ## Published ### **[1.2.0]** -- 2021-04-22 + Features + - cEUR support - [#7524](https://github.com/celo-org/celo-monorepo/pull/7524) - Add more info to network:contracts - [#7379](https://github.com/celo-org/celo-monorepo/pull/7379) - Approvehotfix to support multisigs - [#7671](https://github.com/celo-org/celo-monorepo/pull/7671) Other Changes + - Add --globalHelp option to BaseCommand - [#7669](https://github.com/celo-org/celo-monorepo/pull/7669) ### **[1.1.1--beta]** -- 2021-03-22 + Features + - Support Portuguese mnemonics - [#7220](https://github.com/celo-org/celo-monorepo/pull/7220) - Improve granularity of governance tooling information - [#6475](https://github.com/celo-org/celo-monorepo/pull/6475) - Small fixes in the proposal process for cEUR/Release 3 - [#7184](https://github.com/celo-org/celo-monorepo/pull/7184) - CIP8 name access via the CLI - [#6855](https://github.com/celo-org/celo-monorepo/pull/6855) Other Changes + - Upload/Download Profile Data with CIP8 - [#6604](https://github.com/celo-org/celo-monorepo/pull/6604) -- Improve naming in the DKG - [#4062](https://github.com/celo-org/celo-monorepo/pull/4062) +- Improve naming in the DKG - [#4062](https://github.com/celo-org/celo-monorepo/pull/4062) - Fix packages vulnerabilities - [#7476](https://github.com/celo-org/celo-monorepo/pull/7476) ### **[1.1.0]** -- 2021-02-16 + Features + - Add plugins to CLI - [#5973](https://github.com/celo-org/celo-monorepo/pull/5973) - New CLI command `identity:get-attestations` to query attestations - [#5974](https://github.com/celo-org/celo-monorepo/pull/5974) Bug Fixes + - `releasegold:show` should succeed w/o account registration - [#7092](https://github.com/celo-org/celo-monorepo/pull/7092) - Add check for signer or registered account in `releasegold:show` - [#7098](https://github.com/celo-org/celo-monorepo/pull/7098) Other Changes + - Clarify Docs for `multisig:transfer` - [#6982](https://github.com/celo-org/celo-monorepo/pull/6982) ### **[1.0.3]** -- 2021-01-25 + Bug Fixes + - Add missing lib in the `shrinkwrap.json` that avoids the usage of the package - [#6671](https://github.com/celo-org/celo-monorepo/pull/6671) ### **[1.0.2]** -- 2021-01-22 + Bug Fixes + - Fixed Global Flag Parsing in CLI - [#6619](https://github.com/celo-org/celo-monorepo/pull/6619) Other Changes + - Fix libraries versions (`shrinkwrap`) to avoid supply chain attacks - [#6575](https://github.com/celo-org/celo-monorepo/pull/6575) ### **[1.0.1]** -- 2021-01-20 + Features + - Pass through [oclif table flags](https://github.com/oclif/cli-ux#clitable) to commands which output tables - [#5618](https://github.com/celo-org/celo-monorepo/pull/5618) - CIP 8 Encryption - [#5091](https://github.com/celo-org/celo-monorepo/pull/5091) - Add authorized signers to release gold show - [#5596](https://github.com/celo-org/celo-monorepo/pull/5596) @@ -72,12 +151,14 @@ Features - Write transfer and transferFrom commands for MultiSig contract - [#6425](https://github.com/celo-org/celo-monorepo/pull/6425) Bug Fixes + - Fix param order on account:new internal call - [#6319](https://github.com/celo-org/celo-monorepo/pull/6319) - Remove broken header links in generated CLI docs - [#6415](https://github.com/celo-org/celo-monorepo/pull/6415) -- Fix @ledgerhq package version in CK and CLI - [#6496](https://github.com/celo-org/celo-monorepo/pull/6496) +- Fix @ledgerhq package version in CK and CLI - [#6496](https://github.com/celo-org/celo-monorepo/pull/6496) - Fix call to set gas currency in CLI base - [#6505](https://github.com/celo-org/celo-monorepo/pull/6505) Other Changes + - KomenciKit - [#5436](https://github.com/celo-org/celo-monorepo/pull/5436) - Update base and utils package versions [#5655](https://github.com/celo-org/celo-monorepo/pull/5655) - Parallelize and simplify fetching of comprensive registry address map - [#5568](https://github.com/celo-org/celo-monorepo/pull/5568) @@ -88,40 +169,51 @@ Other Changes - Add install instructions for CLI readme - [#6466](https://github.com/celo-org/celo-monorepo/pull/6466) ### **[0.0.60]** -- 2020-10-27 + Bug Fixes + - Uses the most up-to-date version of @celo/contractkit (0.4.17) & fixes backward compatibility issues from the last release - Actually call toString in oracle report CLI - [#5594](https://github.com/celo-org/celo-monorepo/pull/5594) Other Changes + - Support the use of scientific notation for the deposit of a governance proposal - [#5326](https://github.com/celo-org/celo-monorepo/pull/5326) - Require `--force` with `account:claim-attestation-service-url` for non-TLS urls [#5599](https://github.com/celo-org/celo-monorepo/pull/5599) ### **[0.0.59]** -- 2020-10-23 + Features -- Add `jsonTransactions` flag to `governance:show` for use in the (contract release process)[https://docs.celo.org/community/release-process/smart-contracts] - [#5111](https://github.com/celo-org/celo-monorepo/pull/5111) + +- Add `jsonTransactions` flag to `governance:show` for use in the (contract release process)[https://docs.celo.org/community/release-process/smart-contracts] - [#5111](https://github.com/celo-org/celo-monorepo/pull/5111) Bug Fixes + - Fix attestation service test delivering false negatives - [#5336](https://github.com/celo-org/celo-monorepo/pull/5336) - Fix error when listing contract addresses and include some missing new contracts - [#5301](https://github.com/celo-org/celo-monorepo/pull/5301) Other Changes -- Convert default log output color from red to yellow - [#5517](https://github.com/celo-org/celo-monorepo/pull/5517) +- Convert default log output color from red to yellow - [#5517](https://github.com/celo-org/celo-monorepo/pull/5517) ### **[0.0.58]** -- 2020-10-08 + Features + - CLI compatability with [Attestation Service 1.0.5](https://github.com/celo-org/celo-monorepo/releases/tag/attestation-service-1-0-5) - [#5011](https://github.com/celo-org/celo-monorepo/pull/5011) - Adds an interactive prompt for forming proposals from Celo registry contracts and functions - [#3008](https://github.com/celo-org/celo-monorepo/pull/3008) Other Changes + - Correct documentation on the validator and validator group deregister - [#5197](https://github.com/celo-org/celo-monorepo/pull/5197) ### **[0.0.57]** -- 2020-09-23 + Features + - Adds ODIS identifier query to celocli - [#4976](https://github.com/celo-org/celo-monorepo/pull/4976) Bug Fixes -- Fixes backward compatibility issues in cli - [#5124](https://github.com/celo-org/celo-monorepo/pull/5124) +- Fixes backward compatibility issues in cli - [#5124](https://github.com/celo-org/celo-monorepo/pull/5124) _Note: Changes before 0.0.57 are not documented_ diff --git a/packages/cli/jest.config.js b/packages/cli/jest.config.js index 0cce853d6f9..b985c7f3d5a 100644 --- a/packages/cli/jest.config.js +++ b/packages/cli/jest.config.js @@ -1,10 +1,7 @@ -const { nodeFlakeTracking } = require('@celo/flake-tracker/src/jest/config.js') - module.exports = { preset: 'ts-jest', - ...nodeFlakeTracking, testMatch: ['/src/**/?(*.)+(spec|test).ts?(x)'], - setupFilesAfterEnv: ['@celo/dev-utils/lib/matchers', ...nodeFlakeTracking.setupFilesAfterEnv], + setupFilesAfterEnv: ['@celo/dev-utils/lib/matchers'], globalSetup: '/src/test-utils/setup.global.ts', globalTeardown: '/src/test-utils/teardown.global.ts', } diff --git a/packages/cli/npm-shrinkwrap.json b/packages/cli/npm-shrinkwrap.json deleted file mode 100644 index ccfd34d41be..00000000000 --- a/packages/cli/npm-shrinkwrap.json +++ /dev/null @@ -1,7684 +0,0 @@ -{ - "name": "@celo/celocli", - "version": "2.1.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@azure/abort-controller": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", - "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", - "requires": { - "tslib": "^2.2.0" - }, - "dependencies": { - "tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - } - } - }, - "@azure/core-auth": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.4.0.tgz", - "integrity": "sha512-HFrcTgmuSuukRf/EdPmqBrc5l6Q5Uu+2TbuhaKbgaCpP2TfAeiNaQPAadxO+CYBRHGUzIDteMAjFspFLDLnKVQ==", - "requires": { - "@azure/abort-controller": "^1.0.0", - "tslib": "^2.2.0" - }, - "dependencies": { - "tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - } - } - }, - "@azure/core-client": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.7.2.tgz", - "integrity": "sha512-ye5554gnVnXdfZ64hptUtETgacXoRWxYv1JF5MctoAzTSH5dXhDPZd9gOjDPyWMcLIk58pnP5+p5vGX6PYn1ag==", - "requires": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-auth": "^1.4.0", - "@azure/core-rest-pipeline": "^1.9.1", - "@azure/core-tracing": "^1.0.0", - "@azure/core-util": "^1.0.0", - "@azure/logger": "^1.0.0", - "tslib": "^2.2.0" - }, - "dependencies": { - "@azure/core-tracing": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.1.tgz", - "integrity": "sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw==", - "requires": { - "tslib": "^2.2.0" - } - }, - "tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - } - } - }, - "@azure/core-http-compat": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-1.3.0.tgz", - "integrity": "sha512-ZN9avruqbQ5TxopzG3ih3KRy52n8OAbitX3fnZT5go4hzu0J+KVPSzkL+Wt3hpJpdG8WIfg1sBD1tWkgUdEpBA==", - "requires": { - "@azure/abort-controller": "^1.0.4", - "@azure/core-client": "^1.3.0", - "@azure/core-rest-pipeline": "^1.3.0" - } - }, - "@azure/core-lro": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.5.3.tgz", - "integrity": "sha512-ubkOf2YCnVtq7KqEJQqAI8dDD5rH1M6OP5kW0KO/JQyTaxLA0N0pjFWvvaysCj9eHMNBcuuoZXhhl0ypjod2DA==", - "requires": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-util": "^1.2.0", - "@azure/logger": "^1.0.0", - "tslib": "^2.2.0" - }, - "dependencies": { - "tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - } - } - }, - "@azure/core-paging": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.5.0.tgz", - "integrity": "sha512-zqWdVIt+2Z+3wqxEOGzR5hXFZ8MGKK52x4vFLw8n58pR6ZfKRx3EXYTxTaYxYHc/PexPUTyimcTWFJbji9Z6Iw==", - "requires": { - "tslib": "^2.2.0" - }, - "dependencies": { - "tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - } - } - }, - "@azure/core-rest-pipeline": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.10.3.tgz", - "integrity": "sha512-AMQb0ttiGJ0MIV/r+4TVra6U4+90mPeOveehFnrqKlo7dknPJYdJ61wOzYJXJjDxF8LcCtSogfRelkq+fCGFTw==", - "requires": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-auth": "^1.4.0", - "@azure/core-tracing": "^1.0.1", - "@azure/core-util": "^1.3.0", - "@azure/logger": "^1.0.0", - "form-data": "^4.0.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "tslib": "^2.2.0" - }, - "dependencies": { - "@azure/core-tracing": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.1.tgz", - "integrity": "sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw==", - "requires": { - "tslib": "^2.2.0" - } - }, - "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==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - } - } - }, - "@azure/core-tracing": { - "version": "1.0.0-preview.12", - "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.12.tgz", - "integrity": "sha512-nvo2Wc4EKZGN6eFu9n3U7OXmASmL8VxoPIH7xaD6OlQqi44bouF0YIi9ID5rEsKLiAU59IYx6M297nqWVMWPDg==", - "requires": { - "@opentelemetry/api": "^1.0.0", - "tslib": "^2.2.0" - }, - "dependencies": { - "tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - } - } - }, - "@azure/core-util": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.3.2.tgz", - "integrity": "sha512-2bECOUh88RvL1pMZTcc6OzfobBeWDBf5oBbhjIhT1MV9otMVWCzpOJkkiKtrnO88y5GGBelgY8At73KGAdbkeQ==", - "requires": { - "@azure/abort-controller": "^1.0.0", - "tslib": "^2.2.0" - }, - "dependencies": { - "tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - } - } - }, - "@azure/identity": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-1.5.2.tgz", - "integrity": "sha512-vqyeRbd2i0h9F4mqW5JbkP1xfabqKQ21l/81osKhpOQ2LtwaJW6nw4+0PsVYnxcbPHFCIZt6EWAk74a3OGYZJA==", - "requires": { - "@azure/core-auth": "^1.3.0", - "@azure/core-client": "^1.0.0", - "@azure/core-rest-pipeline": "^1.1.0", - "@azure/core-tracing": "1.0.0-preview.12", - "@azure/logger": "^1.0.0", - "@azure/msal-node": "1.0.0-beta.6", - "@types/stoppable": "^1.1.0", - "axios": "^0.21.1", - "events": "^3.0.0", - "jws": "^4.0.0", - "keytar": "^7.3.0", - "msal": "^1.0.2", - "open": "^7.0.0", - "qs": "^6.7.0", - "stoppable": "^1.1.0", - "tslib": "^2.0.0", - "uuid": "^8.3.0" - }, - "dependencies": { - "tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - } - } - }, - "@azure/keyvault-keys": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@azure/keyvault-keys/-/keyvault-keys-4.7.0.tgz", - "integrity": "sha512-HScWdORbRCKi1vdKI6EChe/t/P/zV7jcGZWfj18BOyeensk5d1/Ynfx1t6xfAy5zUIQvAWVU97hXdCznDpULbQ==", - "requires": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-auth": "^1.3.0", - "@azure/core-client": "^1.5.0", - "@azure/core-http-compat": "^1.3.0", - "@azure/core-lro": "^2.2.0", - "@azure/core-paging": "^1.1.1", - "@azure/core-rest-pipeline": "^1.8.0", - "@azure/core-tracing": "^1.0.0", - "@azure/core-util": "^1.0.0", - "@azure/logger": "^1.0.0", - "tslib": "^2.2.0" - }, - "dependencies": { - "@azure/core-tracing": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.1.tgz", - "integrity": "sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw==", - "requires": { - "tslib": "^2.2.0" - } - }, - "tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - } - } - }, - "@azure/keyvault-secrets": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@azure/keyvault-secrets/-/keyvault-secrets-4.7.0.tgz", - "integrity": "sha512-YvlFXRQ+SI5NT4GtSFbb6HGo6prW3yzDab8tr6vga2/SjDQew3wJsCAAr/xwZz6XshFXCYEX26CDKmPf+SJKJg==", - "requires": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-auth": "^1.3.0", - "@azure/core-client": "^1.5.0", - "@azure/core-http-compat": "^1.3.0", - "@azure/core-lro": "^2.2.0", - "@azure/core-paging": "^1.1.1", - "@azure/core-rest-pipeline": "^1.8.0", - "@azure/core-tracing": "^1.0.0", - "@azure/core-util": "^1.0.0", - "@azure/logger": "^1.0.0", - "tslib": "^2.2.0" - }, - "dependencies": { - "@azure/core-tracing": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.1.tgz", - "integrity": "sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw==", - "requires": { - "tslib": "^2.2.0" - } - }, - "tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - } - } - }, - "@azure/logger": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.4.tgz", - "integrity": "sha512-ustrPY8MryhloQj7OWGe+HrYx+aoiOxzbXTtgblbV3xwCqpzUK36phH3XNHQKj3EPonyFUuDTfR3qFhTEAuZEg==", - "requires": { - "tslib": "^2.2.0" - }, - "dependencies": { - "tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - } - } - }, - "@azure/msal-common": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-4.5.1.tgz", - "integrity": "sha512-/i5dXM+QAtO+6atYd5oHGBAx48EGSISkXNXViheliOQe+SIFMDo3gSq3lL54W0suOSAsVPws3XnTaIHlla0PIQ==", - "requires": { - "debug": "^4.1.1" - } - }, - "@azure/msal-node": { - "version": "1.0.0-beta.6", - "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-1.0.0-beta.6.tgz", - "integrity": "sha512-ZQI11Uz1j0HJohb9JZLRD8z0moVcPks1AFW4Q/Gcl67+QvH4aKEJti7fjCcipEEZYb/qzLSO8U6IZgPYytsiJQ==", - "requires": { - "@azure/msal-common": "^4.0.0", - "axios": "^0.21.1", - "jsonwebtoken": "^8.5.1", - "uuid": "^8.3.0" - }, - "dependencies": { - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - } - } - }, - "@celo/base": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@celo/base/-/base-4.1.0.tgz", - "integrity": "sha512-Q9wKLa4JJw06FXpToZm5PYfFYjx+tvwIQlvFofx+GleX+uq+BT/gdxgTQ/c08OrxZUyc53TUdqSCs+88VlhmlA==" - }, - "@celo/bls12377js": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@celo/bls12377js/-/bls12377js-0.1.1.tgz", - "integrity": "sha512-hQjTIegg1+ZX5lPyeBYUEHYW7ktG0xkWxf4tcj//bK+tzyeQd6mMrne7C4+tBZgn+I35HtxYIo+YOLYJxXBg7A==", - "requires": { - "@stablelib/blake2xs": "0.10.4", - "big-integer": "^1.6.44" - } - }, - "@celo/connect": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@celo/connect/-/connect-4.1.0.tgz", - "integrity": "sha512-1PaPy/b9ZLtXGCNN7lqlt0Nqsw/Ql20iCcOZbrAhoN9HkxtHI/yMK7p9aMGDR0Qy4jEi123p8kt5qgJhH2IkKg==", - "requires": { - "@celo/base": "4.1.0", - "@celo/utils": "4.1.0", - "@types/debug": "^4.1.5", - "@types/utf8": "^2.1.6", - "bignumber.js": "^9.0.0", - "debug": "^4.1.1", - "utf8": "3.0.0" - } - }, - "@celo/contractkit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@celo/contractkit/-/contractkit-4.1.0.tgz", - "integrity": "sha512-JmlCMrU2o09zjA6hiRixdKzTTmwkm2STL39sraYD96JMJsywwnu300oRMEupDZN3HobwStKTuTnR8r+xGeWT9Q==", - "requires": { - "@celo/base": "4.1.0", - "@celo/connect": "4.1.0", - "@celo/utils": "4.1.0", - "@celo/wallet-local": "4.1.0", - "@types/bn.js": "^5.1.0", - "@types/debug": "^4.1.5", - "bignumber.js": "^9.0.0", - "cross-fetch": "^3.0.6", - "debug": "^4.1.1", - "fp-ts": "2.1.1", - "io-ts": "2.0.1", - "semver": "^7.3.5", - "web3": "1.3.6" - } - }, - "@celo/cryptographic-utils": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@celo/cryptographic-utils/-/cryptographic-utils-4.1.0.tgz", - "integrity": "sha512-EBmzoDYRS+ZL99Ywm32w9iAb7MaHGfUkjtKwUPp9m20k5hhMhZb1+h34tgrH/F7HIs7GU5bwc262ykE9uHDLZQ==", - "requires": { - "@celo/base": "4.1.0", - "@celo/bls12377js": "0.1.1", - "@celo/utils": "4.1.0", - "@types/bn.js": "^5.1.0", - "@types/elliptic": "^6.4.9", - "@types/ethereumjs-util": "^5.2.0", - "@types/node": "^10.12.18", - "@types/randombytes": "^2.0.0", - "bigi": "^1.1.0", - "bip32": "^2.0.6", - "bip39": "git+https://github.com/bitcoinjs/bip39.git#d8ea080a18b40f301d4e2219a2991cd2417e83c2", - "buffer-reverse": "^1.0.1", - "elliptic": "^6.5.4", - "ethereumjs-util": "^5.2.0" - }, - "dependencies": { - "bip32": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.6.tgz", - "integrity": "sha512-HpV5OMLLGTjSVblmrtYRfFFKuQB+GArM0+XP8HGWfJ5vxYBqo+DesvJwOdC2WJ3bCkZShGf0QIfoIpeomVzVdA==", - "requires": { - "@types/node": "10.12.18", - "bs58check": "^2.1.1", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "tiny-secp256k1": "^1.1.3", - "typeforce": "^1.11.5", - "wif": "^2.0.6" - }, - "dependencies": { - "@types/node": { - "version": "10.12.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", - "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==" - } - } - } - } - }, - "@celo/explorer": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@celo/explorer/-/explorer-4.1.0.tgz", - "integrity": "sha512-/v07XW+EgfDoiKBBf9gq1BGKvVTkhrKF+jZACRvttFOLCYB+uKRv7gjwIoYrwTI4abr3c/7nFWcBvo4d6s7SIg==", - "requires": { - "@celo/base": "4.1.0", - "@celo/connect": "4.1.0", - "@celo/contractkit": "4.1.0", - "@celo/utils": "4.1.0", - "@types/debug": "^4.1.5", - "cross-fetch": "^3.1.5", - "debug": "^4.1.1" - } - }, - "@celo/governance": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@celo/governance/-/governance-4.1.0.tgz", - "integrity": "sha512-IWUWuo8AIPG+dJri5QoH/NgZdD6hgs2Ued0CWSYPX/kZMcCa8y1Qxftw5IBSZ+tDtFVTMMxiAzudKU4vsFr2jg==", - "requires": { - "@celo/base": "4.1.0", - "@celo/connect": "4.1.0", - "@celo/contractkit": "4.1.0", - "@celo/explorer": "4.1.0", - "@celo/utils": "4.1.0", - "@types/debug": "^4.1.5", - "@types/ethereumjs-util": "^5.2.0", - "@types/inquirer": "^6.5.0", - "debug": "^4.1.1", - "ethereumjs-util": "^5.2.0", - "inquirer": "^7.0.5" - } - }, - "@celo/identity": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@celo/identity/-/identity-4.1.0.tgz", - "integrity": "sha512-ZWOyohdQ2g2XZH7JYWYL+3nma3vtCSGclSu371ZWAp+BpS0wF1+Qo7L8a6Q2/rD0340+tZLqtinRz1k2JlfhkA==", - "requires": { - "@celo/base": "4.1.0", - "@celo/contractkit": "4.1.0", - "@celo/phone-number-privacy-common": "^2.0.2", - "@celo/utils": "4.1.0", - "@types/debug": "^4.1.5", - "bignumber.js": "^9.0.0", - "blind-threshold-bls": "git+https://github.com/celo-org/blind-threshold-bls-wasm.git#e1e2f8a", - "cross-fetch": "3.0.4", - "debug": "^4.1.1", - "elliptic": "^6.5.4", - "fp-ts": "2.1.1", - "io-ts": "2.0.1" - }, - "dependencies": { - "cross-fetch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.4.tgz", - "integrity": "sha512-MSHgpjQqgbT/94D4CyADeNoYh52zMkCX4pcJvPP5WqPsLFMKjr2TCMg381ox5qI0ii2dPwaLx/00477knXqXVw==", - "requires": { - "node-fetch": "2.6.0", - "whatwg-fetch": "3.0.0" - } - }, - "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" - } - } - }, - "@celo/phone-number-privacy-common": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@celo/phone-number-privacy-common/-/phone-number-privacy-common-2.0.2.tgz", - "integrity": "sha512-bQ3ufYADU+LDnZyQPl+/zVQMTPREgwmYXFremKkm4nlTm+83Ti9G7obXeMkFtDrG//vvgC8ryMHq0ZkYh5XkFA==", - "requires": { - "@celo/base": "^3.1.0", - "@celo/contractkit": "^3.1.0", - "@celo/phone-utils": "^3.1.0", - "@celo/utils": "^3.1.0", - "bignumber.js": "^9.0.0", - "bunyan": "1.8.12", - "bunyan-debug-stream": "2.0.0", - "bunyan-gke-stackdriver": "0.1.2", - "dotenv": "^8.2.0", - "elliptic": "^6.5.4", - "io-ts": "2.0.1", - "is-base64": "^1.1.0" - }, - "dependencies": { - "@celo/base": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@celo/base/-/base-3.2.0.tgz", - "integrity": "sha512-9wfZYiYv7dzt17a29fxU6sV7JssyXfpSQ9kPSpfOlsewPICXwfOMQ+25Jn6xZu20Vx9rmKebmLHiQyiuYEDOcQ==" - }, - "@celo/connect": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@celo/connect/-/connect-3.2.0.tgz", - "integrity": "sha512-iLOLo8d1OqNcX827/iCfWCeWaewUl0kyhL1xgyXrf//YaPU+ljKtruJmiLLrxkfdB/etWUdcryrbmuUhjHZmKg==", - "requires": { - "@celo/base": "3.2.0", - "@celo/utils": "3.2.0", - "@types/debug": "^4.1.5", - "@types/utf8": "^2.1.6", - "bignumber.js": "^9.0.0", - "debug": "^4.1.1", - "utf8": "3.0.0" - } - }, - "@celo/contractkit": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@celo/contractkit/-/contractkit-3.2.0.tgz", - "integrity": "sha512-kt4ViBRMg7ezCPi2SdcrYdDorA1Meg/qk97/u4izEIthl9GM4QSRfhhHYsbXYm0NV/MZ2BkS0cCsQ/SHcILSaA==", - "requires": { - "@celo/base": "3.2.0", - "@celo/connect": "3.2.0", - "@celo/utils": "3.2.0", - "@celo/wallet-local": "3.2.0", - "@types/bn.js": "^5.1.0", - "@types/debug": "^4.1.5", - "bignumber.js": "^9.0.0", - "cross-fetch": "^3.0.6", - "debug": "^4.1.1", - "fp-ts": "2.1.1", - "io-ts": "2.0.1", - "semver": "^7.3.5", - "web3": "1.3.6" - } - }, - "@celo/phone-utils": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@celo/phone-utils/-/phone-utils-3.2.0.tgz", - "integrity": "sha512-TbEKN7emsQY3nac7Lv5XkPA7HWXjrhuRpbtVDsJ8uB3Pbu9zAYCs0J8QlikEUJLPGoRA3oCZLri54GhWFG84BQ==", - "requires": { - "@celo/base": "3.2.0", - "@celo/utils": "3.2.0", - "@types/country-data": "^0.0.0", - "@types/ethereumjs-util": "^5.2.0", - "@types/google-libphonenumber": "^7.4.23", - "@types/node": "^10.12.18", - "country-data": "^0.0.31", - "fp-ts": "2.1.1", - "google-libphonenumber": "^3.2.27", - "io-ts": "2.0.1" - } - }, - "@celo/utils": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@celo/utils/-/utils-3.2.0.tgz", - "integrity": "sha512-Om1mTzwsdV6FVPvraafcJeRnzz7Xv/lyGmyZaoEZ9fErRadu9ZrOsuDQniYe+lD78DQ0NATxJL04WjhEKVkn+A==", - "requires": { - "@celo/base": "3.2.0", - "@types/bn.js": "^5.1.0", - "@types/elliptic": "^6.4.9", - "@types/ethereumjs-util": "^5.2.0", - "@types/node": "^10.12.18", - "bignumber.js": "^9.0.0", - "elliptic": "^6.5.4", - "ethereumjs-util": "^5.2.0", - "io-ts": "2.0.1", - "web3-eth-abi": "1.3.6", - "web3-utils": "1.3.6" - } - }, - "@celo/wallet-base": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@celo/wallet-base/-/wallet-base-3.2.0.tgz", - "integrity": "sha512-lwhesT2BkXIyPI/ox/QbVVCRtLzTAGO25M3TlWBfSCzkRAf/AiV41lzEf9J7A1ozDKXS9s7bj8odiRkMAcelyQ==", - "requires": { - "@celo/base": "3.2.0", - "@celo/connect": "3.2.0", - "@celo/utils": "3.2.0", - "@types/debug": "^4.1.5", - "@types/ethereumjs-util": "^5.2.0", - "bignumber.js": "^9.0.0", - "debug": "^4.1.1", - "eth-lib": "^0.2.8", - "ethereumjs-util": "^5.2.0" - } - }, - "@celo/wallet-local": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@celo/wallet-local/-/wallet-local-3.2.0.tgz", - "integrity": "sha512-hR70gzNCDHgf/GskaaLtB7Jz4AqJEW0b+1jG1rsuAyaXLJomSRADGBwUUgMy1dKU87fcQ9h7Uh+AuRAP4/5COQ==", - "requires": { - "@celo/connect": "3.2.0", - "@celo/utils": "3.2.0", - "@celo/wallet-base": "3.2.0", - "@types/ethereumjs-util": "^5.2.0", - "eth-lib": "^0.2.8", - "ethereumjs-util": "^5.2.0" - } - } - } - }, - "@celo/phone-utils": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@celo/phone-utils/-/phone-utils-4.1.0.tgz", - "integrity": "sha512-qWvX1Po6+BYBj0a32ZNoqmggd3fA52OMJryIYS6eoRDOFC2qo9wRNEw6O8ZWIN3JJ24wEEbtiVATMrrt2qCKJg==", - "requires": { - "@celo/base": "4.1.0", - "@celo/utils": "4.1.0", - "@types/country-data": "^0.0.0", - "@types/ethereumjs-util": "^5.2.0", - "@types/google-libphonenumber": "^7.4.23", - "@types/node": "^10.12.18", - "country-data": "^0.0.31", - "fp-ts": "2.1.1", - "google-libphonenumber": "^3.2.27", - "io-ts": "2.0.1" - } - }, - "@celo/utils": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@celo/utils/-/utils-4.1.0.tgz", - "integrity": "sha512-N0ijAXAGFQavX75clVXhGOVTzSpv/AJmh+igYUCNVzyF0s/Ms/l9WjjEQsd0c3uWRkWBLXiKmCNTI9LZXazjkw==", - "requires": { - "@celo/base": "4.1.0", - "@types/bn.js": "^5.1.0", - "@types/elliptic": "^6.4.9", - "@types/ethereumjs-util": "^5.2.0", - "@types/node": "^10.12.18", - "bignumber.js": "^9.0.0", - "elliptic": "^6.5.4", - "ethereumjs-util": "^5.2.0", - "io-ts": "2.0.1", - "web3-eth-abi": "1.3.6", - "web3-utils": "1.3.6" - } - }, - "@celo/wallet-base": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@celo/wallet-base/-/wallet-base-4.1.0.tgz", - "integrity": "sha512-fc6DGI6vcwGE/z3hT/7rZkQ9nvv/7l8RAEmof05zqMCdXdqaq135GWuE5H7gcC/qCLJzRNVpPGcWAPKUQ4z03A==", - "requires": { - "@celo/base": "4.1.0", - "@celo/connect": "4.1.0", - "@celo/utils": "4.1.0", - "@types/debug": "^4.1.5", - "@types/ethereumjs-util": "^5.2.0", - "bignumber.js": "^9.0.0", - "debug": "^4.1.1", - "eth-lib": "^0.2.8", - "ethereumjs-util": "^5.2.0" - } - }, - "@celo/wallet-hsm": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@celo/wallet-hsm/-/wallet-hsm-4.1.0.tgz", - "integrity": "sha512-n/j3JVoiJywCFYRZitcxrGphtoe/qUkrab3AMkrTIUko3XzgNhCeMLe8BszR7gpFr9HfTScM12xwsaIUv21Iww==", - "requires": { - "@celo/base": "4.1.0", - "@types/asn1js": "^0.0.2", - "@types/debug": "^4.1.5", - "@types/secp256k1": "^4.0.0", - "asn1js": "^2.0.26", - "elliptic": "^6.5.4", - "eth-lib": "^0.2.8", - "ethereumjs-util": "^5.2.0", - "secp256k1": "^4.0.0" - } - }, - "@celo/wallet-hsm-azure": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@celo/wallet-hsm-azure/-/wallet-hsm-azure-4.1.0.tgz", - "integrity": "sha512-GL27+mZjXBKxQZ3uSl9PuhjuqXCiJpFqIH4Ge6ePsSc0BWV7fX2bzosj7Nw174Q5VQpUCVm3YPRDHSDzufHPVA==", - "requires": { - "@azure/identity": "^1.1.0", - "@azure/keyvault-keys": "^4.1.0", - "@azure/keyvault-secrets": "^4.1.0", - "@celo/connect": "4.1.0", - "@celo/utils": "4.1.0", - "@celo/wallet-base": "4.1.0", - "@celo/wallet-hsm": "4.1.0", - "@celo/wallet-remote": "4.1.0", - "@types/secp256k1": "^4.0.0", - "bignumber.js": "^9.0.0", - "debug": "^4.1.1", - "eth-lib": "^0.2.8", - "ethereumjs-util": "^5.2.0", - "secp256k1": "^4.0.0" - } - }, - "@celo/wallet-ledger": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@celo/wallet-ledger/-/wallet-ledger-4.1.0.tgz", - "integrity": "sha512-8UVJsEIGGc2rWo3U70fq3dv9QoTpapYdiOQ/RJGctfoWduMLRWeTIApk4DJ9+wUS0Wjf3F5+U9q35UxVp/KTdA==", - "requires": { - "@celo/connect": "4.1.0", - "@celo/utils": "4.1.0", - "@celo/wallet-base": "4.1.0", - "@celo/wallet-remote": "4.1.0", - "@ledgerhq/hw-app-eth": "~5.11.0", - "@ledgerhq/hw-transport": "~5.11.0", - "@types/ethereumjs-util": "^5.2.0", - "debug": "^4.1.1", - "eth-lib": "^0.2.8", - "ethereumjs-util": "^5.2.0" - } - }, - "@celo/wallet-local": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@celo/wallet-local/-/wallet-local-4.1.0.tgz", - "integrity": "sha512-nG7f77gblF3CQ3811LOtmoLr8yC+f+G6QjkgUV9GmFNBDdUNQZf7OM1ZQaYib10D6OwY0UPxlP9N+tweVWDOdA==", - "requires": { - "@celo/connect": "4.1.0", - "@celo/utils": "4.1.0", - "@celo/wallet-base": "4.1.0", - "@types/ethereumjs-util": "^5.2.0", - "eth-lib": "^0.2.8", - "ethereumjs-util": "^5.2.0" - } - }, - "@celo/wallet-remote": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@celo/wallet-remote/-/wallet-remote-4.1.0.tgz", - "integrity": "sha512-SNxVdLa6ixJGDqOyBI2SOJN5CLr6gZG0AUCo0cPwkpV7c5Dy9ezJ2gRWxxvcbU1j++Jjh/H1yyKLmqbfUEtw8w==", - "requires": { - "@celo/connect": "4.1.0", - "@celo/utils": "4.1.0", - "@celo/wallet-base": "4.1.0", - "@types/debug": "^4.1.5", - "@types/ethereumjs-util": "^5.2.0", - "eth-lib": "^0.2.8", - "ethereumjs-util": "^5.2.0" - } - }, - "@ethersproject/abi": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.0.7.tgz", - "integrity": "sha512-Cqktk+hSIckwP/W8O47Eef60VwmoSC/L3lY0+dIBhQPCNn9E4V7rwmm2aFrNRRDJfFlGuZ1khkQUOc3oBX+niw==", - "requires": { - "@ethersproject/address": "^5.0.4", - "@ethersproject/bignumber": "^5.0.7", - "@ethersproject/bytes": "^5.0.4", - "@ethersproject/constants": "^5.0.4", - "@ethersproject/hash": "^5.0.4", - "@ethersproject/keccak256": "^5.0.3", - "@ethersproject/logger": "^5.0.5", - "@ethersproject/properties": "^5.0.3", - "@ethersproject/strings": "^5.0.4" - } - }, - "@ethersproject/abstract-provider": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.6.1.tgz", - "integrity": "sha512-BxlIgogYJtp1FS8Muvj8YfdClk3unZH0vRMVX791Z9INBNT/kuACZ9GzaY1Y4yFq+YSy6/w4gzj3HCRKrK9hsQ==", - "requires": { - "@ethersproject/bignumber": "^5.6.2", - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "@ethersproject/networks": "^5.6.3", - "@ethersproject/properties": "^5.6.0", - "@ethersproject/transactions": "^5.6.2", - "@ethersproject/web": "^5.6.1" - } - }, - "@ethersproject/abstract-signer": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.6.2.tgz", - "integrity": "sha512-n1r6lttFBG0t2vNiI3HoWaS/KdOt8xyDjzlP2cuevlWLG6EX0OwcKLyG/Kp/cuwNxdy/ous+R/DEMdTUwWQIjQ==", - "requires": { - "@ethersproject/abstract-provider": "^5.6.1", - "@ethersproject/bignumber": "^5.6.2", - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "@ethersproject/properties": "^5.6.0" - } - }, - "@ethersproject/address": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.6.1.tgz", - "integrity": "sha512-uOgF0kS5MJv9ZvCz7x6T2EXJSzotiybApn4XlOgoTX0xdtyVIJ7pF+6cGPxiEq/dpBiTfMiw7Yc81JcwhSYA0Q==", - "requires": { - "@ethersproject/bignumber": "^5.6.2", - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/keccak256": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "@ethersproject/rlp": "^5.6.1" - } - }, - "@ethersproject/base64": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.6.1.tgz", - "integrity": "sha512-qB76rjop6a0RIYYMiB4Eh/8n+Hxu2NIZm8S/Q7kNo5pmZfXhHGHmS4MinUainiBC54SCyRnwzL+KZjj8zbsSsw==", - "requires": { - "@ethersproject/bytes": "^5.6.1" - } - }, - "@ethersproject/bignumber": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.6.2.tgz", - "integrity": "sha512-v7+EEUbhGqT3XJ9LMPsKvXYHFc8eHxTowFCG/HgJErmq4XHJ2WR7aeyICg3uTOAQ7Icn0GFHAohXEhxQHq4Ubw==", - "requires": { - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "bn.js": "^5.2.1" - }, - "dependencies": { - "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==" - } - } - }, - "@ethersproject/bytes": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.6.1.tgz", - "integrity": "sha512-NwQt7cKn5+ZE4uDn+X5RAXLp46E1chXoaMmrxAyA0rblpxz8t58lVkrHXoRIn0lz1joQElQ8410GqhTqMOwc6g==", - "requires": { - "@ethersproject/logger": "^5.6.0" - } - }, - "@ethersproject/constants": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.6.1.tgz", - "integrity": "sha512-QSq9WVnZbxXYFftrjSjZDUshp6/eKp6qrtdBtUCm0QxCV5z1fG/w3kdlcsjMCQuQHUnAclKoK7XpXMezhRDOLg==", - "requires": { - "@ethersproject/bignumber": "^5.6.2" - } - }, - "@ethersproject/hash": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.6.1.tgz", - "integrity": "sha512-L1xAHurbaxG8VVul4ankNX5HgQ8PNCTrnVXEiFnE9xoRnaUcgfD12tZINtDinSllxPLCtGwguQxJ5E6keE84pA==", - "requires": { - "@ethersproject/abstract-signer": "^5.6.2", - "@ethersproject/address": "^5.6.1", - "@ethersproject/bignumber": "^5.6.2", - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/keccak256": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "@ethersproject/properties": "^5.6.0", - "@ethersproject/strings": "^5.6.1" - } - }, - "@ethersproject/keccak256": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.6.1.tgz", - "integrity": "sha512-bB7DQHCTRDooZZdL3lk9wpL0+XuG3XLGHLh3cePnybsO3V0rdCAOQGpn/0R3aODmnTOOkCATJiD2hnL+5bwthA==", - "requires": { - "@ethersproject/bytes": "^5.6.1", - "js-sha3": "0.8.0" - } - }, - "@ethersproject/logger": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.6.0.tgz", - "integrity": "sha512-BiBWllUROH9w+P21RzoxJKzqoqpkyM1pRnEKG69bulE9TSQD8SAIvTQqIMZmmCO8pUNkgLP1wndX1gKghSpBmg==" - }, - "@ethersproject/networks": { - "version": "5.6.4", - "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.6.4.tgz", - "integrity": "sha512-KShHeHPahHI2UlWdtDMn2lJETcbtaJge4k7XSjDR9h79QTd6yQJmv6Cp2ZA4JdqWnhszAOLSuJEd9C0PRw7hSQ==", - "requires": { - "@ethersproject/logger": "^5.6.0" - } - }, - "@ethersproject/properties": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.6.0.tgz", - "integrity": "sha512-szoOkHskajKePTJSZ46uHUWWkbv7TzP2ypdEK6jGMqJaEt2sb0jCgfBo0gH0m2HBpRixMuJ6TBRaQCF7a9DoCg==", - "requires": { - "@ethersproject/logger": "^5.6.0" - } - }, - "@ethersproject/rlp": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.6.1.tgz", - "integrity": "sha512-uYjmcZx+DKlFUk7a5/W9aQVaoEC7+1MOBgNtvNg13+RnuUwT4F0zTovC0tmay5SmRslb29V1B7Y5KCri46WhuQ==", - "requires": { - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/logger": "^5.6.0" - } - }, - "@ethersproject/signing-key": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.6.2.tgz", - "integrity": "sha512-jVbu0RuP7EFpw82vHcL+GP35+KaNruVAZM90GxgQnGqB6crhBqW/ozBfFvdeImtmb4qPko0uxXjn8l9jpn0cwQ==", - "requires": { - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "@ethersproject/properties": "^5.6.0", - "bn.js": "^5.2.1", - "elliptic": "6.5.4", - "hash.js": "1.1.7" - }, - "dependencies": { - "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==" - } - } - }, - "@ethersproject/strings": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.6.1.tgz", - "integrity": "sha512-2X1Lgk6Jyfg26MUnsHiT456U9ijxKUybz8IM1Vih+NJxYtXhmvKBcHOmvGqpFSVJ0nQ4ZCoIViR8XlRw1v/+Cw==", - "requires": { - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/constants": "^5.6.1", - "@ethersproject/logger": "^5.6.0" - } - }, - "@ethersproject/transactions": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.6.2.tgz", - "integrity": "sha512-BuV63IRPHmJvthNkkt9G70Ullx6AcM+SDc+a8Aw/8Yew6YwT51TcBKEp1P4oOQ/bP25I18JJr7rcFRgFtU9B2Q==", - "requires": { - "@ethersproject/address": "^5.6.1", - "@ethersproject/bignumber": "^5.6.2", - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/constants": "^5.6.1", - "@ethersproject/keccak256": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "@ethersproject/properties": "^5.6.0", - "@ethersproject/rlp": "^5.6.1", - "@ethersproject/signing-key": "^5.6.2" - } - }, - "@ethersproject/web": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.6.1.tgz", - "integrity": "sha512-/vSyzaQlNXkO1WV+RneYKqCJwualcUdx/Z3gseVovZP0wIlOFcCE1hkRhKBH8ImKbGQbMl9EAAyJFrJu7V0aqA==", - "requires": { - "@ethersproject/base64": "^5.6.1", - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "@ethersproject/properties": "^5.6.0", - "@ethersproject/strings": "^5.6.1" - } - }, - "@firebase/analytics": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.6.0.tgz", - "integrity": "sha512-6qYEOPUVYrMhqvJ46Z5Uf1S4uULd6d7vGpMP5Qz+u8kIWuOQGcPdJKQap+Hla6Rq164or9gC2HRXuYXKlgWfpw==", - "requires": { - "@firebase/analytics-types": "0.4.0", - "@firebase/component": "0.1.19", - "@firebase/installations": "0.4.17", - "@firebase/logger": "0.2.6", - "@firebase/util": "0.3.2", - "tslib": "^1.11.1" - } - }, - "@firebase/analytics-types": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.4.0.tgz", - "integrity": "sha512-Jj2xW+8+8XPfWGkv9HPv/uR+Qrmq37NPYT352wf7MvE9LrstpLVmFg3LqG6MCRr5miLAom5sen2gZ+iOhVDeRA==" - }, - "@firebase/app": { - "version": "0.6.11", - "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.6.11.tgz", - "integrity": "sha512-FH++PaoyTzfTAVuJ0gITNYEIcjT5G+D0671La27MU8Vvr6MTko+5YUZ4xS9QItyotSeRF4rMJ1KR7G8LSyySiA==", - "requires": { - "@firebase/app-types": "0.6.1", - "@firebase/component": "0.1.19", - "@firebase/logger": "0.2.6", - "@firebase/util": "0.3.2", - "dom-storage": "2.1.0", - "tslib": "^1.11.1", - "xmlhttprequest": "1.8.0" - } - }, - "@firebase/app-types": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.6.1.tgz", - "integrity": "sha512-L/ZnJRAq7F++utfuoTKX4CLBG5YR7tFO3PLzG1/oXXKEezJ0kRL3CMRoueBEmTCzVb/6SIs2Qlaw++uDgi5Xyg==" - }, - "@firebase/auth": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.15.0.tgz", - "integrity": "sha512-IFuzhxS+HtOQl7+SZ/Mhaghy/zTU7CENsJFWbC16tv2wfLZbayKF5jYGdAU3VFLehgC8KjlcIWd10akc3XivfQ==", - "requires": { - "@firebase/auth-types": "0.10.1" - } - }, - "@firebase/auth-interop-types": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.1.5.tgz", - "integrity": "sha512-88h74TMQ6wXChPA6h9Q3E1Jg6TkTHep2+k63OWg3s0ozyGVMeY+TTOti7PFPzq5RhszQPQOoCi59es4MaRvgCw==" - }, - "@firebase/auth-types": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.10.1.tgz", - "integrity": "sha512-/+gBHb1O9x/YlG7inXfxff/6X3BPZt4zgBv4kql6HEmdzNQCodIRlEYnI+/da+lN+dha7PjaFH7C7ewMmfV7rw==" - }, - "@firebase/component": { - "version": "0.1.19", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.1.19.tgz", - "integrity": "sha512-L0S3g8eqaerg8y0zox3oOHSTwn/FE8RbcRHiurnbESvDViZtP5S5WnhuAPd7FnFxa8ElWK0z1Tr3ikzWDv1xdQ==", - "requires": { - "@firebase/util": "0.3.2", - "tslib": "^1.11.1" - } - }, - "@firebase/database": { - "version": "0.6.13", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.6.13.tgz", - "integrity": "sha512-NommVkAPzU7CKd1gyehmi3lz0K78q0KOfiex7Nfy7MBMwknLm7oNqKovXSgQV1PCLvKXvvAplDSFhDhzIf9obA==", - "requires": { - "@firebase/auth-interop-types": "0.1.5", - "@firebase/component": "0.1.19", - "@firebase/database-types": "0.5.2", - "@firebase/logger": "0.2.6", - "@firebase/util": "0.3.2", - "faye-websocket": "0.11.3", - "tslib": "^1.11.1" - } - }, - "@firebase/database-types": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.5.2.tgz", - "integrity": "sha512-ap2WQOS3LKmGuVFKUghFft7RxXTyZTDr0Xd8y2aqmWsbJVjgozi0huL/EUMgTjGFrATAjcf2A7aNs8AKKZ2a8g==", - "requires": { - "@firebase/app-types": "0.6.1" - } - }, - "@firebase/firestore": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-1.18.0.tgz", - "integrity": "sha512-maMq4ltkrwjDRusR2nt0qS4wldHQMp+0IDSfXIjC+SNmjnWY/t/+Skn9U3Po+dB38xpz3i7nsKbs+8utpDnPSw==", - "requires": { - "@firebase/component": "0.1.19", - "@firebase/firestore-types": "1.14.0", - "@firebase/logger": "0.2.6", - "@firebase/util": "0.3.2", - "@firebase/webchannel-wrapper": "0.4.0", - "@grpc/grpc-js": "^1.0.0", - "@grpc/proto-loader": "^0.5.0", - "node-fetch": "2.6.1", - "tslib": "^1.11.1" - }, - "dependencies": { - "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" - } - } - }, - "@firebase/firestore-types": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-1.14.0.tgz", - "integrity": "sha512-WF8IBwHzZDhwyOgQnmB0pheVrLNP78A8PGxk1nxb/Nrgh1amo4/zYvFMGgSsTeaQK37xMYS/g7eS948te/dJxw==" - }, - "@firebase/functions": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.5.1.tgz", - "integrity": "sha512-yyjPZXXvzFPjkGRSqFVS5Hc2Y7Y48GyyMH+M3i7hLGe69r/59w6wzgXKqTiSYmyE1pxfjxU4a1YqBDHNkQkrYQ==", - "requires": { - "@firebase/component": "0.1.19", - "@firebase/functions-types": "0.3.17", - "@firebase/messaging-types": "0.5.0", - "node-fetch": "2.6.1", - "tslib": "^1.11.1" - }, - "dependencies": { - "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" - } - } - }, - "@firebase/functions-types": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.3.17.tgz", - "integrity": "sha512-DGR4i3VI55KnYk4IxrIw7+VG7Q3gA65azHnZxo98Il8IvYLr2UTBlSh72dTLlDf25NW51HqvJgYJDKvSaAeyHQ==" - }, - "@firebase/installations": { - "version": "0.4.17", - "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.4.17.tgz", - "integrity": "sha512-AE/TyzIpwkC4UayRJD419xTqZkKzxwk0FLht3Dci8WI2OEKHSwoZG9xv4hOBZebe+fDzoV2EzfatQY8c/6Avig==", - "requires": { - "@firebase/component": "0.1.19", - "@firebase/installations-types": "0.3.4", - "@firebase/util": "0.3.2", - "idb": "3.0.2", - "tslib": "^1.11.1" - } - }, - "@firebase/installations-types": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.3.4.tgz", - "integrity": "sha512-RfePJFovmdIXb6rYwtngyxuEcWnOrzdZd9m7xAW0gRxDIjBT20n3BOhjpmgRWXo/DAxRmS7bRjWAyTHY9cqN7Q==" - }, - "@firebase/logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.2.6.tgz", - "integrity": "sha512-KIxcUvW/cRGWlzK9Vd2KB864HlUnCfdTH0taHE0sXW5Xl7+W68suaeau1oKNEqmc3l45azkd4NzXTCWZRZdXrw==" - }, - "@firebase/messaging": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.7.1.tgz", - "integrity": "sha512-iev/ST9v0xd/8YpGYrZtDcqdD9J6ZWzSuceRn8EKy5vIgQvW/rk2eTQc8axzvDpQ36ZfphMYuhW6XuNrR3Pd2Q==", - "requires": { - "@firebase/component": "0.1.19", - "@firebase/installations": "0.4.17", - "@firebase/messaging-types": "0.5.0", - "@firebase/util": "0.3.2", - "idb": "3.0.2", - "tslib": "^1.11.1" - } - }, - "@firebase/messaging-types": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@firebase/messaging-types/-/messaging-types-0.5.0.tgz", - "integrity": "sha512-QaaBswrU6umJYb/ZYvjR5JDSslCGOH6D9P136PhabFAHLTR4TWjsaACvbBXuvwrfCXu10DtcjMxqfhdNIB1Xfg==" - }, - "@firebase/performance": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.4.2.tgz", - "integrity": "sha512-irHTCVWJ/sxJo0QHg+yQifBeVu8ZJPihiTqYzBUz/0AGc51YSt49FZwqSfknvCN2+OfHaazz/ARVBn87g7Ex8g==", - "requires": { - "@firebase/component": "0.1.19", - "@firebase/installations": "0.4.17", - "@firebase/logger": "0.2.6", - "@firebase/performance-types": "0.0.13", - "@firebase/util": "0.3.2", - "tslib": "^1.11.1" - } - }, - "@firebase/performance-types": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.0.13.tgz", - "integrity": "sha512-6fZfIGjQpwo9S5OzMpPyqgYAUZcFzZxHFqOyNtorDIgNXq33nlldTL/vtaUZA8iT9TT5cJlCrF/jthKU7X21EA==" - }, - "@firebase/polyfill": { - "version": "0.3.36", - "resolved": "https://registry.npmjs.org/@firebase/polyfill/-/polyfill-0.3.36.tgz", - "integrity": "sha512-zMM9oSJgY6cT2jx3Ce9LYqb0eIpDE52meIzd/oe/y70F+v9u1LDqk5kUF5mf16zovGBWMNFmgzlsh6Wj0OsFtg==", - "requires": { - "core-js": "3.6.5", - "promise-polyfill": "8.1.3", - "whatwg-fetch": "2.0.4" - }, - "dependencies": { - "whatwg-fetch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", - "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==" - } - } - }, - "@firebase/remote-config": { - "version": "0.1.28", - "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.1.28.tgz", - "integrity": "sha512-4zSdyxpt94jAnFhO8toNjG8oMKBD+xTuBIcK+Nw8BdQWeJhEamgXlupdBARUk1uf3AvYICngHH32+Si/dMVTbw==", - "requires": { - "@firebase/component": "0.1.19", - "@firebase/installations": "0.4.17", - "@firebase/logger": "0.2.6", - "@firebase/remote-config-types": "0.1.9", - "@firebase/util": "0.3.2", - "tslib": "^1.11.1" - } - }, - "@firebase/remote-config-types": { - "version": "0.1.9", - "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.1.9.tgz", - "integrity": "sha512-G96qnF3RYGbZsTRut7NBX0sxyczxt1uyCgXQuH/eAfUCngxjEGcZQnBdy6mvSdqdJh5mC31rWPO4v9/s7HwtzA==" - }, - "@firebase/storage": { - "version": "0.3.43", - "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.3.43.tgz", - "integrity": "sha512-Jp54jcuyimLxPhZHFVAhNbQmgTu3Sda7vXjXrNpPEhlvvMSq4yuZBR6RrZxe/OrNVprLHh/6lTCjwjOVSo3bWA==", - "requires": { - "@firebase/component": "0.1.19", - "@firebase/storage-types": "0.3.13", - "@firebase/util": "0.3.2", - "tslib": "^1.11.1" - } - }, - "@firebase/storage-types": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.3.13.tgz", - "integrity": "sha512-pL7b8d5kMNCCL0w9hF7pr16POyKkb3imOW7w0qYrhBnbyJTdVxMWZhb0HxCFyQWC0w3EiIFFmxoz8NTFZDEFog==" - }, - "@firebase/util": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-0.3.2.tgz", - "integrity": "sha512-Dqs00++c8rwKky6KCKLLY2T1qYO4Q+X5t+lF7DInXDNF4ae1Oau35bkD+OpJ9u7l1pEv7KHowP6CUKuySCOc8g==", - "requires": { - "tslib": "^1.11.1" - } - }, - "@firebase/webchannel-wrapper": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.4.0.tgz", - "integrity": "sha512-8cUA/mg0S+BxIZ72TdZRsXKBP5n5uRcE3k29TZhZw6oIiHBt9JA7CTb/4pE1uKtE/q5NeTY2tBDcagoZ+1zjXQ==" - }, - "@grpc/grpc-js": { - "version": "1.6.9", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.6.9.tgz", - "integrity": "sha512-01Dy1wqXVsuiMI4m4tDoX+IPYAeNI8EsfNFPqAJBX4OiCSs5VU8Gw0pJq5NhGizH6nKUprmHb/QvxTq3d1xL5g==", - "requires": { - "@grpc/proto-loader": "^0.7.0", - "@types/node": ">=12.12.47" - }, - "dependencies": { - "@grpc/proto-loader": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.0.tgz", - "integrity": "sha512-SGPZtVmqOvNfPFOA/nNPn+0Weqa5wubBgQ56+JgTbeLY2VezwtMjwPPFzh0kvQccwWT3a2TXT0ZGK/pJoOTk1A==", - "requires": { - "@types/long": "^4.0.1", - "lodash.camelcase": "^4.3.0", - "long": "^4.0.0", - "protobufjs": "^7.0.0", - "yargs": "^16.2.0" - } - }, - "@types/node": { - "version": "18.7.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.1.tgz", - "integrity": "sha512-GKX1Qnqxo4S+Z/+Z8KKPLpH282LD7jLHWJcVryOflnsnH+BtSDfieR6ObwBMwpnNws0bUK8GI7z0unQf9bARNQ==" - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==" - } - } - }, - "@grpc/proto-loader": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.5.6.tgz", - "integrity": "sha512-DT14xgw3PSzPxwS13auTEwxhMMOoz33DPUKNtmYK/QYbBSpLXJy78FGGs5yVoxVobEqPm4iW9MOIoz0A3bLTRQ==", - "requires": { - "lodash.camelcase": "^4.3.0", - "protobufjs": "^6.8.6" - }, - "dependencies": { - "@types/node": { - "version": "18.7.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.1.tgz", - "integrity": "sha512-GKX1Qnqxo4S+Z/+Z8KKPLpH282LD7jLHWJcVryOflnsnH+BtSDfieR6ObwBMwpnNws0bUK8GI7z0unQf9bARNQ==" - }, - "protobufjs": { - "version": "6.11.3", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", - "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", - "requires": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/long": "^4.0.1", - "@types/node": ">=13.7.0", - "long": "^4.0.0" - } - } - } - }, - "@ledgerhq/devices": { - "version": "5.51.1", - "resolved": "https://registry.npmjs.org/@ledgerhq/devices/-/devices-5.51.1.tgz", - "integrity": "sha512-4w+P0VkbjzEXC7kv8T1GJ/9AVaP9I6uasMZ/JcdwZBS3qwvKo5A5z9uGhP5c7TvItzcmPb44b5Mw2kT+WjUuAA==", - "requires": { - "@ledgerhq/errors": "^5.50.0", - "@ledgerhq/logs": "^5.50.0", - "rxjs": "6", - "semver": "^7.3.5" - } - }, - "@ledgerhq/errors": { - "version": "5.50.0", - "resolved": "https://registry.npmjs.org/@ledgerhq/errors/-/errors-5.50.0.tgz", - "integrity": "sha512-gu6aJ/BHuRlpU7kgVpy2vcYk6atjB4iauP2ymF7Gk0ez0Y/6VSMVSJvubeEQN+IV60+OBK0JgeIZG7OiHaw8ow==" - }, - "@ledgerhq/hw-app-eth": { - "version": "5.11.0", - "resolved": "https://registry.npmjs.org/@ledgerhq/hw-app-eth/-/hw-app-eth-5.11.0.tgz", - "integrity": "sha512-qgpPwZzM8UMHYMC5+9xYV2O+8kgkDAl9+38w9JiBksaGmUFqcS4najsB1nj6AWf2rGEuXdKMb2WEYRskVypJrA==", - "requires": { - "@ledgerhq/errors": "^5.11.0", - "@ledgerhq/hw-transport": "^5.11.0" - } - }, - "@ledgerhq/hw-transport": { - "version": "5.11.0", - "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport/-/hw-transport-5.11.0.tgz", - "integrity": "sha512-z56iwv0DZZu20T5q9sNMHFQNVuRKYqzCuNFhY9woWSpmOQkyVHCRiEgOQbN5h6kVri6fkfPkDzqqcsYjJlnT9g==", - "requires": { - "@ledgerhq/devices": "^5.11.0", - "@ledgerhq/errors": "^5.11.0", - "events": "^3.1.0" - } - }, - "@ledgerhq/hw-transport-node-hid": { - "version": "5.11.0", - "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-node-hid/-/hw-transport-node-hid-5.11.0.tgz", - "integrity": "sha512-J/hP3IqcgZn/sTGblELkwZcH68Gbv3ZeaRWdhei6K2dAuxae4dxoVUf6PoOcupEOcXT5XzJBNMc5800AwFeOgg==", - "requires": { - "@ledgerhq/devices": "^5.11.0", - "@ledgerhq/errors": "^5.11.0", - "@ledgerhq/hw-transport": "^5.11.0", - "@ledgerhq/hw-transport-node-hid-noevents": "^5.11.0", - "@ledgerhq/logs": "^5.11.0", - "lodash": "^4.17.15", - "node-hid": "^1.2.0", - "usb": "^1.6.0" - } - }, - "@ledgerhq/hw-transport-node-hid-noevents": { - "version": "5.51.1", - "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-node-hid-noevents/-/hw-transport-node-hid-noevents-5.51.1.tgz", - "integrity": "sha512-9wFf1L8ZQplF7XOY2sQGEeOhpmBRzrn+4X43kghZ7FBDoltrcK+s/D7S+7ffg3j2OySyP6vIIIgloXylao5Scg==", - "requires": { - "@ledgerhq/devices": "^5.51.1", - "@ledgerhq/errors": "^5.50.0", - "@ledgerhq/hw-transport": "^5.51.1", - "@ledgerhq/logs": "^5.50.0", - "node-hid": "2.1.1" - }, - "dependencies": { - "@ledgerhq/hw-transport": { - "version": "5.51.1", - "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport/-/hw-transport-5.51.1.tgz", - "integrity": "sha512-6wDYdbWrw9VwHIcoDnqWBaDFyviyjZWv6H9vz9Vyhe4Qd7TIFmbTl/eWs6hZvtZBza9K8y7zD8ChHwRI4s9tSw==", - "requires": { - "@ledgerhq/devices": "^5.51.1", - "@ledgerhq/errors": "^5.50.0", - "events": "^3.3.0" - } - }, - "decompress-response": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", - "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", - "requires": { - "mimic-response": "^2.0.0" - } - }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==" - }, - "mimic-response": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", - "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==" - }, - "node-abi": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz", - "integrity": "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==", - "requires": { - "semver": "^5.4.1" - } - }, - "node-addon-api": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", - "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==" - }, - "node-hid": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/node-hid/-/node-hid-2.1.1.tgz", - "integrity": "sha512-Skzhqow7hyLZU93eIPthM9yjot9lszg9xrKxESleEs05V2NcbUptZc5HFqzjOkSmL0sFlZFr3kmvaYebx06wrw==", - "requires": { - "bindings": "^1.5.0", - "node-addon-api": "^3.0.2", - "prebuild-install": "^6.0.0" - } - }, - "prebuild-install": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.4.tgz", - "integrity": "sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ==", - "requires": { - "detect-libc": "^1.0.3", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^2.21.0", - "npmlog": "^4.0.1", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^3.0.3", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "simple-get": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", - "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", - "requires": { - "decompress-response": "^4.2.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - } - } - }, - "@ledgerhq/logs": { - "version": "5.50.0", - "resolved": "https://registry.npmjs.org/@ledgerhq/logs/-/logs-5.50.0.tgz", - "integrity": "sha512-swKHYCOZUGyVt4ge0u8a7AwNcA//h4nx5wIi0sruGye1IJ5Cva0GyK9L2/WdX+kWVTKp92ZiEo1df31lrWGPgA==" - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@oclif/color": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@oclif/color/-/color-0.1.2.tgz", - "integrity": "sha512-M9o+DOrb8l603qvgz1FogJBUGLqcMFL1aFg2ZEL0FbXJofiNTLOWIeB4faeZTLwE6dt0xH9GpCVpzksMMzGbmA==", - "requires": { - "ansi-styles": "^3.2.1", - "chalk": "^3.0.0", - "strip-ansi": "^5.2.0", - "supports-color": "^5.4.0", - "tslib": "^1" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==" - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - }, - "dependencies": { - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" - } - } - } - } - }, - "@oclif/command": { - "version": "1.8.16", - "resolved": "https://registry.npmjs.org/@oclif/command/-/command-1.8.16.tgz", - "integrity": "sha512-rmVKYEsKzurfRU0xJz+iHelbi1LGlihIWZ7Qvmb/CBz1EkhL7nOkW4SVXmG2dA5Ce0si2gr88i6q4eBOMRNJ1w==", - "requires": { - "@oclif/config": "^1.18.2", - "@oclif/errors": "^1.3.5", - "@oclif/help": "^1.0.1", - "@oclif/parser": "^3.8.6", - "debug": "^4.1.1", - "semver": "^7.3.2" - } - }, - "@oclif/config": { - "version": "1.18.3", - "resolved": "https://registry.npmjs.org/@oclif/config/-/config-1.18.3.tgz", - "integrity": "sha512-sBpko86IrTscc39EvHUhL+c++81BVTsIZ3ETu/vG+cCdi0N6vb2DoahR67A9FI2CGnxRRHjnTfa3m6LulwNATA==", - "requires": { - "@oclif/errors": "^1.3.5", - "@oclif/parser": "^3.8.0", - "debug": "^4.1.1", - "globby": "^11.0.1", - "is-wsl": "^2.1.1", - "tslib": "^2.3.1" - }, - "dependencies": { - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" - } - } - }, - "@oclif/errors": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@oclif/errors/-/errors-1.3.5.tgz", - "integrity": "sha512-OivucXPH/eLLlOT7FkCMoZXiaVYf8I/w1eTAM1+gKzfhALwWTusxEx7wBmW0uzvkSg/9ovWLycPaBgJbM3LOCQ==", - "requires": { - "clean-stack": "^3.0.0", - "fs-extra": "^8.1", - "indent-string": "^4.0.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - } - } - }, - "@oclif/help": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@oclif/help/-/help-1.0.1.tgz", - "integrity": "sha512-8rsl4RHL5+vBUAKBL6PFI3mj58hjPCp2VYyXD4TAa7IMStikFfOH2gtWmqLzIlxAED2EpD0dfYwo9JJxYsH7Aw==", - "requires": { - "@oclif/config": "1.18.2", - "@oclif/errors": "1.3.5", - "chalk": "^4.1.2", - "indent-string": "^4.0.0", - "lodash": "^4.17.21", - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "widest-line": "^3.1.0", - "wrap-ansi": "^6.2.0" - }, - "dependencies": { - "@oclif/config": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/@oclif/config/-/config-1.18.2.tgz", - "integrity": "sha512-cE3qfHWv8hGRCP31j7fIS7BfCflm/BNZ2HNqHexH+fDrdF2f1D5S8VmXWLC77ffv3oDvWyvE9AZeR0RfmHCCaA==", - "requires": { - "@oclif/errors": "^1.3.3", - "@oclif/parser": "^3.8.0", - "debug": "^4.1.1", - "globby": "^11.0.1", - "is-wsl": "^2.1.1", - "tslib": "^2.0.0" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" - }, - "widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "requires": { - "string-width": "^4.0.0" - } - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - } - } - }, - "@oclif/linewrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@oclif/linewrap/-/linewrap-1.0.0.tgz", - "integrity": "sha512-Ups2dShK52xXa8w6iBWLgcjPJWjais6KPJQq3gQ/88AY6BXoTX+MIGFPrWQO1KLMiQfoTpcLnUwloN4brrVUHw==" - }, - "@oclif/parser": { - "version": "3.8.7", - "resolved": "https://registry.npmjs.org/@oclif/parser/-/parser-3.8.7.tgz", - "integrity": "sha512-b11xBmIUK+LuuwVGJpFs4LwQN2xj2cBWj2c4z1FtiXGrJ85h9xV6q+k136Hw0tGg1jQoRXuvuBnqQ7es7vO9/Q==", - "requires": { - "@oclif/errors": "^1.3.5", - "@oclif/linewrap": "^1.0.0", - "chalk": "^4.1.0", - "tslib": "^2.3.1" - }, - "dependencies": { - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" - } - } - }, - "@oclif/plugin-autocomplete": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@oclif/plugin-autocomplete/-/plugin-autocomplete-0.1.5.tgz", - "integrity": "sha512-Afchpdd8FNfx9GaU/1D9IzyfiXvjfGybgzQ6G4GTFvPO0/hLdkXX3YyYq+SnxE6/bCrhg4pleiB+GuJACmmkEA==", - "requires": { - "@oclif/command": "^1.4.31", - "@oclif/config": "^1.6.22", - "chalk": "^2.4.1", - "cli-ux": "^4.4.0", - "debug": "^3.1.0", - "fs-extra": "^6.0.1", - "moment": "^2.22.1" - }, - "dependencies": { - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==" - }, - "ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==" - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" - }, - "cli-ux": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/cli-ux/-/cli-ux-4.9.3.tgz", - "integrity": "sha512-/1owvF0SZ5Gn54cgrikJ0QskgTzeg30HGjkmjFoaHDJzAqFpuX1DBpFR8aLvsE1J5s9MgeYRENQK4BFwOag5VA==", - "requires": { - "@oclif/errors": "^1.2.2", - "@oclif/linewrap": "^1.0.0", - "@oclif/screen": "^1.0.3", - "ansi-escapes": "^3.1.0", - "ansi-styles": "^3.2.1", - "cardinal": "^2.1.1", - "chalk": "^2.4.1", - "clean-stack": "^2.0.0", - "extract-stack": "^1.0.0", - "fs-extra": "^7.0.0", - "hyperlinker": "^1.0.0", - "indent-string": "^3.2.0", - "is-wsl": "^1.1.0", - "lodash": "^4.17.11", - "password-prompt": "^1.0.7", - "semver": "^5.6.0", - "strip-ansi": "^5.0.0", - "supports-color": "^5.5.0", - "supports-hyperlinks": "^1.0.1", - "treeify": "^1.1.0", - "tslib": "^1.9.3" - }, - "dependencies": { - "fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "requires": { - "ms": "^2.1.1" - } - }, - "fs-extra": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", - "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" - }, - "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha512-BYqTHXTGUIvg7t1r4sJNKcbDZkL92nkXA8YtRpbjFHRHGDL/NtUeiBJMeE60kIFN/Mg8ESaWQvftaYMGJzQZCQ==" - }, - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==" - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@oclif/plugin-commands": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@oclif/plugin-commands/-/plugin-commands-1.3.0.tgz", - "integrity": "sha512-Qx9gJ7/aPBgo+Q/DHmGcWyxn2/0bjqmCwt/nO0lWuTZQIH3ZTqclTm68TMZLS4QnQyDGeeYK0GqZ5qJlrXD+SQ==", - "requires": { - "@oclif/command": "^1.5.4", - "@oclif/config": "^1.8.7", - "cli-ux": "^5.4.5", - "lodash": "^4.17.11" - } - }, - "@oclif/plugin-help": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-1.2.11.tgz", - "integrity": "sha512-tuzhvxxRtfLnWa96klngXBi5IwHt9S/twedCbQhl9dYIKTFMHI1BcOQcPra6ylct+M+b9jhEF5sjWLv78tB6tw==", - "requires": { - "@oclif/command": "^1.4.29", - "chalk": "^2.4.1", - "indent-string": "^3.2.0", - "lodash.template": "^4.4.0", - "string-width": "^2.1.1", - "widest-line": "^2.0.0", - "wrap-ansi": "^3.0.1" - }, - "dependencies": { - "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha512-BYqTHXTGUIvg7t1r4sJNKcbDZkL92nkXA8YtRpbjFHRHGDL/NtUeiBJMeE60kIFN/Mg8ESaWQvftaYMGJzQZCQ==" - } - } - }, - "@oclif/plugin-not-found": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@oclif/plugin-not-found/-/plugin-not-found-1.2.6.tgz", - "integrity": "sha512-cfkDub79I9EpselfU/W8FTXhslrkOgfqjaa25tyGo99dAX5UVr6BWL2wbUobsU+rUcm4HN3byzdHDcqfu6hoAw==", - "requires": { - "@oclif/color": "^0.1.2", - "@oclif/command": "1.8.11", - "cli-ux": "5.6.6", - "fast-levenshtein": "^3.0.0", - "lodash": "^4.17.21" - }, - "dependencies": { - "@oclif/command": { - "version": "1.8.11", - "resolved": "https://registry.npmjs.org/@oclif/command/-/command-1.8.11.tgz", - "integrity": "sha512-2fGLMvi6J5+oNxTaZfdWPMWY8oW15rYj0V8yLzmZBAEjfzjLqLIzJE9IlNccN1zwRqRHc1bcISSRDdxJ56IS/Q==", - "requires": { - "@oclif/config": "^1.18.2", - "@oclif/errors": "^1.3.5", - "@oclif/parser": "^3.8.6", - "@oclif/plugin-help": "3.2.14", - "debug": "^4.1.1", - "semver": "^7.3.2" - } - }, - "@oclif/plugin-help": { - "version": "3.2.14", - "resolved": "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-3.2.14.tgz", - "integrity": "sha512-NP5qmE2YfcW3MmXjcrxiqKe9Hf3G0uK/qNc0zAMYKU4crFyIsWj7dBfQVFZSb28YXGioOOpjMzG1I7VMxKF38Q==", - "requires": { - "@oclif/command": "^1.8.9", - "@oclif/config": "^1.18.2", - "@oclif/errors": "^1.3.5", - "chalk": "^4.1.2", - "indent-string": "^4.0.0", - "lodash": "^4.17.21", - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "widest-line": "^3.1.0", - "wrap-ansi": "^6.2.0" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "cli-ux": { - "version": "5.6.6", - "resolved": "https://registry.npmjs.org/cli-ux/-/cli-ux-5.6.6.tgz", - "integrity": "sha512-4wUB34zoFklcZV0z5YiOM5IqVMMt9c3TK3QYRK3dqyk3XoRC0ybiWDWHfsMDjkKrzsVTw95rXn9NrzSHbae4pg==", - "requires": { - "@oclif/command": "^1.8.9", - "@oclif/errors": "^1.3.5", - "@oclif/linewrap": "^1.0.0", - "@oclif/screen": "^1.0.4", - "ansi-escapes": "^4.3.0", - "ansi-styles": "^4.2.0", - "cardinal": "^2.1.1", - "chalk": "^4.1.0", - "clean-stack": "^3.0.0", - "cli-progress": "^3.4.0", - "extract-stack": "^2.0.0", - "fs-extra": "^8.1", - "hyperlinker": "^1.0.0", - "indent-string": "^4.0.0", - "is-wsl": "^2.2.0", - "js-yaml": "^3.13.1", - "lodash": "^4.17.21", - "natural-orderby": "^2.0.1", - "object-treeify": "^1.1.4", - "password-prompt": "^1.1.2", - "semver": "^7.3.2", - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "supports-color": "^8.1.0", - "supports-hyperlinks": "^2.1.0", - "tslib": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "extract-stack": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/extract-stack/-/extract-stack-2.0.0.tgz", - "integrity": "sha512-AEo4zm+TenK7zQorGK1f9mJ8L14hnTDi2ZQPR+Mub1NX8zimka1mXpV5LpH8x9HoUmFSHZCfLHqWvp0Y4FxxzQ==" - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "supports-hyperlinks": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", - "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", - "requires": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - } - }, - "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" - }, - "widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "requires": { - "string-width": "^4.0.0" - } - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - } - } - }, - "@oclif/plugin-plugins": { - "version": "1.10.11", - "resolved": "https://registry.npmjs.org/@oclif/plugin-plugins/-/plugin-plugins-1.10.11.tgz", - "integrity": "sha512-C9eHF10UkxwoAqRYrPW51YDuDOpDXASX4BEA++kTVcqhMQTKBQalmEJKw+gVnLl1YNmapse1ZSAcU1TrXjqykg==", - "requires": { - "@oclif/color": "^0.1.2", - "@oclif/command": "^1.8.15", - "@oclif/errors": "^1.3.5", - "chalk": "^4.1.2", - "cli-ux": "^5.6.7", - "debug": "^4.3.3", - "fs-extra": "^9.0", - "http-call": "^5.3.0", - "load-json-file": "^5.3.0", - "npm-run-path": "^4.0.1", - "semver": "^7.3.2", - "tslib": "^2.0.0", - "yarn": "^1.21.1" - }, - "dependencies": { - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" - } - } - }, - "@oclif/plugin-warn-if-update-available": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@oclif/plugin-warn-if-update-available/-/plugin-warn-if-update-available-1.7.3.tgz", - "integrity": "sha512-q8q0NIneVCwIAJzglUMsl3EbXR/H5aPDk6g+qs7uF0tToxe07SWSONoNaKPzViwRWvYChMPjL77/rXyW1HVn4A==", - "requires": { - "@oclif/command": "^1.8.6", - "@oclif/config": "^1.17.1", - "@oclif/errors": "^1.3.5", - "chalk": "^4.1.0", - "debug": "^4.1.0", - "fs-extra": "^9.0.1", - "http-call": "^5.2.2", - "lodash": "^4.17.21", - "semver": "^7.3.2" - }, - "dependencies": { - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" - } - } - }, - "@oclif/screen": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@oclif/screen/-/screen-1.0.4.tgz", - "integrity": "sha512-60CHpq+eqnTxLZQ4PGHYNwUX572hgpMHGPtTWMjdTMsAvlm69lZV/4ly6O3sAYkomo4NggGcomrDpBe34rxUqw==" - }, - "@opentelemetry/api": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.4.1.tgz", - "integrity": "sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA==" - }, - "@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" - }, - "@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" - }, - "@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" - }, - "@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" - }, - "@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "requires": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" - }, - "@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" - }, - "@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" - }, - "@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" - }, - "@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" - }, - "@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" - }, - "@stablelib/binary": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@stablelib/binary/-/binary-0.7.2.tgz", - "integrity": "sha512-J7iGppeKR112ICTZTAoALcT3yBpTrd2Z/F0wwiOUZPVPTDFTQFWHZZdYzfal9+mY1uMUPRSEnNmDuXRZbtE8Xg==", - "requires": { - "@stablelib/int": "^0.5.0" - } - }, - "@stablelib/blake2s": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/@stablelib/blake2s/-/blake2s-0.10.4.tgz", - "integrity": "sha512-IasdklC7YfXXLmVbnsxqmd66+Ki+Ysbp0BtcrNxAtrGx/HRGjkUZbSTbEa7HxFhBWIstJRcE5ExgY+RCqAiULQ==", - "requires": { - "@stablelib/binary": "^0.7.2", - "@stablelib/hash": "^0.5.0", - "@stablelib/wipe": "^0.5.0" - } - }, - "@stablelib/blake2xs": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/@stablelib/blake2xs/-/blake2xs-0.10.4.tgz", - "integrity": "sha512-1N0S4cruso/StV9TmoujPGj3RU0Cy42wlZneBWLWby7m2ssnY57l/CsYQSm03TshOoYss4hqc5kwSy5pmWAdUA==", - "requires": { - "@stablelib/blake2s": "^0.10.4", - "@stablelib/hash": "^0.5.0", - "@stablelib/wipe": "^0.5.0" - } - }, - "@stablelib/hash": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@stablelib/hash/-/hash-0.5.0.tgz", - "integrity": "sha512-rlNEBTskjKVl9f4rpRgM2GV3IrZWfNJFY5Y/2tmQtA2ozEkPLoUp9J/uJnBRnOpCsuflPW2z+pwqPbEYOPCHwQ==" - }, - "@stablelib/int": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@stablelib/int/-/int-0.5.0.tgz", - "integrity": "sha512-cuaPoxm3K14LiEICiA3iz0aeGurg75v+haZMV+xloVTw3CT25oMRJgQ6VxZ2p2cHy4kjhVI68kX4oaYrhnTm+g==" - }, - "@stablelib/wipe": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@stablelib/wipe/-/wipe-0.5.0.tgz", - "integrity": "sha512-SifvRV0rTTFR1qEF6G1hondGZyrmiM1laR8PPrO6TZwQG03hJduVbUX8uQk+Q6FdkND2Z9B8uLPyUAquQIk3iA==" - }, - "@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "requires": { - "defer-to-connect": "^1.0.1" - } - }, - "@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==" - }, - "@types/asn1js": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@types/asn1js/-/asn1js-0.0.2.tgz", - "integrity": "sha512-xtLPq140WhPqvDZDpY70rTx4qTezHs+8htbhWQGuevBRQko8FRjFSO5WVTwXOwd3W5tQRxJ7eni30fDUP2q4wQ==", - "requires": { - "@types/pvutils": "*" - } - }, - "@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==", - "requires": { - "@types/node": "*" - } - }, - "@types/command-exists": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/command-exists/-/command-exists-1.2.0.tgz", - "integrity": "sha512-ugsxEJfsCuqMLSuCD4PIJkp5Uk2z6TCMRCgYVuhRo5cYQY3+1xXTQkSlPtkpGHuvWMjS2KTeVQXxkXRACMbM6A==" - }, - "@types/country-data": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/@types/country-data/-/country-data-0.0.0.tgz", - "integrity": "sha512-lIxCk6G7AwmUagQ4gIQGxUBnvAq664prFD9nSAz6dgd1XmBXBtZABV/op+QsJsIyaP1GZsf/iXhYKHX3azSRCw==" - }, - "@types/debug": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", - "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", - "requires": { - "@types/ms": "*" - } - }, - "@types/elliptic": { - "version": "6.4.14", - "resolved": "https://registry.npmjs.org/@types/elliptic/-/elliptic-6.4.14.tgz", - "integrity": "sha512-z4OBcDAU0GVwDTuwJzQCiL6188QvZMkvoERgcVjq0/mPM8jCfdwZ3x5zQEVoL9WCAru3aG5wl3Z5Ww5wBWn7ZQ==", - "requires": { - "@types/bn.js": "*" - } - }, - "@types/ethereumjs-util": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@types/ethereumjs-util/-/ethereumjs-util-5.2.0.tgz", - "integrity": "sha512-qwQgQqXXTRv2h2AlJef+tMEszLFkCB9dWnrJYIdAwqjubERXEc/geB+S3apRw0yQyTVnsBf8r6BhlrE8vx+3WQ==", - "requires": { - "@types/bn.js": "*", - "@types/node": "*" - } - }, - "@types/google-libphonenumber": { - "version": "7.4.23", - "resolved": "https://registry.npmjs.org/@types/google-libphonenumber/-/google-libphonenumber-7.4.23.tgz", - "integrity": "sha512-C3ydakLTQa8HxtYf9ge4q6uT9krDX8smSIxmmW3oACFi5g5vv6T068PRExF7UyWbWpuYiDG8Nm24q2X5XhcZWw==" - }, - "@types/inquirer": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-6.5.0.tgz", - "integrity": "sha512-rjaYQ9b9y/VFGOpqBEXRavc3jh0a+e6evAbI31tMda8VlPaSy0AZJfXsvmIe3wklc7W6C3zCSfleuMXR7NOyXw==", - "requires": { - "@types/through": "*", - "rxjs": "^6.4.0" - } - }, - "@types/long": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", - "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" - }, - "@types/ms": { - "version": "0.7.31", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", - "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" - }, - "@types/node": { - "version": "10.14.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.10.tgz", - "integrity": "sha512-V8wj+w2YMNvGuhgl/MA5fmTxgjmVHVoasfIaxMMZJV6Y8Kk+Ydpi1z2whoShDCJ2BuNVoqH/h1hrygnBxkrw/Q==" - }, - "@types/pbkdf2": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz", - "integrity": "sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==", - "requires": { - "@types/node": "*" - } - }, - "@types/pvutils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/pvutils/-/pvutils-1.0.1.tgz", - "integrity": "sha512-OwGyloDb4Gz7cKzFt1ZfHkNXbh1gxd5706hPF5ot4cvsa2EfbqY1tz2nWtu6xWDccNSZ16p/pi027gloQu4hsQ==" - }, - "@types/randombytes": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/randombytes/-/randombytes-2.0.0.tgz", - "integrity": "sha512-bz8PhAVlwN72vqefzxa14DKNT8jK/mV66CSjwdVQM/k3Th3EPKfUtdMniwZgMedQTFuywAsfjnZsg+pEnltaMA==", - "requires": { - "@types/node": "*" - } - }, - "@types/secp256k1": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.3.tgz", - "integrity": "sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w==", - "requires": { - "@types/node": "*" - } - }, - "@types/stoppable": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/stoppable/-/stoppable-1.1.1.tgz", - "integrity": "sha512-b8N+fCADRIYYrGZOcmOR8ZNBOqhktWTB/bMUl5LvGtT201QKJZOOH5UsFyI3qtteM6ZAJbJqZoBcLqqxKIwjhw==", - "requires": { - "@types/node": "*" - } - }, - "@types/through": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz", - "integrity": "sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==", - "requires": { - "@types/node": "*" - } - }, - "@types/utf8": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@types/utf8/-/utf8-2.1.6.tgz", - "integrity": "sha512-pRs2gYF5yoKYrgSaira0DJqVg2tFuF+Qjp838xS7K+mJyY2jJzjsrl6y17GbIa4uMRogMbxs+ghNCvKg6XyNrA==" - }, - "accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "requires": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - } - }, - "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "requires": { - "debug": "4" - } - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-escapes": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", - "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", - "requires": { - "type-fest": "^0.11.0" - } - }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha512-wFUFA5bg5dviipbQQ32yOQhl6gcJaJXiHE7dvR8VYPG97+J/GNC5FKGepKdEDUFeXRzDxPF1X/Btc8L+v7oqIQ==" - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "ansicolors": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", - "integrity": "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==" - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "are-we-there-yet": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", - "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" - }, - "asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "asn1.js": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", - "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", - "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "safer-buffer": "^2.1.0" - } - }, - "asn1js": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-2.4.0.tgz", - "integrity": "sha512-PvZC0FMyMut8aOnR2jAEGSkmRtHIUYPe9amUEnGjr9TdnUmsfoOkjrvUkOEU9mzpYBR1HyO9bF+8U1cLTMMHhQ==", - "requires": { - "pvutils": "^1.1.3" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" - }, - "async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" - }, - "async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" - }, - "available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==" - }, - "aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" - }, - "axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "requires": { - "follow-redirects": "^1.14.0" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "optional": true - }, - "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==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "big-integer": { - "version": "1.6.51", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", - "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==" - }, - "bigi": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/bigi/-/bigi-1.4.2.tgz", - "integrity": "sha512-ddkU+dFIuEIW8lE7ZwdIAf2UPoM90eaprg5m3YXAVVTmKlqV/9BX4A2M8BOK2yOq6/VgZFVhK6QAxJebhlbhzw==" - }, - "bignumber.js": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", - "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==" - }, - "bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "requires": { - "file-uri-to-path": "1.0.0" - } - }, - "bip32": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.5.tgz", - "integrity": "sha512-zVY4VvJV+b2fS0/dcap/5XLlpqtgwyN8oRkuGgAS1uLOeEp0Yo6Tw2yUTozTtlrMJO3G8n4g/KX/XGFHW6Pq3g==", - "requires": { - "@types/node": "10.12.18", - "bs58check": "^2.1.1", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "tiny-secp256k1": "^1.1.3", - "typeforce": "^1.11.5", - "wif": "^2.0.6" - }, - "dependencies": { - "@types/node": { - "version": "10.12.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", - "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==" - } - } - }, - "bip39": { - "version": "git+https://github.com/bitcoinjs/bip39.git#d8ea080a18b40f301d4e2219a2991cd2417e83c2", - "from": "git+https://github.com/bitcoinjs/bip39.git#d8ea080a18b40f301d4e2219a2991cd2417e83c2", - "requires": { - "@types/node": "11.11.6", - "create-hash": "^1.1.0", - "pbkdf2": "^3.0.9", - "randombytes": "^2.0.1" - }, - "dependencies": { - "@types/node": { - "version": "11.11.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.6.tgz", - "integrity": "sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ==" - } - } - }, - "bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "blakejs": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", - "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==" - }, - "blind-threshold-bls": { - "version": "git+https://github.com/celo-org/blind-threshold-bls-wasm.git#e1e2f8a1ab5154c2f0b1c55cb0d61fbb1d907208", - "from": "git+https://github.com/celo-org/blind-threshold-bls-wasm.git#e1e2f8a" - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" - }, - "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==" - }, - "body-parser": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", - "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", - "requires": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.10.3", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "optional": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "requires": { - "fill-range": "^7.0.1" - } - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" - }, - "browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "requires": { - "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" - } - }, - "browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", - "requires": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" - } - }, - "browserify-des": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", - "requires": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "browserify-rsa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", - "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", - "requires": { - "bn.js": "^5.0.0", - "randombytes": "^2.0.1" - }, - "dependencies": { - "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==" - } - } - }, - "browserify-sign": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", - "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", - "requires": { - "bn.js": "^5.1.1", - "browserify-rsa": "^4.0.1", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "elliptic": "^6.5.3", - "inherits": "^2.0.4", - "parse-asn1": "^5.1.5", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - }, - "dependencies": { - "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==" - } - } - }, - "bs58": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", - "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", - "requires": { - "base-x": "^3.0.2" - } - }, - "bs58check": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", - "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", - "requires": { - "bs58": "^4.0.0", - "create-hash": "^1.1.0", - "safe-buffer": "^5.1.2" - } - }, - "buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" - }, - "buffer-reverse": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-reverse/-/buffer-reverse-1.0.1.tgz", - "integrity": "sha512-M87YIUBsZ6N924W57vDwT/aOu8hw7ZgdByz6ijksLjmHJELBASmYTTlNHRgjE+pTsT9oJXGaDSgqqwfdHotDUg==" - }, - "buffer-to-arraybuffer": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz", - "integrity": "sha512-3dthu5CYiVB1DEJp61FtApNnNndTckcqe4pFcLdvHtrpG+kcyekCJKg4MRiDcFW7A6AODnXB9U4dwQiCW5kzJQ==" - }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==" - }, - "bufferutil": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.6.tgz", - "integrity": "sha512-jduaYOYtnio4aIAyc6UbvPCVcgq7nYpVnucyxr6eCYg/Woad9Hf/oxxBRDnGGjPfjUm6j5O/uBWhIu4iLebFaw==", - "requires": { - "node-gyp-build": "^4.3.0" - }, - "dependencies": { - "node-gyp-build": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz", - "integrity": "sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==" - } - } - }, - "bunyan": { - "version": "1.8.12", - "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.12.tgz", - "integrity": "sha512-dmDUbGHeGcvCDLRFOscZkwx1ZO/aFz3bJOCi5nCgzdhFGPxwK+y5AcDBnqagNGlJZ7lje/l6JUEz9mQcutttdg==", - "requires": { - "dtrace-provider": "~0.8", - "moment": "^2.10.6", - "mv": "~2", - "safe-json-stringify": "~1" - } - }, - "bunyan-debug-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/bunyan-debug-stream/-/bunyan-debug-stream-2.0.0.tgz", - "integrity": "sha512-Ovl43CJ7nUwalLzdXc6E1nGIy6ift9Z/QpYXUtsjpDAg35ZFKXifKNZyfpMGuN3N7ijLLqbnxPsMMHsXDdXa9A==", - "requires": { - "colors": "^1.0.3", - "exception-formatter": "^1.0.4" - } - }, - "bunyan-gke-stackdriver": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/bunyan-gke-stackdriver/-/bunyan-gke-stackdriver-0.1.2.tgz", - "integrity": "sha512-eY5OLgAXvOvOq2YpxI0HlV5HjAcLm36Ln3PxxsztO+2GrFSgU3oXoic2LCif/heBKoyOZdMyXKWF5dvswSOS6w==" - }, - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" - }, - "cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "dependencies": { - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "requires": { - "pump": "^3.0.0" - } - }, - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" - } - } - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "cardinal": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", - "integrity": "sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==", - "requires": { - "ansicolors": "~0.3.2", - "redeyed": "~2.1.0" - } - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" - }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, - "cids": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/cids/-/cids-0.7.5.tgz", - "integrity": "sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA==", - "requires": { - "buffer": "^5.5.0", - "class-is": "^1.1.0", - "multibase": "~0.6.0", - "multicodec": "^1.0.0", - "multihashes": "~0.4.15" - }, - "dependencies": { - "multicodec": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", - "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", - "requires": { - "buffer": "^5.6.0", - "varint": "^5.0.0" - } - } - } - }, - "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==", - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "class-is": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/class-is/-/class-is-1.1.0.tgz", - "integrity": "sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw==" - }, - "clean-stack": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-3.0.1.tgz", - "integrity": "sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg==", - "requires": { - "escape-string-regexp": "4.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" - } - } - }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-progress": { - "version": "3.11.2", - "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.11.2.tgz", - "integrity": "sha512-lCPoS6ncgX4+rJu5bS3F/iCz17kZ9MPZ6dpuTtI0KXKABkhyXIdYB3Inby1OpaGti3YlI3EeEkM9AuWpelJrVA==", - "requires": { - "string-width": "^4.2.3" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - } - } - }, - "cli-table": { - "version": "0.3.11", - "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.11.tgz", - "integrity": "sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ==", - "requires": { - "colors": "1.0.3" - }, - "dependencies": { - "colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==" - } - } - }, - "cli-ux": { - "version": "5.6.7", - "resolved": "https://registry.npmjs.org/cli-ux/-/cli-ux-5.6.7.tgz", - "integrity": "sha512-dsKAurMNyFDnO6X1TiiRNiVbL90XReLKcvIq4H777NMqXGBxBws23ag8ubCJE97vVZEgWG2eSUhsyLf63Jv8+g==", - "requires": { - "@oclif/command": "^1.8.15", - "@oclif/errors": "^1.3.5", - "@oclif/linewrap": "^1.0.0", - "@oclif/screen": "^1.0.4", - "ansi-escapes": "^4.3.0", - "ansi-styles": "^4.2.0", - "cardinal": "^2.1.1", - "chalk": "^4.1.0", - "clean-stack": "^3.0.0", - "cli-progress": "^3.4.0", - "extract-stack": "^2.0.0", - "fs-extra": "^8.1", - "hyperlinker": "^1.0.0", - "indent-string": "^4.0.0", - "is-wsl": "^2.2.0", - "js-yaml": "^3.13.1", - "lodash": "^4.17.21", - "natural-orderby": "^2.0.1", - "object-treeify": "^1.1.4", - "password-prompt": "^1.1.2", - "semver": "^7.3.2", - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "supports-color": "^8.1.0", - "supports-hyperlinks": "^2.1.0", - "tslib": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "extract-stack": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/extract-stack/-/extract-stack-2.0.0.tgz", - "integrity": "sha512-AEo4zm+TenK7zQorGK1f9mJ8L14hnTDi2ZQPR+Mub1NX8zimka1mXpV5LpH8x9HoUmFSHZCfLHqWvp0Y4FxxzQ==" - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "requires": { - "has-flag": "^4.0.0" - } - }, - "supports-hyperlinks": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", - "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", - "requires": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "dependencies": { - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" - } - } - }, - "cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==" - }, - "clone-response": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", - "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", - "requires": { - "mimic-response": "^1.0.0" - } - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==" - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "command-exists": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", - "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==" - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "optional": true - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" - }, - "content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "requires": { - "safe-buffer": "5.2.1" - } - }, - "content-hash": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/content-hash/-/content-hash-2.5.2.tgz", - "integrity": "sha512-FvIQKy0S1JaWV10sMsA7TRx8bpU+pqPkhbsfvOJAdjRXvYxEckAwQWGwtRjiaJfh+E0DvcWUGqcdjwMGFjsSdw==", - "requires": { - "cids": "^0.7.1", - "multicodec": "^0.5.5", - "multihashes": "^0.4.15" - } - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "cookiejar": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", - "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==" - }, - "core-js": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", - "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" - }, - "cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "requires": { - "object-assign": "^4", - "vary": "^1" - } - }, - "country-data": { - "version": "0.0.31", - "resolved": "https://registry.npmjs.org/country-data/-/country-data-0.0.31.tgz", - "integrity": "sha512-YqlY/i6ikZwoBFfdjK+hJTGaBdTgDpXLI15MCj2UsXZ2cPBb+Kx86AXmDH7PRGt0LUleck0cCgNdWeIhfbcxkQ==", - "requires": { - "currency-symbol-map": "~2", - "underscore": ">1.4.4" - } - }, - "create-ecdh": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", - "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", - "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.5.3" - } - }, - "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==", - "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "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==", - "requires": { - "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" - } - }, - "cross-fetch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", - "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", - "requires": { - "node-fetch": "2.6.7" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - } - } - }, - "crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "requires": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" - } - }, - "currency-symbol-map": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/currency-symbol-map/-/currency-symbol-map-2.2.0.tgz", - "integrity": "sha512-fPZJ3jqM68+AAgqQ7UaGbgHL/39rp6l7GyqS2k1HJPu/kpS8D07x/+Uup6a9tCUKIlOFcRrDCf1qxSt8jnI5BA==" - }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "requires": { - "ms": "2.1.2" - } - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==" - }, - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==", - "requires": { - "mimic-response": "^1.0.0" - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" - }, - "defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" - }, - "define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", - "requires": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - }, - "des.js": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", - "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", - "requires": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" - }, - "detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", - "optional": true - }, - "diffie-hellman": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", - "requires": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" - } - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "requires": { - "path-type": "^4.0.0" - } - }, - "dom-storage": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/dom-storage/-/dom-storage-2.1.0.tgz", - "integrity": "sha512-g6RpyWXzl0RR6OTElHKBl7nwnK87GUyZMYC7JWsB/IA73vpqK2K6LT39x4VepLxlSsWBFrPVLnsSR5Jyty0+2Q==" - }, - "dom-walk": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", - "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==" - }, - "dotenv": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", - "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==" - }, - "dtrace-provider": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz", - "integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==", - "optional": true, - "requires": { - "nan": "^2.14.0" - } - }, - "duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" - }, - "duplexer3": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", - "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==" - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", - "requires": { - "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" - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "requires": { - "once": "^1.4.0" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", - "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==", - "requires": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.1", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "regexp.prototype.flags": "^1.4.3", - "string.prototype.trimend": "^1.0.5", - "string.prototype.trimstart": "^1.0.5", - "unbox-primitive": "^1.0.2" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "es5-ext": { - "version": "0.10.62", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", - "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", - "requires": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "next-tick": "^1.1.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" - }, - "eth-ens-namehash": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz", - "integrity": "sha512-VWEI1+KJfz4Km//dadyvBBoBeSQ0MHTXPvr8UIXiLW6IanxvAV+DmlZAijZwAyggqGUfwQBeHf7tc9wzc1piSw==", - "requires": { - "idna-uts46-hx": "^2.3.1", - "js-sha3": "^0.5.7" - }, - "dependencies": { - "js-sha3": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", - "integrity": "sha512-GII20kjaPX0zJ8wzkTbNDYMY7msuZcTWk8S5UOh6806Jq/wz1J8/bnr8uGU0DAUmYDjj2Mr4X1cW8v/GLYnR+g==" - } - } - }, - "eth-lib": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.8.tgz", - "integrity": "sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw==", - "requires": { - "bn.js": "^4.11.6", - "elliptic": "^6.4.0", - "xhr-request-promise": "^0.1.2" - } - }, - "ethereum-bloom-filters": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz", - "integrity": "sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA==", - "requires": { - "js-sha3": "^0.8.0" - } - }, - "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==", - "requires": { - "@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" - } - }, - "ethereumjs-common": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/ethereumjs-common/-/ethereumjs-common-1.5.2.tgz", - "integrity": "sha512-hTfZjwGX52GS2jcVO6E2sx4YuFnf0Fhp5ylo4pEPhEffNln7vS59Hr5sLnp3/QCazFLluuBZ+FZ6J5HTp0EqCA==" - }, - "ethereumjs-tx": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ethereumjs-tx/-/ethereumjs-tx-2.1.2.tgz", - "integrity": "sha512-zZEK1onCeiORb0wyCXUvg94Ve5It/K6GD1K+26KfFKodiBiS6d9lfCXlUKGBBdQ+bv7Day+JK0tj1K+BeNFRAw==", - "requires": { - "ethereumjs-common": "^1.5.0", - "ethereumjs-util": "^6.0.0" - }, - "dependencies": { - "@types/bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", - "requires": { - "@types/node": "*" - } - }, - "ethereumjs-util": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", - "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", - "requires": { - "@types/bn.js": "^4.11.3", - "bn.js": "^4.11.0", - "create-hash": "^1.1.2", - "elliptic": "^6.5.2", - "ethereum-cryptography": "^0.1.3", - "ethjs-util": "0.1.6", - "rlp": "^2.2.3" - } - } - } - }, - "ethereumjs-util": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", - "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", - "requires": { - "bn.js": "^4.11.0", - "create-hash": "^1.1.2", - "elliptic": "^6.5.2", - "ethereum-cryptography": "^0.1.3", - "ethjs-util": "^0.1.3", - "rlp": "^2.0.0", - "safe-buffer": "^5.1.1" - } - }, - "ethjs-unit": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", - "integrity": "sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw==", - "requires": { - "bn.js": "4.11.6", - "number-to-bn": "1.7.0" - }, - "dependencies": { - "bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==" - } - } - }, - "ethjs-util": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", - "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", - "requires": { - "is-hex-prefixed": "1.0.0", - "strip-hex-prefix": "1.0.0" - } - }, - "event-stream": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-4.0.1.tgz", - "integrity": "sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA==", - "requires": { - "duplexer": "^0.1.1", - "from": "^0.1.7", - "map-stream": "0.0.7", - "pause-stream": "^0.0.11", - "split": "^1.0.1", - "stream-combiner": "^0.2.2", - "through": "^2.3.8" - } - }, - "eventemitter3": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", - "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==" - }, - "events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" - }, - "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==", - "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" - } - }, - "exception-formatter": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/exception-formatter/-/exception-formatter-1.0.7.tgz", - "integrity": "sha512-zV45vEsjytJrwfGq6X9qd1Ll56cW4NC2mhCO6lqwMk4ZpA1fZ6C3UiaQM/X7if+7wZFmCgss3ahp9B/uVFuLRw==", - "requires": { - "colors": "^1.0.3" - } - }, - "expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==" - }, - "express": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", - "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", - "requires": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.0", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.10.3", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, - "ext": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", - "integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==", - "requires": { - "type": "^2.5.0" - }, - "dependencies": { - "type": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", - "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" - } - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, - "extract-stack": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/extract-stack/-/extract-stack-1.0.0.tgz", - "integrity": "sha512-M5Ge0JIrn12EtIVpje2G+hI5X78hmX4UDzynZ7Vnp1MiPSqleEonmgr2Rh59eygEEgq3YJ1GDP96rnM8tnVg/Q==" - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==" - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "fast-levenshtein": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-3.0.0.tgz", - "integrity": "sha512-hKKNajm46uNmTlhHSyZkmToAc56uZJwYq7yrciZjqOxnlfQwERDQJmHPUp7m1m9wx8vgOe8IaCKZ5Kv2k1DdCQ==", - "requires": { - "fastest-levenshtein": "^1.0.7" - } - }, - "fastest-levenshtein": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", - "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==" - }, - "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "requires": { - "reusify": "^1.0.4" - } - }, - "faye-websocket": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", - "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==", - "requires": { - "websocket-driver": ">=0.5.1" - } - }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "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==" - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, - "firebase": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/firebase/-/firebase-7.24.0.tgz", - "integrity": "sha512-j6jIyGFFBlwWAmrlUg9HyQ/x+YpsPkc/TTkbTyeLwwAJrpAmmEHNPT6O9xtAnMV4g7d3RqLL/u9//aZlbY4rQA==", - "requires": { - "@firebase/analytics": "0.6.0", - "@firebase/app": "0.6.11", - "@firebase/app-types": "0.6.1", - "@firebase/auth": "0.15.0", - "@firebase/database": "0.6.13", - "@firebase/firestore": "1.18.0", - "@firebase/functions": "0.5.1", - "@firebase/installations": "0.4.17", - "@firebase/messaging": "0.7.1", - "@firebase/performance": "0.4.2", - "@firebase/polyfill": "0.3.36", - "@firebase/remote-config": "0.1.28", - "@firebase/storage": "0.3.43", - "@firebase/util": "0.3.2" - } - }, - "follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" - }, - "for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "requires": { - "is-callable": "^1.1.3" - } - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==" - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" - }, - "fp-ts": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-2.1.1.tgz", - "integrity": "sha512-YcWhMdDCFCja0MmaDroTgNu+NWWrrnUEn92nvDgrtVy9Z71YFnhNVIghoHPt8gs82ijoMzFGeWKvArbyICiJgw==" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" - }, - "from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==" - }, - "fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" - }, - "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "requires": { - "minipass": "^2.6.0" - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - } - }, - "functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==", - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" - }, - "get-intrinsic": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", - "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - } - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "requires": { - "pump": "^3.0.0" - } - }, - "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - } - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" - }, - "glob": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", - "integrity": "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==", - "optional": true, - "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "requires": { - "is-glob": "^4.0.1" - } - }, - "global": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", - "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", - "requires": { - "min-document": "^2.19.0", - "process": "^0.11.10" - } - }, - "google-libphonenumber": { - "version": "3.2.32", - "resolved": "https://registry.npmjs.org/google-libphonenumber/-/google-libphonenumber-3.2.32.tgz", - "integrity": "sha512-mcNgakausov/B/eTgVeX8qc8IwWjRrupk9UzZZ/QDEvdh5fAjE7Aa211bkZpZj42zKkeS6MTT8avHUwjcLxuGQ==" - }, - "got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "requires": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - } - }, - "graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==" - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "requires": { - "get-intrinsic": "^1.1.1" - } - }, - "has-symbol-support-x": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", - "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==" - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" - }, - "has-to-string-tag-x": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", - "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", - "requires": { - "has-symbol-support-x": "^1.4.1" - } - }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "requires": { - "has-symbols": "^1.0.2" - } - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" - }, - "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==", - "requires": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - } - }, - "hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" - }, - "http-call": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/http-call/-/http-call-5.3.0.tgz", - "integrity": "sha512-ahwimsC23ICE4kPl9xTBjKB4inbRaeLyZeRunC/1Jy/Z6X8tv22MEAjK+KBOMSVLaqXPTTmd8638waVIKLGx2w==", - "requires": { - "content-type": "^1.0.4", - "debug": "^4.1.1", - "is-retry-allowed": "^1.1.0", - "is-stream": "^2.0.0", - "parse-json": "^4.0.0", - "tunnel-agent": "^0.6.0" - }, - "dependencies": { - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" - } - } - }, - "http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - } - }, - "http-https": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/http-https/-/http-https-1.0.0.tgz", - "integrity": "sha512-o0PWwVCSp3O0wS6FvNr6xfBCHgt0m1tvPLFOCc2iFDKTRAXhB7m8klDf7ErowFH8POa6dVdGatKU5I1YYwzUyg==" - }, - "http-parser-js": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", - "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" - }, - "http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", - "requires": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "requires": { - "agent-base": "6", - "debug": "4" - } - }, - "humanize-duration": { - "version": "3.27.2", - "resolved": "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.27.2.tgz", - "integrity": "sha512-A15OmA3FLFRnehvF4ZMocsxTZYvHq4ze7L+AgR1DeHw0xC9vMd4euInY83uqGU9/XXKNnVIEeKc1R8G8nKqtzg==" - }, - "hyperlinker": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hyperlinker/-/hyperlinker-1.0.0.tgz", - "integrity": "sha512-Ty8UblRWFEcfSuIaajM34LdPXIhbs1ajEX/BBPv24J+enSVaEVY63xQ6lTO9VRYS5LAoghIG0IDJ+p+IPzKUQQ==" - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "idb": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/idb/-/idb-3.0.2.tgz", - "integrity": "sha512-+FLa/0sTXqyux0o6C+i2lOR0VoS60LU/jzUo5xjfY6+7sEEgy4Gz1O7yFBXvjd7N0NyIGWIRg8DcQSLEG+VSPw==" - }, - "idna-uts46-hx": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz", - "integrity": "sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA==", - "requires": { - "punycode": "2.1.0" - }, - "dependencies": { - "punycode": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", - "integrity": "sha512-Yxz2kRwT90aPiWEMHVYnEf4+rhwF1tBmmZ4KepCP+Wkium9JxtWnUm1nqGwpiAHr/tnTSeHqr3wb++jgSkXjhA==" - } - } - }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" - }, - "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==" - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, - "inquirer": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", - "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.19", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.6.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - } - } - }, - "internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "requires": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - } - }, - "io-ts": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/io-ts/-/io-ts-2.0.1.tgz", - "integrity": "sha512-RezD+WcCfW4VkMkEcQWL/Nmy/nqsWTvTYg7oUmTGzglvSSV2P9h2z1PVeREPFf0GWNzruYleAt1XCMQZSg1xxQ==" - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - }, - "is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" - }, - "is-base64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-base64/-/is-base64-1.1.0.tgz", - "integrity": "sha512-Nlhg7Z2dVC4/PTvIFkgVVNvPHSO2eR/Yd0XzhGiXCXEvWnptXlXa/clQ8aePPiMuxEGcWfzWbGw2Fe3d+Y3v1g==" - }, - "is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "requires": { - "has-bigints": "^1.0.1" - } - }, - "is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==" - }, - "is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "is-function": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", - "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==" - }, - "is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-hex-prefixed": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", - "integrity": "sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==" - }, - "is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" - }, - "is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.2.tgz", - "integrity": "sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA==" - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==" - }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-retry-allowed": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", - "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==" - }, - "is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==" - }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "requires": { - "has-symbols": "^1.0.2" - } - }, - "is-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.9.tgz", - "integrity": "sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A==", - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-abstract": "^1.20.0", - "for-each": "^0.3.3", - "has-tostringtag": "^1.0.0" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" - }, - "is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "requires": { - "is-docker": "^2.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" - }, - "isurl": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", - "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", - "requires": { - "has-to-string-tag-x": "^1.2.0", - "is-object": "^1.0.1" - } - }, - "js-sha3": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", - "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" - }, - "json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==" - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" - }, - "json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "jsonwebtoken": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", - "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", - "requires": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^5.6.0" - }, - "dependencies": { - "jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "requires": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - } - } - }, - "jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - } - }, - "jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "jws": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", - "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", - "requires": { - "jwa": "^2.0.0", - "safe-buffer": "^5.0.1" - }, - "dependencies": { - "jwa": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", - "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - } - } - }, - "keccak": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.2.tgz", - "integrity": "sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ==", - "requires": { - "node-addon-api": "^2.0.0", - "node-gyp-build": "^4.2.0", - "readable-stream": "^3.6.0" - } - }, - "keytar": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz", - "integrity": "sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==", - "optional": true, - "requires": { - "node-addon-api": "^4.3.0", - "prebuild-install": "^7.0.1" - }, - "dependencies": { - "node-addon-api": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", - "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", - "optional": true - } - } - }, - "keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "requires": { - "json-buffer": "3.0.0" - } - }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" - }, - "load-json-file": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz", - "integrity": "sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==", - "requires": { - "graceful-fs": "^4.1.15", - "parse-json": "^4.0.0", - "pify": "^4.0.1", - "strip-bom": "^3.0.0", - "type-fest": "^0.3.0" - }, - "dependencies": { - "type-fest": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", - "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==" - } - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash._reinterpolate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", - "integrity": "sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==" - }, - "lodash.assign": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", - "integrity": "sha512-hFuH8TY+Yji7Eja3mGiuAxBqLagejScbG8GbG0j6o9vzn0YL14My+ktnqtZgFTosKymC9/44wP6s7xyuLfnClw==" - }, - "lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" - }, - "lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" - }, - "lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" - }, - "lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" - }, - "lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" - }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" - }, - "lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" - }, - "lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" - }, - "lodash.template": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", - "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", - "requires": { - "lodash._reinterpolate": "^3.0.0", - "lodash.templatesettings": "^4.0.0" - } - }, - "lodash.templatesettings": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", - "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", - "requires": { - "lodash._reinterpolate": "^3.0.0" - } - }, - "long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" - }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" - } - }, - "map-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", - "integrity": "sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==" - }, - "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==", - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" - }, - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "requires": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" - } - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "requires": { - "mime-db": "1.52.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, - "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" - }, - "min-document": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", - "integrity": "sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==", - "requires": { - "dom-walk": "^0.1.0" - } - }, - "mingo": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/mingo/-/mingo-1.3.3.tgz", - "integrity": "sha512-Y4wGTD/M7AMqF8QxKaBGps+axq/Z48hdtRAeiKtInkEXMLzUWUwT0OPDzrB26xrav9GF1AOYJfwVWPcLwnkgTA==" - }, - "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==" - }, - "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==" - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "optional": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" - }, - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - }, - "dependencies": { - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - } - } - }, - "minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "requires": { - "minipass": "^2.9.0" - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" - }, - "mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" - }, - "mkdirp-promise": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz", - "integrity": "sha512-Hepn5kb1lJPtVW84RFT40YG1OddBNTOVUZR2bzQUHc+Z03en8/3uX0+060JDhcEzyO08HmipsN9DcnFMxhIL9w==", - "requires": { - "mkdirp": "*" - } - }, - "mock-fs": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.14.0.tgz", - "integrity": "sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw==" - }, - "moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "msal": { - "version": "1.4.18", - "resolved": "https://registry.npmjs.org/msal/-/msal-1.4.18.tgz", - "integrity": "sha512-QyWMWrZqpwtK6LEqhwtbikxIWqA1EOcdMvDeIDjIXdGU29wM4orwq538sPe1+JfKDIgPmJj1Fgi5B7luaw/IyA==", - "requires": { - "tslib": "^1.9.3" - } - }, - "multibase": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.6.1.tgz", - "integrity": "sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw==", - "requires": { - "base-x": "^3.0.8", - "buffer": "^5.5.0" - } - }, - "multicodec": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-0.5.7.tgz", - "integrity": "sha512-PscoRxm3f+88fAtELwUnZxGDkduE2HD9Q6GHUOywQLjOGT/HAdhjLDYNZ1e7VR0s0TP0EwZ16LNUTFpoBGivOA==", - "requires": { - "varint": "^5.0.0" - } - }, - "multihashes": { - "version": "0.4.21", - "resolved": "https://registry.npmjs.org/multihashes/-/multihashes-0.4.21.tgz", - "integrity": "sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw==", - "requires": { - "buffer": "^5.5.0", - "multibase": "^0.7.0", - "varint": "^5.0.0" - }, - "dependencies": { - "multibase": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.7.0.tgz", - "integrity": "sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg==", - "requires": { - "base-x": "^3.0.8", - "buffer": "^5.5.0" - } - } - } - }, - "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" - }, - "mv": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", - "integrity": "sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==", - "optional": true, - "requires": { - "mkdirp": "~0.5.1", - "ncp": "~2.0.0", - "rimraf": "~2.4.0" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "optional": true, - "requires": { - "minimist": "^1.2.6" - } - } - } - }, - "nan": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz", - "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==" - }, - "nano-json-stream-parser": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz", - "integrity": "sha512-9MqxMH/BSJC7dnLsEMPyfN5Dvoo49IsPFYMcHw3Bcfc2kN0lpHRBSzlMSVx4HGyJ7s9B31CyBTVehWJoQ8Ctew==" - }, - "napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" - }, - "natural-orderby": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/natural-orderby/-/natural-orderby-2.0.3.tgz", - "integrity": "sha512-p7KTHxU0CUrcOXe62Zfrb5Z13nLvPhSWR/so3kFulUQU0sgUll2Z0LwpsLN351eOOD+hRGu/F1g+6xDfPeD++Q==" - }, - "ncp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", - "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", - "optional": true - }, - "negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" - }, - "next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" - }, - "node-abi": { - "version": "3.40.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.40.0.tgz", - "integrity": "sha512-zNy02qivjjRosswoYmPi8hIKJRr8MpQyeKT6qlcq/OnOgA3Rhoae+IYOqsM9V5+JnHWmxKnWOT2GxvtqdtOCXA==", - "optional": true, - "requires": { - "semver": "^7.3.5" - } - }, - "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-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "requires": { - "whatwg-url": "^5.0.0" - } - }, - "node-gyp-build": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.2.3.tgz", - "integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==" - }, - "node-hid": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/node-hid/-/node-hid-1.3.2.tgz", - "integrity": "sha512-dRFRpCAoZvMSVN+GYjtbA7D86GPBEAvyv262CV60NF18s/KgtKAGSqHUm677KZtf2rUvBkbccp9QhCXy/0gBmg==", - "requires": { - "bindings": "^1.5.0", - "nan": "^2.14.0", - "node-abi": "^2.19.3", - "prebuild-install": "^6.0.0" - }, - "dependencies": { - "decompress-response": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", - "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", - "requires": { - "mimic-response": "^2.0.0" - } - }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==" - }, - "mimic-response": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", - "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==" - }, - "node-abi": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz", - "integrity": "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==", - "requires": { - "semver": "^5.4.1" - } - }, - "prebuild-install": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.4.tgz", - "integrity": "sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ==", - "requires": { - "detect-libc": "^1.0.3", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^2.21.0", - "npmlog": "^4.0.1", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^3.0.3", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "simple-get": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", - "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", - "requires": { - "decompress-response": "^4.2.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - } - } - }, - "normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==" - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "requires": { - "path-key": "^3.0.0" - }, - "dependencies": { - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" - } - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==" - }, - "number-to-bn": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", - "integrity": "sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==", - "requires": { - "bn.js": "4.11.6", - "strip-hex-prefix": "1.0.0" - }, - "dependencies": { - "bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==" - } - } - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" - }, - "object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - }, - "object-treeify": { - "version": "1.1.33", - "resolved": "https://registry.npmjs.org/object-treeify/-/object-treeify-1.1.33.tgz", - "integrity": "sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A==" - }, - "object.assign": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.3.tgz", - "integrity": "sha512-ZFJnX3zltyjcYJL0RoCJuzb+11zWGyaDbjgxZbdV7rFEcHQuYxrZqhow67aA7xpes6LhojyFDaBKAFfogQrikA==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - } - }, - "oboe": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/oboe/-/oboe-2.1.5.tgz", - "integrity": "sha512-zRFWiF+FoicxEs3jNI/WYUrVEgA7DeET/InK0XQuudGHRg8iIob3cNPrJTKaz4004uaA9Pbe+Dwa8iluhjLZWA==", - "requires": { - "http-https": "^1.0.0" - } - }, - "on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "requires": { - "ee-first": "1.1.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "open": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", - "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", - "requires": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==" - }, - "p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==" - }, - "p-timeout": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", - "integrity": "sha512-gb0ryzr+K2qFqFv6qi3khoeqMZF/+ajxQipEF6NteZVnvz9tzdsfAVj3lYtn1gAXvH5lfLwfxEII799gt/mRIA==", - "requires": { - "p-finally": "^1.0.0" - } - }, - "parse-asn1": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", - "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", - "requires": { - "asn1.js": "^5.2.0", - "browserify-aes": "^1.0.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" - } - }, - "parse-headers": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.5.tgz", - "integrity": "sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA==" - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "password-prompt": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/password-prompt/-/password-prompt-1.1.2.tgz", - "integrity": "sha512-bpuBhROdrhuN3E7G/koAju0WjVw9/uQOG5Co5mokNj0MiOSBVZS1JTwM4zl55hu0WFmIEFvO9cU9sJQiBIYeIA==", - "requires": { - "ansi-escapes": "^3.1.0", - "cross-spawn": "^6.0.5" - }, - "dependencies": { - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==" - } - } - }, - "path": { - "version": "0.12.7", - "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", - "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", - "requires": { - "process": "^0.11.1", - "util": "^0.10.3" - }, - "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" - }, - "util": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", - "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", - "requires": { - "inherits": "2.0.3" - } - } - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "optional": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==" - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" - }, - "pause-stream": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", - "requires": { - "through": "~2.3" - } - }, - "pbkdf2": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", - "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", - "requires": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" - }, - "prebuild-install": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", - "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", - "optional": true, - "requires": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "dependencies": { - "decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "optional": true, - "requires": { - "mimic-response": "^3.1.0" - } - }, - "mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "optional": true - }, - "simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "optional": true, - "requires": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - } - } - }, - "prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==" - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "promise-polyfill": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.1.3.tgz", - "integrity": "sha512-MG5r82wBzh7pSKDRa9y+vllNHz3e3d4CNj1PQE4BQYxLme0gKYYBm9YENq+UkEikyZ0XbiGWxYlVw3Rl9O/U8g==" - }, - "prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - } - }, - "protobufjs": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.0.0.tgz", - "integrity": "sha512-ffNIEm+quOcYtQvHdW406v1NQmZSuqVklxsXk076BtuFnlYZfigLU+JOMrTD8TUOyqHYbRI/fSVNvgd25YeN3w==", - "requires": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/long": "^4.0.1", - "@types/node": ">=13.7.0", - "long": "^5.0.0" - }, - "dependencies": { - "@types/node": { - "version": "18.7.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.1.tgz", - "integrity": "sha512-GKX1Qnqxo4S+Z/+Z8KKPLpH282LD7jLHWJcVryOflnsnH+BtSDfieR6ObwBMwpnNws0bUK8GI7z0unQf9bARNQ==" - }, - "long": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.0.tgz", - "integrity": "sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w==" - } - } - }, - "proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "requires": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - } - }, - "psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" - }, - "public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", - "requires": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "pvutils": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.3.tgz", - "integrity": "sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==" - }, - "qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", - "requires": { - "side-channel": "^1.0.4" - } - }, - "query-string": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", - "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", - "requires": { - "decode-uri-component": "^0.2.0", - "object-assign": "^4.1.0", - "strict-uri-encode": "^1.0.0" - } - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "requires": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "requires": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "redeyed": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", - "integrity": "sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==", - "requires": { - "esprima": "~4.0.0" - } - }, - "regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" - } - }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==" - } - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" - }, - "responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==", - "requires": { - "lowercase-keys": "^1.0.0" - } - }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" - }, - "rimraf": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", - "integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==", - "optional": true, - "requires": { - "glob": "^6.0.1" - } - }, - "ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "rlp": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", - "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==", - "requires": { - "bn.js": "^5.2.0" - }, - "dependencies": { - "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==" - } - } - }, - "run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==" - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "requires": { - "tslib": "^1.9.0" - } - }, - "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==" - }, - "safe-json-stringify": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", - "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", - "optional": true - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "save": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/save/-/save-2.5.0.tgz", - "integrity": "sha512-xiVLpKVbx8EmW0HDkNRjYL271OnIRCo8VGWAEq6/K+E0dgNrwKV2xvKXdfPj6HGYA6l760800LyewSY3ooljCg==", - "requires": { - "async": "^3.2.2", - "event-stream": "^4.0.1", - "lodash.assign": "^4.2.0", - "mingo": "1" - } - }, - "scrypt-js": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", - "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" - }, - "secp256k1": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.2.tgz", - "integrity": "sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg==", - "requires": { - "elliptic": "^6.5.2", - "node-addon-api": "^2.0.0", - "node-gyp-build": "^4.2.0" - } - }, - "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "requires": { - "lru-cache": "^6.0.0" - } - }, - "send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "requires": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - } - } - }, - "serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - } - }, - "servify": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/servify/-/servify-0.1.12.tgz", - "integrity": "sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw==", - "requires": { - "body-parser": "^1.16.0", - "cors": "^2.8.1", - "express": "^4.14.0", - "request": "^2.79.0", - "xhr": "^2.3.3" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" - }, - "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==" - }, - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==" - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" - }, - "simple-get": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.2.tgz", - "integrity": "sha512-Ijd/rV5o+mSBBs4F/x9oDPtTx9Zb6X9brmnXvMW4J7IR15ngi9q5xxqWBKU744jTZiaXtxaPL7uHG6vtN8kUkw==", - "requires": { - "decompress-response": "^3.3.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" - }, - "split": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", - "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", - "requires": { - "through": "2" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" - }, - "sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" - }, - "stoppable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", - "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==" - }, - "stream-combiner": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", - "integrity": "sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==", - "requires": { - "duplexer": "~0.1.1", - "through": "~2.3.4" - } - }, - "strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==" - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==" - } - } - }, - "string.prototype.trimend": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", - "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" - } - }, - "string.prototype.trimstart": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", - "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" - } - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==" - }, - "strip-hex-prefix": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", - "integrity": "sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==", - "requires": { - "is-hex-prefixed": "1.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==" - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - }, - "supports-hyperlinks": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-1.0.1.tgz", - "integrity": "sha512-HHi5kVSefKaJkGYXbDuKbUGRVxqnWGn3J2e39CYcNJEfWciGq2zYtOhXLTlvrOZW1QU7VX67w7fMmWafHX9Pfw==", - "requires": { - "has-flag": "^2.0.0", - "supports-color": "^5.0.0" - }, - "dependencies": { - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha512-P+1n3MnwjR/Epg9BBo1KT8qbye2g2Ou4sFumihwt6I4tsUX7jnLcX4BTOSKg/B1ZrIYMN9FcEnG4x5a7NB8Eng==" - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - }, - "dependencies": { - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" - } - } - } - } - }, - "swarm-js": { - "version": "0.1.40", - "resolved": "https://registry.npmjs.org/swarm-js/-/swarm-js-0.1.40.tgz", - "integrity": "sha512-yqiOCEoA4/IShXkY3WKwP5PvZhmoOOD8clsKA7EEcRILMkTEYHCQ21HDCAcVpmIxZq4LyZvWeRJ6quIyHk1caA==", - "requires": { - "bluebird": "^3.5.0", - "buffer": "^5.0.5", - "eth-lib": "^0.1.26", - "fs-extra": "^4.0.2", - "got": "^7.1.0", - "mime-types": "^2.1.16", - "mkdirp-promise": "^5.0.1", - "mock-fs": "^4.1.0", - "setimmediate": "^1.0.5", - "tar": "^4.0.2", - "xhr-request": "^1.0.1" - }, - "dependencies": { - "eth-lib": { - "version": "0.1.29", - "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.1.29.tgz", - "integrity": "sha512-bfttrr3/7gG4E02HoWTDUcDDslN003OlOoBxk9virpAZQ1ja/jDgwkWB8QfJF7ojuEowrqy+lzp9VcJG7/k5bQ==", - "requires": { - "bn.js": "^4.11.6", - "elliptic": "^6.4.0", - "nano-json-stream-parser": "^0.1.2", - "servify": "^0.1.12", - "ws": "^3.0.0", - "xhr-request-promise": "^0.1.2" - } - }, - "fs-extra": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", - "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==" - }, - "got": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", - "integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==", - "requires": { - "decompress-response": "^3.2.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-plain-obj": "^1.1.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "isurl": "^1.0.0-alpha5", - "lowercase-keys": "^1.0.0", - "p-cancelable": "^0.3.0", - "p-timeout": "^1.1.1", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "url-parse-lax": "^1.0.0", - "url-to-options": "^1.0.1" - } - }, - "p-cancelable": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", - "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==" - }, - "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg==" - }, - "url-parse-lax": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", - "integrity": "sha512-BVA4lR5PIviy2PMseNd2jbFQ+jwSwQGdJejf5ctd1rEXt0Ypd7yanUK9+lYechVlN5VaTJGsu2U/3MDDu6KgBA==", - "requires": { - "prepend-http": "^1.0.1" - } - } - } - }, - "tar": { - "version": "4.4.19", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", - "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", - "requires": { - "chownr": "^1.1.4", - "fs-minipass": "^1.2.7", - "minipass": "^2.9.0", - "minizlib": "^1.3.3", - "mkdirp": "^0.5.5", - "safe-buffer": "^5.2.1", - "yallist": "^3.1.1" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "requires": { - "minimist": "^1.2.6" - } - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - } - } - }, - "tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "requires": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "requires": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - } - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" - }, - "timed-out": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA==" - }, - "tiny-secp256k1": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz", - "integrity": "sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA==", - "requires": { - "bindings": "^1.3.0", - "bn.js": "^4.11.8", - "create-hmac": "^1.1.7", - "elliptic": "^6.4.0", - "nan": "^2.13.2" - } - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "requires": { - "is-number": "^7.0.0" - } - }, - "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "treeify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz", - "integrity": "sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==" - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" - }, - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, - "type-fest": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", - "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==" - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "typeforce": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz", - "integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==" - }, - "ultron": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" - }, - "unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "requires": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - } - }, - "underscore": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", - "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==" - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "requires": { - "punycode": "^2.1.0" - } - }, - "url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==", - "requires": { - "prepend-http": "^2.0.0" - } - }, - "url-set-query": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-set-query/-/url-set-query-1.0.0.tgz", - "integrity": "sha512-3AChu4NiXquPfeckE5R5cGdiHCMWJx1dwCWOmWIL4KHAziJNOFIYJlpGFeKDvwLPHovZRCxK3cYlwzqI9Vp+Gg==" - }, - "url-to-options": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", - "integrity": "sha512-0kQLIzG4fdk/G5NONku64rSH/x32NOA39LVQqlK8Le6lvTF6GGRJpqaQFGgU+CLwySIqBSMdwYM0sYcW9f6P4A==" - }, - "usb": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/usb/-/usb-1.9.2.tgz", - "integrity": "sha512-dryNz030LWBPAf6gj8vyq0Iev3vPbCLHCT8dBw3gQRXRzVNsIdeuU+VjPp3ksmSPkeMAl1k+kQ14Ij0QHyeiAg==", - "requires": { - "node-addon-api": "^4.2.0", - "node-gyp-build": "^4.3.0" - }, - "dependencies": { - "node-addon-api": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", - "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==" - }, - "node-gyp-build": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz", - "integrity": "sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==" - } - } - }, - "utf-8-validate": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.9.tgz", - "integrity": "sha512-Yek7dAy0v3Kl0orwMlvi7TPtiCNrdfHNd7Gcc/pLq4BLXqfAmd0J7OWMizUQnTTJsyjKn02mU7anqwfmUP4J8Q==", - "requires": { - "node-gyp-build": "^4.3.0" - }, - "dependencies": { - "node-gyp-build": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz", - "integrity": "sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==" - } - } - }, - "utf8": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", - "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==" - }, - "util": { - "version": "0.12.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", - "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", - "requires": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "safe-buffer": "^5.1.2", - "which-typed-array": "^1.1.2" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - }, - "varint": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz", - "integrity": "sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow==" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "web3": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3/-/web3-1.3.6.tgz", - "integrity": "sha512-jEpPhnL6GDteifdVh7ulzlPrtVQeA30V9vnki9liYlUvLV82ZM7BNOQJiuzlDePuE+jZETZSP/0G/JlUVt6pOA==", - "requires": { - "web3-bzz": "1.3.6", - "web3-core": "1.3.6", - "web3-eth": "1.3.6", - "web3-eth-personal": "1.3.6", - "web3-net": "1.3.6", - "web3-shh": "1.3.6", - "web3-utils": "1.3.6" - } - }, - "web3-bzz": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-bzz/-/web3-bzz-1.3.6.tgz", - "integrity": "sha512-ibHdx1wkseujFejrtY7ZyC0QxQ4ATXjzcNUpaLrvM6AEae8prUiyT/OloG9FWDgFD2CPLwzKwfSQezYQlANNlw==", - "requires": { - "@types/node": "^12.12.6", - "got": "9.6.0", - "swarm-js": "^0.1.40", - "underscore": "1.12.1" - }, - "dependencies": { - "@types/node": { - "version": "12.20.55", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", - "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==" - } - } - }, - "web3-core": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-1.3.6.tgz", - "integrity": "sha512-gkLDM4T1Sc0T+HZIwxrNrwPg0IfWI0oABSglP2X5ZbBAYVUeEATA0o92LWV8BeF+okvKXLK1Fek/p6axwM/h3Q==", - "requires": { - "@types/bn.js": "^4.11.5", - "@types/node": "^12.12.6", - "bignumber.js": "^9.0.0", - "web3-core-helpers": "1.3.6", - "web3-core-method": "1.3.6", - "web3-core-requestmanager": "1.3.6", - "web3-utils": "1.3.6" - }, - "dependencies": { - "@types/bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", - "requires": { - "@types/node": "*" - } - }, - "@types/node": { - "version": "12.20.55", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", - "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==" - } - } - }, - "web3-core-helpers": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.3.6.tgz", - "integrity": "sha512-nhtjA2ZbkppjlxTSwG0Ttu6FcPkVu1rCN5IFAOVpF/L0SEt+jy+O5l90+cjDq0jAYvlBwUwnbh2mR9hwDEJCNA==", - "requires": { - "underscore": "1.12.1", - "web3-eth-iban": "1.3.6", - "web3-utils": "1.3.6" - } - }, - "web3-core-method": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-core-method/-/web3-core-method-1.3.6.tgz", - "integrity": "sha512-RyegqVGxn0cyYW5yzAwkPlsSEynkdPiegd7RxgB4ak1eKk2Cv1q2x4C7D2sZjeeCEF+q6fOkVmo2OZNqS2iQxg==", - "requires": { - "@ethersproject/transactions": "^5.0.0-beta.135", - "underscore": "1.12.1", - "web3-core-helpers": "1.3.6", - "web3-core-promievent": "1.3.6", - "web3-core-subscriptions": "1.3.6", - "web3-utils": "1.3.6" - } - }, - "web3-core-promievent": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-core-promievent/-/web3-core-promievent-1.3.6.tgz", - "integrity": "sha512-Z+QzfyYDTXD5wJmZO5wwnRO8bAAHEItT1XNSPVb4J1CToV/I/SbF7CuF8Uzh2jns0Cm1109o666H7StFFvzVKw==", - "requires": { - "eventemitter3": "4.0.4" - } - }, - "web3-core-requestmanager": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-core-requestmanager/-/web3-core-requestmanager-1.3.6.tgz", - "integrity": "sha512-2rIaeuqeo7QN1Eex7aXP0ZqeteJEPWXYFS/M3r3LXMiV8R4STQBKE+//dnHJXoo2ctzEB5cgd+7NaJM8S3gPyA==", - "requires": { - "underscore": "1.12.1", - "util": "^0.12.0", - "web3-core-helpers": "1.3.6", - "web3-providers-http": "1.3.6", - "web3-providers-ipc": "1.3.6", - "web3-providers-ws": "1.3.6" - } - }, - "web3-core-subscriptions": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-core-subscriptions/-/web3-core-subscriptions-1.3.6.tgz", - "integrity": "sha512-wi9Z9X5X75OKvxAg42GGIf81ttbNR2TxzkAsp1g+nnp5K8mBwgZvXrIsDuj7Z7gx72Y45mWJADCWjk/2vqNu8g==", - "requires": { - "eventemitter3": "4.0.4", - "underscore": "1.12.1", - "web3-core-helpers": "1.3.6" - } - }, - "web3-eth": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-1.3.6.tgz", - "integrity": "sha512-9+rnywRRpyX3C4hfsAQXPQh6vHh9XzQkgLxo3gyeXfbhbShUoq2gFVuy42vsRs//6JlsKdyZS7Z3hHPHz2wreA==", - "requires": { - "underscore": "1.12.1", - "web3-core": "1.3.6", - "web3-core-helpers": "1.3.6", - "web3-core-method": "1.3.6", - "web3-core-subscriptions": "1.3.6", - "web3-eth-abi": "1.3.6", - "web3-eth-accounts": "1.3.6", - "web3-eth-contract": "1.3.6", - "web3-eth-ens": "1.3.6", - "web3-eth-iban": "1.3.6", - "web3-eth-personal": "1.3.6", - "web3-net": "1.3.6", - "web3-utils": "1.3.6" - } - }, - "web3-eth-abi": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.3.6.tgz", - "integrity": "sha512-Or5cRnZu6WzgScpmbkvC6bfNxR26hqiKK4i8sMPFeTUABQcb/FU3pBj7huBLYbp9dH+P5W79D2MqwbWwjj9DoQ==", - "requires": { - "@ethersproject/abi": "5.0.7", - "underscore": "1.12.1", - "web3-utils": "1.3.6" - } - }, - "web3-eth-accounts": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-1.3.6.tgz", - "integrity": "sha512-Ilr0hG6ONbCdSlVKffasCmNwftD5HsNpwyQASevocIQwHdTlvlwO0tb3oGYuajbKOaDzNTwXfz25bttAEoFCGA==", - "requires": { - "crypto-browserify": "3.12.0", - "eth-lib": "0.2.8", - "ethereumjs-common": "^1.3.2", - "ethereumjs-tx": "^2.1.1", - "scrypt-js": "^3.0.1", - "underscore": "1.12.1", - "uuid": "3.3.2", - "web3-core": "1.3.6", - "web3-core-helpers": "1.3.6", - "web3-core-method": "1.3.6", - "web3-utils": "1.3.6" - }, - "dependencies": { - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" - } - } - }, - "web3-eth-contract": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.3.6.tgz", - "integrity": "sha512-8gDaRrLF2HCg+YEZN1ov0zN35vmtPnGf3h1DxmJQK5Wm2lRMLomz9rsWsuvig3UJMHqZAQKD7tOl3ocJocQsmA==", - "requires": { - "@types/bn.js": "^4.11.5", - "underscore": "1.12.1", - "web3-core": "1.3.6", - "web3-core-helpers": "1.3.6", - "web3-core-method": "1.3.6", - "web3-core-promievent": "1.3.6", - "web3-core-subscriptions": "1.3.6", - "web3-eth-abi": "1.3.6", - "web3-utils": "1.3.6" - }, - "dependencies": { - "@types/bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", - "requires": { - "@types/node": "*" - } - } - } - }, - "web3-eth-ens": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-1.3.6.tgz", - "integrity": "sha512-n27HNj7lpSkRxTgSx+Zo7cmKAgyg2ElFilaFlUu/X2CNH23lXfcPm2bWssivH9z0ndhg0OyR4AYFZqPaqDHkJA==", - "requires": { - "content-hash": "^2.5.2", - "eth-ens-namehash": "2.0.8", - "underscore": "1.12.1", - "web3-core": "1.3.6", - "web3-core-helpers": "1.3.6", - "web3-core-promievent": "1.3.6", - "web3-eth-abi": "1.3.6", - "web3-eth-contract": "1.3.6", - "web3-utils": "1.3.6" - } - }, - "web3-eth-iban": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.3.6.tgz", - "integrity": "sha512-nfMQaaLA/zsg5W4Oy/EJQbs8rSs1vBAX6b/35xzjYoutXlpHMQadujDx2RerTKhSHqFXSJeQAfE+2f6mdhYkRQ==", - "requires": { - "bn.js": "^4.11.9", - "web3-utils": "1.3.6" - } - }, - "web3-eth-personal": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-1.3.6.tgz", - "integrity": "sha512-pOHU0+/h1RFRYoh1ehYBehRbcKWP4OSzd4F7mDljhHngv6W8ewMHrAN8O1ol9uysN2MuCdRE19qkRg5eNgvzFQ==", - "requires": { - "@types/node": "^12.12.6", - "web3-core": "1.3.6", - "web3-core-helpers": "1.3.6", - "web3-core-method": "1.3.6", - "web3-net": "1.3.6", - "web3-utils": "1.3.6" - }, - "dependencies": { - "@types/node": { - "version": "12.20.55", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", - "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==" - } - } - }, - "web3-net": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-net/-/web3-net-1.3.6.tgz", - "integrity": "sha512-KhzU3wMQY/YYjyMiQzbaLPt2kut88Ncx2iqjy3nw28vRux3gVX0WOCk9EL/KVJBiAA/fK7VklTXvgy9dZnnipw==", - "requires": { - "web3-core": "1.3.6", - "web3-core-method": "1.3.6", - "web3-utils": "1.3.6" - } - }, - "web3-providers-http": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-1.3.6.tgz", - "integrity": "sha512-OQkT32O1A06dISIdazpGLveZcOXhEo5cEX6QyiSQkiPk/cjzDrXMw4SKZOGQbbS1+0Vjizm1Hrp7O8Vp2D1M5Q==", - "requires": { - "web3-core-helpers": "1.3.6", - "xhr2-cookies": "1.1.0" - } - }, - "web3-providers-ipc": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-1.3.6.tgz", - "integrity": "sha512-+TVsSd2sSVvVgHG4s6FXwwYPPT91boKKcRuEFXqEfAbUC5t52XOgmyc2LNiD9LzPhed65FbV4LqICpeYGUvSwA==", - "requires": { - "oboe": "2.1.5", - "underscore": "1.12.1", - "web3-core-helpers": "1.3.6" - } - }, - "web3-providers-ws": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-1.3.6.tgz", - "integrity": "sha512-bk7MnJf5or0Re2zKyhR3L3CjGululLCHXx4vlbc/drnaTARUVvi559OI5uLytc/1k5HKUUyENAxLvetz2G1dnQ==", - "requires": { - "eventemitter3": "4.0.4", - "underscore": "1.12.1", - "web3-core-helpers": "1.3.6", - "websocket": "^1.0.32" - } - }, - "web3-shh": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-shh/-/web3-shh-1.3.6.tgz", - "integrity": "sha512-9zRo415O0iBslxBnmu9OzYjNErzLnzOsy+IOvSpIreLYbbAw0XkDWxv3SfcpKnTIWIACBR4AYMIxmmyi5iB3jw==", - "requires": { - "web3-core": "1.3.6", - "web3-core-method": "1.3.6", - "web3-core-subscriptions": "1.3.6", - "web3-net": "1.3.6" - } - }, - "web3-utils": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.3.6.tgz", - "integrity": "sha512-hHatFaQpkQgjGVER17gNx8u1qMyaXFZtM0y0XLGH1bzsjMPlkMPLRcYOrZ00rOPfTEuYFOdrpGOqZXVmGrMZRg==", - "requires": { - "bn.js": "^4.11.9", - "eth-lib": "0.2.8", - "ethereum-bloom-filters": "^1.0.6", - "ethjs-unit": "0.1.6", - "number-to-bn": "1.7.0", - "randombytes": "^2.1.0", - "underscore": "1.12.1", - "utf8": "3.0.0" - } - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "websocket": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz", - "integrity": "sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==", - "requires": { - "bufferutil": "^4.0.1", - "debug": "^2.2.0", - "es5-ext": "^0.10.50", - "typedarray-to-buffer": "^3.1.5", - "utf-8-validate": "^5.0.2", - "yaeti": "^0.0.6" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, - "websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "requires": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - } - }, - "websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" - }, - "whatwg-fetch": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz", - "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==" - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "requires": { - "isexe": "^2.0.0" - } - }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - } - }, - "which-typed-array": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.8.tgz", - "integrity": "sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==", - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-abstract": "^1.20.0", - "for-each": "^0.3.3", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.9" - } - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "widest-line": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", - "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", - "requires": { - "string-width": "^2.1.1" - } - }, - "wif": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz", - "integrity": "sha512-HIanZn1zmduSF+BQhkE+YXIbEiH0xPr1012QbFEGB0xsKqJii0/SqJjyn8dFv6y36kOznMgMB+LGcbZTJ1xACQ==", - "requires": { - "bs58check": "<3.0.0" - } - }, - "wrap-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz", - "integrity": "sha512-iXR3tDXpbnTpzjKSylUJRkLuOrEC7hwEB221cgn6wtF8wpmz28puFXAEfPT5zrjM3wahygB//VuWEr1vTkDcNQ==", - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "ws": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", - "requires": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0", - "ultron": "~1.1.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, - "xhr": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz", - "integrity": "sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==", - "requires": { - "global": "~4.4.0", - "is-function": "^1.0.1", - "parse-headers": "^2.0.0", - "xtend": "^4.0.0" - } - }, - "xhr-request": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/xhr-request/-/xhr-request-1.1.0.tgz", - "integrity": "sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA==", - "requires": { - "buffer-to-arraybuffer": "^0.0.5", - "object-assign": "^4.1.1", - "query-string": "^5.0.1", - "simple-get": "^2.7.0", - "timed-out": "^4.0.1", - "url-set-query": "^1.0.0", - "xhr": "^2.0.4" - } - }, - "xhr-request-promise": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/xhr-request-promise/-/xhr-request-promise-0.1.3.tgz", - "integrity": "sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg==", - "requires": { - "xhr-request": "^1.1.0" - } - }, - "xhr2-cookies": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz", - "integrity": "sha512-hjXUA6q+jl/bd8ADHcVfFsSPIf+tyLIjuO9TwJC9WI6JP2zKcS7C+p56I9kCLLsaCiNT035iYvEUUzdEFj/8+g==", - "requires": { - "cookiejar": "^2.1.1" - } - }, - "xmlhttprequest": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", - "integrity": "sha512-58Im/U0mlVBLM38NdZjHyhuMtCqa61469k2YP/AaPbvCoV9aQGUpbJBj1QRm2ytRiVQBD/fsw7L2bJGDVQswBA==" - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" - }, - "yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "yarn": { - "version": "1.22.19", - "resolved": "https://registry.npmjs.org/yarn/-/yarn-1.22.19.tgz", - "integrity": "sha512-/0V5q0WbslqnwP91tirOvldvYISzaqhClxzyUKXYxs07yUILIs5jx/k6CFe8bvKSkds5w+eiOqta39Wk3WxdcQ==" - } - } -} diff --git a/packages/cli/package.json b/packages/cli/package.json index a6c80f6c560..4a4b3154a83 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "@celo/celocli", "description": "CLI Tool for transacting with the Celo protocol", - "version": "2.1.1-dev", + "version": "3.0.2", "author": "Celo", "license": "Apache-2.0", "repository": "celo-org/celo-monorepo", @@ -18,7 +18,7 @@ "celo-cli" ], "engines": { - "node": ">=14" + "node": ">=16" }, "scripts": { "clean": "tsc -b . --clean", @@ -34,18 +34,21 @@ "test": "TZ=UTC jest --runInBand" }, "dependencies": { - "@celo/bls12377js": "0.1.1", - "@celo/contractkit": "^4.1.1-dev", - "@celo/explorer": "^4.1.1-dev", - "@celo/governance": "^4.1.1-dev", - "@celo/identity": "^4.1.1-dev", - "@celo/phone-utils": "^4.1.1-dev", - "@celo/utils": "^4.1.1-dev", - "@celo/cryptographic-utils": "^4.1.1-dev", - "@celo/wallet-hsm-azure": "^4.1.1-dev", - "@celo/wallet-ledger": "^4.1.1-dev", - "@celo/wallet-local": "^4.1.1-dev", + "@celo/base": "^5.0.5", + "@celo/connect": "^5.1.0", + "@celo/contractkit": "^5.1.0", + "@celo/explorer": "^5.0.5", + "@celo/governance": "^5.0.5", + "@celo/identity": "^5.0.4", + "@celo/phone-utils": "^5.0.5", + "@celo/utils": "^5.0.5", + "@celo/cryptographic-utils": "^5.0.5", + "@celo/wallet-hsm-azure": "^5.1.0", + "@celo/wallet-ledger": "^5.1.0", + "@celo/wallet-local": "^5.1.0", "@ledgerhq/hw-transport-node-hid": "^6.27.4", + "@oclif/parser": "^3.8.16", + "@oclif/errors": "^1.3.6", "@oclif/command": "^1.6.0", "@oclif/config": "^1.6.0", "@oclif/plugin-autocomplete": "^0.1.5", @@ -58,7 +61,6 @@ "@types/command-exists": "^1.2.0", "bip32": "3.1.0", "bignumber.js": "9.0.0", - "bip39": "https://github.com/bitcoinjs/bip39#d8ea080a18b40f301d4e2219a2991cd2417e83c2", "chalk": "^2.4.2", "cli-table": "^0.3.1", "cli-ux": "^5.4.9", @@ -66,7 +68,7 @@ "debug": "^4.1.1", "events": "^3.0.0", "fs-extra": "^8.1.0", - "humanize-duration": "^3.21.0", + "humanize-duration": "^3.29.0", "moment": "^2.29.0", "path": "^0.12.7", "prompts": "^2.0.1", @@ -75,13 +77,12 @@ "web3": "1.10.0" }, "devDependencies": { - "@celo/dev-utils": "0.0.1-dev", - "@celo/flake-tracker": "0.0.1-dev", + "@celo/dev-utils": "0.0.1", "@oclif/dev-cli": "^1.23.0", "@types/cli-table": "^0.3.0", "@types/debug": "^4.1.4", "@types/fs-extra": "^8.0.0", - "@types/humanize-duration": "^3.18.0", + "@types/humanize-duration": "^3.27.0", "@types/inquirer": "^6.5.0", "@types/ledgerhq__hw-transport-node-hid": "^4.22.2", "@types/mocha": "^7.0.2", @@ -100,8 +101,7 @@ "!lib/test-utils", "!lib/**/*.test.**", "!lib/**/*.d.ts", - "/oclif.manifest.json", - "/npm-shrinkwrap.json" + "/oclif.manifest.json" ], "oclif": { "bin": "celocli", diff --git a/packages/cli/releasing.md b/packages/cli/releasing.md index 847f7fed20f..7ce7b4b07ba 100644 --- a/packages/cli/releasing.md +++ b/packages/cli/releasing.md @@ -2,14 +2,8 @@ - change all @celo/** dependencies which are pointing to the unpublished -dev version to published versions -- check that @celo/phone-number-privacy-common in @celo/identity and @celo/encrypted-backup packages points to a published version. (actually check on npm because just removing -dev might not be enough) - - update cli version in cli/package.json to next version with a pre-release (eg -beta.x) suffix -- run `yarn generate:shrinkwrap` (if you got some nonsense about @celo/phone-number-privacy-signer not being found try removing from peer deps of combiner running yarn again and retrying) - -- commit the the package.json and shrinkwrap - - run `yarn prepack` - *IMPORTANT* double check version in package.json is correct! diff --git a/packages/cli/src/base.ts b/packages/cli/src/base.ts index 6ca0e7753c2..b97492c5ffb 100644 --- a/packages/cli/src/base.ts +++ b/packages/cli/src/base.ts @@ -201,9 +201,6 @@ export abstract class BaseCommand extends Command { const setStableTokenGas = async (stable: StableToken) => { await this.kit.setFeeCurrency(stableTokenInfos[stable].contract) - await this.kit.updateGasPriceInConnectionLayer( - await this.kit.registry.addressFor(stableTokenInfos[stable].contract) - ) } if (Object.keys(StableToken).includes(gasCurrencyConfig)) { await setStableTokenGas(StableToken[gasCurrencyConfig as keyof typeof StableToken]) diff --git a/packages/cli/src/commands/account/offchain-read.ts b/packages/cli/src/commands/account/offchain-read.ts index 99896db0ef2..c66cef31c40 100644 --- a/packages/cli/src/commands/account/offchain-read.ts +++ b/packages/cli/src/commands/account/offchain-read.ts @@ -25,7 +25,10 @@ export default class OffchainRead extends BaseCommand { args: { address }, flags: { from, privateDEK }, } = this.parse(OffchainRead) - + // identity depends on a version of kit that doesnt support cip64 tx + // so the types dont match but it is fine to ignore here as the kit is not sending a tx + // its reading from AccountsWrapper + // @ts-expect-error const provider = new BasicDataWrapper(from!, this.kit) if (privateDEK) { diff --git a/packages/cli/src/commands/identity/get-attestations.ts b/packages/cli/src/commands/identity/get-attestations.ts index de484940246..b18f4f1534b 100644 --- a/packages/cli/src/commands/identity/get-attestations.ts +++ b/packages/cli/src/commands/identity/get-attestations.ts @@ -88,6 +88,7 @@ export default class GetAttestations extends BaseCommand { console.log('Using network: ' + network) const authSigner: AuthSigner = { authenticationMethod: OdisUtils.Query.AuthenticationMethod.WALLET_KEY, + // @ts-ignore contractKit: kit, } diff --git a/packages/cli/src/commands/identity/identifier.ts b/packages/cli/src/commands/identity/identifier.ts index 5b0ee0ad13e..826dce9ed9b 100644 --- a/packages/cli/src/commands/identity/identifier.ts +++ b/packages/cli/src/commands/identity/identifier.ts @@ -42,6 +42,7 @@ export default class IdentifierQuery extends BaseCommand { const authSigner: AuthSigner = { authenticationMethod: OdisUtils.Query.AuthenticationMethod.WALLET_KEY, + // @ts-expect-error contractKit: this.kit, } diff --git a/packages/cli/src/commands/releasegold/transfer-dollars.test.ts b/packages/cli/src/commands/releasegold/transfer-dollars.test.ts index e2e554a604a..560101ce2bb 100644 --- a/packages/cli/src/commands/releasegold/transfer-dollars.test.ts +++ b/packages/cli/src/commands/releasegold/transfer-dollars.test.ts @@ -40,6 +40,8 @@ testWithGanache('releasegold:transfer-dollars cmd', (web3: Web3) => { contractAddress, '--value', cUSDToTransfer, + '--gasCurrency', + 'CELO', ]) // RG cUSD balance should match the amount sent const contractBalance = await kit.getTotalBalance(contractAddress) @@ -52,6 +54,8 @@ testWithGanache('releasegold:transfer-dollars cmd', (web3: Web3) => { accounts[0], '--value', cUSDToTransfer, + '--gasCurrency', + 'CELO', ]) const balanceAfter = await kit.getTotalBalance(accounts[0]) expect(balanceBefore.cUSD).toEqual(balanceAfter.cUSD) diff --git a/packages/cli/src/commands/releasegold/transfer-dollars.ts b/packages/cli/src/commands/releasegold/transfer-dollars.ts index a8d29ef0466..72ba7017505 100644 --- a/packages/cli/src/commands/releasegold/transfer-dollars.ts +++ b/packages/cli/src/commands/releasegold/transfer-dollars.ts @@ -28,7 +28,6 @@ export default class TransferDollars extends ReleaseGoldBaseCommand { this.kit.defaultAccount = isRevoked ? await this.releaseGoldWrapper.getReleaseOwner() : await this.releaseGoldWrapper.getBeneficiary() - await displaySendTx('transfer', this.releaseGoldWrapper.transfer(flags.to, flags.value)) } } diff --git a/packages/cli/src/test-utils/setup.global.ts b/packages/cli/src/test-utils/setup.global.ts index de0256f2f7d..ac368faf7af 100644 --- a/packages/cli/src/test-utils/setup.global.ts +++ b/packages/cli/src/test-utils/setup.global.ts @@ -1,15 +1,12 @@ import baseSetup from '@celo/dev-utils/lib/ganache-setup' // Has to import the matchers somewhere so that typescript knows the matchers have been made available import _unused from '@celo/dev-utils/lib/matchers' -// @ts-ignore -import flakeTrackerSetup from '@celo/flake-tracker/src/jest/setup.global.js' import * as path from 'path' // Warning: There should be an unused import of '@celo/dev-utils/lib/matchers' above. // If there is not, then your editor probably deleted it automatically. export default async function globalSetup() { - await flakeTrackerSetup() await baseSetup(path.resolve(path.join(__dirname, '../../')), '.tmp/devchain.tar.gz', { from_targz: true, }) diff --git a/packages/cli/src/test-utils/teardown.global.ts b/packages/cli/src/test-utils/teardown.global.ts index c85f17c2e77..aea64f79dbb 100644 --- a/packages/cli/src/test-utils/teardown.global.ts +++ b/packages/cli/src/test-utils/teardown.global.ts @@ -1,8 +1,5 @@ import teardown from '@celo/dev-utils/lib/ganache-teardown' -// @ts-ignore -import flakeTrackerTeardown from '@celo/flake-tracker/src/jest/teardown.global.js' export default async function globalTeardown() { - await flakeTrackerTeardown() await teardown() } diff --git a/packages/cli/src/transfer-stable-base.ts b/packages/cli/src/transfer-stable-base.ts index f7378d16199..f3901c0ea0b 100644 --- a/packages/cli/src/transfer-stable-base.ts +++ b/packages/cli/src/transfer-stable-base.ts @@ -1,4 +1,5 @@ import { StableToken } from '@celo/contractkit' +import { stableTokenInfos } from '@celo/contractkit/lib/celo-tokens' import { StableTokenWrapper } from '@celo/contractkit/lib/wrappers/StableTokenWrapper' import { flags } from '@oclif/command' import { ParserOutput } from '@oclif/parser/lib/parse' @@ -35,6 +36,10 @@ export abstract class TransferStableBase extends BaseCommand { } catch { failWith(`The ${this._stableCurrency} token was not deployed yet`) } + // If gasCurrency is not set, use the transferring token + if (!res.flags.gasCurrency) { + await this.kit.setFeeCurrency(stableTokenInfos[this._stableCurrency].contract) + } const tx = res.flags.comment ? stableToken.transferWithComment(to, value.toFixed(), res.flags.comment) @@ -46,14 +51,12 @@ export abstract class TransferStableBase extends BaseCommand { `Account can afford transfer and gas paid in ${this._stableCurrency}`, this.kit.connection.defaultFeeCurrency === stableToken.address, async () => { - const gas = await tx.txo.estimateGas({ feeCurrency: stableToken.address }) - // TODO: replace with gasPrice rpc once supported by min client version - const { gasPrice } = await this.kit.connection.fillGasPrice({ - gasPrice: '0', - feeCurrency: stableToken.address, - }) + const [gas, gasPrice, balance] = await Promise.all([ + tx.txo.estimateGas({ feeCurrency: stableToken.address }), + this.kit.connection.gasPrice(stableToken.address), + stableToken.balanceOf(from), + ]) const gasValue = new BigNumber(gas).times(gasPrice as string) - const balance = await stableToken.balanceOf(from) return balance.gte(value.plus(gasValue)) }, `Cannot afford transfer with ${this._stableCurrency} gasCurrency; try reducing value slightly or using gasCurrency=CELO` diff --git a/packages/cli/src/utils/checks.ts b/packages/cli/src/utils/checks.ts index 6fcd55c75e8..8ab28c6c6f3 100644 --- a/packages/cli/src/utils/checks.ts +++ b/packages/cli/src/utils/checks.ts @@ -503,6 +503,8 @@ class CheckBuilder { if (!allPassed) { return this.cmd.error("Some checks didn't pass!") + } else { + console.log(`All checks passed`) } } diff --git a/packages/cli/src/utils/governance.ts b/packages/cli/src/utils/governance.ts index 90c8dc69076..c1dd228c05f 100644 --- a/packages/cli/src/utils/governance.ts +++ b/packages/cli/src/utils/governance.ts @@ -1,6 +1,6 @@ import { toTxResult } from '@celo/connect' import { ContractKit } from '@celo/contractkit' -import { ProposalTransaction } from '@celo/contractkit/src/wrappers/Governance' +import { ProposalTransaction } from '@celo/contractkit/lib/wrappers/Governance' import { ProposalBuilder, proposalToJSON, ProposalTransactionJSON } from '@celo/governance' import chalk from 'chalk' import { readJsonSync } from 'fs-extra' diff --git a/packages/cli/src/utils/off-chain-data.ts b/packages/cli/src/utils/off-chain-data.ts index 26e4bb70b97..4f4ab16ee78 100644 --- a/packages/cli/src/utils/off-chain-data.ts +++ b/packages/cli/src/utils/off-chain-data.ts @@ -46,6 +46,10 @@ export abstract class OffchainDataCommand extends BaseCommand { }: ParserOutput = this.parse() const from = privateKeyToAddress(privateKey) + // identity depends on a version of kit that doesnt support cip64 tx + // so the types dont match but it is fine to ignore here as the kit is not sending a tx + // its reading from AccountsWrapper + // @ts-expect-error this.offchainDataWrapper = new BasicDataWrapper(from, this.kit) this.offchainDataWrapper.storageWriter = diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index 41d260aa4e5..37afef8c0d3 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,7 @@ { "name": "@celo/dev-utils", - "version": "0.0.1-dev", + "version": "0.0.1", + "private": false, "description": "util package for celo packages that should only be a devDependency", "main": "./lib/index.js", "types": "./lib/index.d.ts", @@ -16,6 +17,7 @@ "lint": "tslint -c tslint.json --project ." }, "dependencies": { + "ganache": "npm:@celo/ganache@7.8.0-unofficial.0", "bignumber.js": "^9.0.0", "web3": "1.10.0", "web3-core-helpers": "1.10.0", diff --git a/packages/docs/developer-resources/utils/reference/README.md b/packages/docs/developer-resources/utils/reference/README.md index f981610c716..3c2f9cb9bcf 100644 --- a/packages/docs/developer-resources/utils/reference/README.md +++ b/packages/docs/developer-resources/utils/reference/README.md @@ -109,20 +109,13 @@ See the [issue backlog](https://github.com/celo-org/celo-monorepo/issues) for a The repository has the following packages (sub projects): - [attestation-service](packages/attestation-service) - service run by validators on the Celo network to send SMS messages, enabling attestations of user phone numbers and their accounts on the Celo network -- [blockchain-api](packages/blockchain-api) - service that uses Blockscout to present view of transactions by account for Celo Wallet activity feed - [celotool](packages/celotool) - scripts for deploying and managing testnets - [cli](packages/cli) - tool that uses ContractKit to interact with the Celo protocol ([docs](https://docs.celo.org/command-line-interface/introduction)) -- [contractkit](packages/contractkit) - library to help developers and validators interact with the protocol and it's smart contracts ([docs](https://docs.celo.org/celo-sdk/contractkit)) +- [sdk/contractkit](packages/sdk/contractkit) - library to help developers and validators interact with the protocol and it's smart contracts ([docs](https://docs.celo.org/celo-sdk/contractkit)) - [dev-utils](packages/dev-utils) - a utils package for use as a dev dependency - [docs](packages/docs) - technical documentation for the Celo project ([live](https://docs.celo.org/)) -- [faucet](packages/faucet) - faucet deployment configuration ([live](https://celo.org/build/faucet)) - [helm-charts](packages/helm-charts) - templatized deployments of entire environments to Kubernetes clusters -- [mobile](packages/mobile) - Android wallet app for the Celo platform ([docs](https://docs.celo.org/getting-started/using-the-mobile-wallet), [live](https://play.google.com/store/apps/details?id=org.celo.mobile.alfajores)) -- [notification-service](packages/notification-service) - service for managing push notifications for Celo Wallet - [protocol](packages/protocol) - identity, stability and other smart contracts for the Celo protocol ([docs](https://docs.celo.org/celo-codebase/protocol)) -- [react-components](packages/react-components) - generalized react native components -- [reserve-site](packages/reserve-site) - website for ([Celo Reserve.org](https://celoreserve.org/)) -- [terraform-modules](packages/terraform-modules) - templatized deployments of entire VM-based testnets for Google Cloud Platform - [typescript](packages/typescript) - no README available (improve?) - [utils](packages/utils) - no README available (improve?) diff --git a/packages/docs/sdk/docs/README.md b/packages/docs/sdk/docs/README.md index afe4d7050ad..ac2255c9c07 100644 --- a/packages/docs/sdk/docs/README.md +++ b/packages/docs/sdk/docs/README.md @@ -7,10 +7,8 @@ You can find more information about the specific packages at the following links - [Base](./base) - [Connect](./connect) - [ContractKit](./contractkit) -- [Encrypted Backup](./ecrypted-backup) - [Explorer](./explorer) - [Governance](./governance) -- [Identity](./identity) - [Keystores](./keystores) - [Network Utils](./network-utils) - [Phone Utils](./phone-utils) diff --git a/packages/docs/sdk/docs/encrypted-backup/README.md b/packages/docs/sdk/docs/encrypted-backup/README.md deleted file mode 100644 index 691631effc3..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/README.md +++ /dev/null @@ -1,15 +0,0 @@ -[@celo/encrypted-backup](README.md) - -# @celo/encrypted-backup - -## Index - -### Modules - -* ["backup"](modules/_backup_.md) -* ["config"](modules/_config_.md) -* ["errors"](modules/_errors_.md) -* ["odis"](modules/_odis_.md) -* ["odis.mock"](modules/_odis_mock_.md) -* ["schema"](modules/_schema_.md) -* ["utils"](modules/_utils_.md) diff --git a/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.authorizationerror.md b/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.authorizationerror.md deleted file mode 100644 index b8ac559db02..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.authorizationerror.md +++ /dev/null @@ -1,93 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["errors"](../modules/_errors_.md) › [AuthorizationError](_errors_.authorizationerror.md) - -# Class: AuthorizationError - -## Hierarchy - -* RootError‹[AUTHORIZATION_ERROR](../enums/_errors_.backuperrortypes.md#authorization_error)› - - ↳ **AuthorizationError** - -## Implements - -* BaseError‹[AUTHORIZATION_ERROR](../enums/_errors_.backuperrortypes.md#authorization_error)› - -## Index - -### Constructors - -* [constructor](_errors_.authorizationerror.md#constructor) - -### Properties - -* [error](_errors_.authorizationerror.md#optional-readonly-error) -* [errorType](_errors_.authorizationerror.md#readonly-errortype) -* [message](_errors_.authorizationerror.md#message) -* [name](_errors_.authorizationerror.md#name) -* [stack](_errors_.authorizationerror.md#optional-stack) - -## Constructors - -### constructor - -\+ **new AuthorizationError**(`error?`: Error): *[AuthorizationError](_errors_.authorizationerror.md)* - -*Overrides void* - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:20](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L20)* - -**Parameters:** - -Name | Type | ------- | ------ | -`error?` | Error | - -**Returns:** *[AuthorizationError](_errors_.authorizationerror.md)* - -## Properties - -### `Optional` `Readonly` error - -• **error**? : *Error* - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:21](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L21)* - -___ - -### `Readonly` errorType - -• **errorType**: *[AUTHORIZATION_ERROR](../enums/_errors_.backuperrortypes.md#authorization_error)* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[errorType](_errors_.authorizationerror.md#readonly-errortype)* - -Defined in packages/sdk/base/lib/result.d.ts:19 - -___ - -### message - -• **message**: *string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[message](_errors_.authorizationerror.md#message)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:974 - -___ - -### name - -• **name**: *string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[name](_errors_.authorizationerror.md#name)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:973 - -___ - -### `Optional` stack - -• **stack**? : *undefined | string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[stack](_errors_.authorizationerror.md#optional-stack)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:975 diff --git a/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.decodeerror.md b/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.decodeerror.md deleted file mode 100644 index 35b31b654d8..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.decodeerror.md +++ /dev/null @@ -1,93 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["errors"](../modules/_errors_.md) › [DecodeError](_errors_.decodeerror.md) - -# Class: DecodeError - -## Hierarchy - -* RootError‹[DECODE_ERROR](../enums/_errors_.backuperrortypes.md#decode_error)› - - ↳ **DecodeError** - -## Implements - -* BaseError‹[DECODE_ERROR](../enums/_errors_.backuperrortypes.md#decode_error)› - -## Index - -### Constructors - -* [constructor](_errors_.decodeerror.md#constructor) - -### Properties - -* [error](_errors_.decodeerror.md#optional-readonly-error) -* [errorType](_errors_.decodeerror.md#readonly-errortype) -* [message](_errors_.decodeerror.md#message) -* [name](_errors_.decodeerror.md#name) -* [stack](_errors_.decodeerror.md#optional-stack) - -## Constructors - -### constructor - -\+ **new DecodeError**(`error?`: Error): *[DecodeError](_errors_.decodeerror.md)* - -*Overrides void* - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:26](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L26)* - -**Parameters:** - -Name | Type | ------- | ------ | -`error?` | Error | - -**Returns:** *[DecodeError](_errors_.decodeerror.md)* - -## Properties - -### `Optional` `Readonly` error - -• **error**? : *Error* - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:27](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L27)* - -___ - -### `Readonly` errorType - -• **errorType**: *[DECODE_ERROR](../enums/_errors_.backuperrortypes.md#decode_error)* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[errorType](_errors_.authorizationerror.md#readonly-errortype)* - -Defined in packages/sdk/base/lib/result.d.ts:19 - -___ - -### message - -• **message**: *string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[message](_errors_.authorizationerror.md#message)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:974 - -___ - -### name - -• **name**: *string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[name](_errors_.authorizationerror.md#name)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:973 - -___ - -### `Optional` stack - -• **stack**? : *undefined | string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[stack](_errors_.authorizationerror.md#optional-stack)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:975 diff --git a/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.decryptionerror.md b/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.decryptionerror.md deleted file mode 100644 index 7d9669c5d91..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.decryptionerror.md +++ /dev/null @@ -1,93 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["errors"](../modules/_errors_.md) › [DecryptionError](_errors_.decryptionerror.md) - -# Class: DecryptionError - -## Hierarchy - -* RootError‹[DECRYPTION_ERROR](../enums/_errors_.backuperrortypes.md#decryption_error)› - - ↳ **DecryptionError** - -## Implements - -* BaseError‹[DECRYPTION_ERROR](../enums/_errors_.backuperrortypes.md#decryption_error)› - -## Index - -### Constructors - -* [constructor](_errors_.decryptionerror.md#constructor) - -### Properties - -* [error](_errors_.decryptionerror.md#optional-readonly-error) -* [errorType](_errors_.decryptionerror.md#readonly-errortype) -* [message](_errors_.decryptionerror.md#message) -* [name](_errors_.decryptionerror.md#name) -* [stack](_errors_.decryptionerror.md#optional-stack) - -## Constructors - -### constructor - -\+ **new DecryptionError**(`error?`: Error): *[DecryptionError](_errors_.decryptionerror.md)* - -*Overrides void* - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:32](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L32)* - -**Parameters:** - -Name | Type | ------- | ------ | -`error?` | Error | - -**Returns:** *[DecryptionError](_errors_.decryptionerror.md)* - -## Properties - -### `Optional` `Readonly` error - -• **error**? : *Error* - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:33](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L33)* - -___ - -### `Readonly` errorType - -• **errorType**: *[DECRYPTION_ERROR](../enums/_errors_.backuperrortypes.md#decryption_error)* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[errorType](_errors_.authorizationerror.md#readonly-errortype)* - -Defined in packages/sdk/base/lib/result.d.ts:19 - -___ - -### message - -• **message**: *string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[message](_errors_.authorizationerror.md#message)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:974 - -___ - -### name - -• **name**: *string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[name](_errors_.authorizationerror.md#name)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:973 - -___ - -### `Optional` stack - -• **stack**? : *undefined | string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[stack](_errors_.authorizationerror.md#optional-stack)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:975 diff --git a/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.encryptionerror.md b/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.encryptionerror.md deleted file mode 100644 index 4a5466a7db3..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.encryptionerror.md +++ /dev/null @@ -1,93 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["errors"](../modules/_errors_.md) › [EncryptionError](_errors_.encryptionerror.md) - -# Class: EncryptionError - -## Hierarchy - -* RootError‹[ENCRYPTION_ERROR](../enums/_errors_.backuperrortypes.md#encryption_error)› - - ↳ **EncryptionError** - -## Implements - -* BaseError‹[ENCRYPTION_ERROR](../enums/_errors_.backuperrortypes.md#encryption_error)› - -## Index - -### Constructors - -* [constructor](_errors_.encryptionerror.md#constructor) - -### Properties - -* [error](_errors_.encryptionerror.md#optional-readonly-error) -* [errorType](_errors_.encryptionerror.md#readonly-errortype) -* [message](_errors_.encryptionerror.md#message) -* [name](_errors_.encryptionerror.md#name) -* [stack](_errors_.encryptionerror.md#optional-stack) - -## Constructors - -### constructor - -\+ **new EncryptionError**(`error?`: Error): *[EncryptionError](_errors_.encryptionerror.md)* - -*Overrides void* - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:38](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L38)* - -**Parameters:** - -Name | Type | ------- | ------ | -`error?` | Error | - -**Returns:** *[EncryptionError](_errors_.encryptionerror.md)* - -## Properties - -### `Optional` `Readonly` error - -• **error**? : *Error* - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:39](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L39)* - -___ - -### `Readonly` errorType - -• **errorType**: *[ENCRYPTION_ERROR](../enums/_errors_.backuperrortypes.md#encryption_error)* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[errorType](_errors_.authorizationerror.md#readonly-errortype)* - -Defined in packages/sdk/base/lib/result.d.ts:19 - -___ - -### message - -• **message**: *string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[message](_errors_.authorizationerror.md#message)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:974 - -___ - -### name - -• **name**: *string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[name](_errors_.authorizationerror.md#name)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:973 - -___ - -### `Optional` stack - -• **stack**? : *undefined | string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[stack](_errors_.authorizationerror.md#optional-stack)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:975 diff --git a/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.fetcherror.md b/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.fetcherror.md deleted file mode 100644 index eaae1fc8ed2..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.fetcherror.md +++ /dev/null @@ -1,93 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["errors"](../modules/_errors_.md) › [FetchError](_errors_.fetcherror.md) - -# Class: FetchError - -## Hierarchy - -* RootError‹[FETCH_ERROR](../enums/_errors_.backuperrortypes.md#fetch_error)› - - ↳ **FetchError** - -## Implements - -* BaseError‹[FETCH_ERROR](../enums/_errors_.backuperrortypes.md#fetch_error)› - -## Index - -### Constructors - -* [constructor](_errors_.fetcherror.md#constructor) - -### Properties - -* [error](_errors_.fetcherror.md#optional-readonly-error) -* [errorType](_errors_.fetcherror.md#readonly-errortype) -* [message](_errors_.fetcherror.md#message) -* [name](_errors_.fetcherror.md#name) -* [stack](_errors_.fetcherror.md#optional-stack) - -## Constructors - -### constructor - -\+ **new FetchError**(`error?`: Error): *[FetchError](_errors_.fetcherror.md)* - -*Overrides void* - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:44](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L44)* - -**Parameters:** - -Name | Type | ------- | ------ | -`error?` | Error | - -**Returns:** *[FetchError](_errors_.fetcherror.md)* - -## Properties - -### `Optional` `Readonly` error - -• **error**? : *Error* - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:45](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L45)* - -___ - -### `Readonly` errorType - -• **errorType**: *[FETCH_ERROR](../enums/_errors_.backuperrortypes.md#fetch_error)* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[errorType](_errors_.authorizationerror.md#readonly-errortype)* - -Defined in packages/sdk/base/lib/result.d.ts:19 - -___ - -### message - -• **message**: *string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[message](_errors_.authorizationerror.md#message)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:974 - -___ - -### name - -• **name**: *string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[name](_errors_.authorizationerror.md#name)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:973 - -___ - -### `Optional` stack - -• **stack**? : *undefined | string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[stack](_errors_.authorizationerror.md#optional-stack)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:975 diff --git a/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.invalidbackuperror.md b/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.invalidbackuperror.md deleted file mode 100644 index c577e1558b4..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.invalidbackuperror.md +++ /dev/null @@ -1,93 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["errors"](../modules/_errors_.md) › [InvalidBackupError](_errors_.invalidbackuperror.md) - -# Class: InvalidBackupError - -## Hierarchy - -* RootError‹[INVALID_BACKUP_ERROR](../enums/_errors_.backuperrortypes.md#invalid_backup_error)› - - ↳ **InvalidBackupError** - -## Implements - -* BaseError‹[INVALID_BACKUP_ERROR](../enums/_errors_.backuperrortypes.md#invalid_backup_error)› - -## Index - -### Constructors - -* [constructor](_errors_.invalidbackuperror.md#constructor) - -### Properties - -* [error](_errors_.invalidbackuperror.md#optional-readonly-error) -* [errorType](_errors_.invalidbackuperror.md#readonly-errortype) -* [message](_errors_.invalidbackuperror.md#message) -* [name](_errors_.invalidbackuperror.md#name) -* [stack](_errors_.invalidbackuperror.md#optional-stack) - -## Constructors - -### constructor - -\+ **new InvalidBackupError**(`error?`: Error): *[InvalidBackupError](_errors_.invalidbackuperror.md)* - -*Overrides void* - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:50](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L50)* - -**Parameters:** - -Name | Type | ------- | ------ | -`error?` | Error | - -**Returns:** *[InvalidBackupError](_errors_.invalidbackuperror.md)* - -## Properties - -### `Optional` `Readonly` error - -• **error**? : *Error* - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:51](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L51)* - -___ - -### `Readonly` errorType - -• **errorType**: *[INVALID_BACKUP_ERROR](../enums/_errors_.backuperrortypes.md#invalid_backup_error)* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[errorType](_errors_.authorizationerror.md#readonly-errortype)* - -Defined in packages/sdk/base/lib/result.d.ts:19 - -___ - -### message - -• **message**: *string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[message](_errors_.authorizationerror.md#message)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:974 - -___ - -### name - -• **name**: *string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[name](_errors_.authorizationerror.md#name)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:973 - -___ - -### `Optional` stack - -• **stack**? : *undefined | string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[stack](_errors_.authorizationerror.md#optional-stack)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:975 diff --git a/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.odisratelimitingerror.md b/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.odisratelimitingerror.md deleted file mode 100644 index faa049f2081..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.odisratelimitingerror.md +++ /dev/null @@ -1,103 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["errors"](../modules/_errors_.md) › [OdisRateLimitingError](_errors_.odisratelimitingerror.md) - -# Class: OdisRateLimitingError - -## Hierarchy - -* RootError‹[ODIS_RATE_LIMITING_ERROR](../enums/_errors_.backuperrortypes.md#odis_rate_limiting_error)› - - ↳ **OdisRateLimitingError** - -## Implements - -* BaseError‹[ODIS_RATE_LIMITING_ERROR](../enums/_errors_.backuperrortypes.md#odis_rate_limiting_error)› - -## Index - -### Constructors - -* [constructor](_errors_.odisratelimitingerror.md#constructor) - -### Properties - -* [error](_errors_.odisratelimitingerror.md#optional-readonly-error) -* [errorType](_errors_.odisratelimitingerror.md#readonly-errortype) -* [message](_errors_.odisratelimitingerror.md#message) -* [name](_errors_.odisratelimitingerror.md#name) -* [notBefore](_errors_.odisratelimitingerror.md#optional-readonly-notbefore) -* [stack](_errors_.odisratelimitingerror.md#optional-stack) - -## Constructors - -### constructor - -\+ **new OdisRateLimitingError**(`notBefore?`: undefined | number, `error?`: Error): *[OdisRateLimitingError](_errors_.odisratelimitingerror.md)* - -*Overrides void* - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:62](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L62)* - -**Parameters:** - -Name | Type | ------- | ------ | -`notBefore?` | undefined | number | -`error?` | Error | - -**Returns:** *[OdisRateLimitingError](_errors_.odisratelimitingerror.md)* - -## Properties - -### `Optional` `Readonly` error - -• **error**? : *Error* - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:63](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L63)* - -___ - -### `Readonly` errorType - -• **errorType**: *[ODIS_RATE_LIMITING_ERROR](../enums/_errors_.backuperrortypes.md#odis_rate_limiting_error)* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[errorType](_errors_.authorizationerror.md#readonly-errortype)* - -Defined in packages/sdk/base/lib/result.d.ts:19 - -___ - -### message - -• **message**: *string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[message](_errors_.authorizationerror.md#message)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:974 - -___ - -### name - -• **name**: *string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[name](_errors_.authorizationerror.md#name)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:973 - -___ - -### `Optional` `Readonly` notBefore - -• **notBefore**? : *undefined | number* - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:63](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L63)* - -___ - -### `Optional` stack - -• **stack**? : *undefined | string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[stack](_errors_.authorizationerror.md#optional-stack)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:975 diff --git a/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.odisserviceerror.md b/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.odisserviceerror.md deleted file mode 100644 index eb6fb6b3725..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.odisserviceerror.md +++ /dev/null @@ -1,103 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["errors"](../modules/_errors_.md) › [OdisServiceError](_errors_.odisserviceerror.md) - -# Class: OdisServiceError - -## Hierarchy - -* RootError‹[ODIS_SERVICE_ERROR](../enums/_errors_.backuperrortypes.md#odis_service_error)› - - ↳ **OdisServiceError** - -## Implements - -* BaseError‹[ODIS_SERVICE_ERROR](../enums/_errors_.backuperrortypes.md#odis_service_error)› - -## Index - -### Constructors - -* [constructor](_errors_.odisserviceerror.md#constructor) - -### Properties - -* [error](_errors_.odisserviceerror.md#optional-readonly-error) -* [errorType](_errors_.odisserviceerror.md#readonly-errortype) -* [message](_errors_.odisserviceerror.md#message) -* [name](_errors_.odisserviceerror.md#name) -* [stack](_errors_.odisserviceerror.md#optional-stack) -* [version](_errors_.odisserviceerror.md#optional-readonly-version) - -## Constructors - -### constructor - -\+ **new OdisServiceError**(`error?`: Error, `version?`: undefined | string): *[OdisServiceError](_errors_.odisserviceerror.md)* - -*Overrides void* - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:56](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L56)* - -**Parameters:** - -Name | Type | ------- | ------ | -`error?` | Error | -`version?` | undefined | string | - -**Returns:** *[OdisServiceError](_errors_.odisserviceerror.md)* - -## Properties - -### `Optional` `Readonly` error - -• **error**? : *Error* - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:57](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L57)* - -___ - -### `Readonly` errorType - -• **errorType**: *[ODIS_SERVICE_ERROR](../enums/_errors_.backuperrortypes.md#odis_service_error)* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[errorType](_errors_.authorizationerror.md#readonly-errortype)* - -Defined in packages/sdk/base/lib/result.d.ts:19 - -___ - -### message - -• **message**: *string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[message](_errors_.authorizationerror.md#message)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:974 - -___ - -### name - -• **name**: *string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[name](_errors_.authorizationerror.md#name)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:973 - -___ - -### `Optional` stack - -• **stack**? : *undefined | string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[stack](_errors_.authorizationerror.md#optional-stack)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:975 - -___ - -### `Optional` `Readonly` version - -• **version**? : *undefined | string* - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:57](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L57)* diff --git a/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.odisverificationerror.md b/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.odisverificationerror.md deleted file mode 100644 index 36892811c10..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.odisverificationerror.md +++ /dev/null @@ -1,93 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["errors"](../modules/_errors_.md) › [OdisVerificationError](_errors_.odisverificationerror.md) - -# Class: OdisVerificationError - -## Hierarchy - -* RootError‹[ODIS_VERIFICATION_ERROR](../enums/_errors_.backuperrortypes.md#odis_verification_error)› - - ↳ **OdisVerificationError** - -## Implements - -* BaseError‹[ODIS_VERIFICATION_ERROR](../enums/_errors_.backuperrortypes.md#odis_verification_error)› - -## Index - -### Constructors - -* [constructor](_errors_.odisverificationerror.md#constructor) - -### Properties - -* [error](_errors_.odisverificationerror.md#optional-readonly-error) -* [errorType](_errors_.odisverificationerror.md#readonly-errortype) -* [message](_errors_.odisverificationerror.md#message) -* [name](_errors_.odisverificationerror.md#name) -* [stack](_errors_.odisverificationerror.md#optional-stack) - -## Constructors - -### constructor - -\+ **new OdisVerificationError**(`error?`: Error): *[OdisVerificationError](_errors_.odisverificationerror.md)* - -*Overrides void* - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:68](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L68)* - -**Parameters:** - -Name | Type | ------- | ------ | -`error?` | Error | - -**Returns:** *[OdisVerificationError](_errors_.odisverificationerror.md)* - -## Properties - -### `Optional` `Readonly` error - -• **error**? : *Error* - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:69](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L69)* - -___ - -### `Readonly` errorType - -• **errorType**: *[ODIS_VERIFICATION_ERROR](../enums/_errors_.backuperrortypes.md#odis_verification_error)* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[errorType](_errors_.authorizationerror.md#readonly-errortype)* - -Defined in packages/sdk/base/lib/result.d.ts:19 - -___ - -### message - -• **message**: *string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[message](_errors_.authorizationerror.md#message)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:974 - -___ - -### name - -• **name**: *string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[name](_errors_.authorizationerror.md#name)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:973 - -___ - -### `Optional` stack - -• **stack**? : *undefined | string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[stack](_errors_.authorizationerror.md#optional-stack)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:975 diff --git a/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.pbkdferror.md b/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.pbkdferror.md deleted file mode 100644 index b53cb6e5ae9..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.pbkdferror.md +++ /dev/null @@ -1,103 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["errors"](../modules/_errors_.md) › [PbkdfError](_errors_.pbkdferror.md) - -# Class: PbkdfError - -## Hierarchy - -* RootError‹[PBKDF_ERROR](../enums/_errors_.backuperrortypes.md#pbkdf_error)› - - ↳ **PbkdfError** - -## Implements - -* BaseError‹[PBKDF_ERROR](../enums/_errors_.backuperrortypes.md#pbkdf_error)› - -## Index - -### Constructors - -* [constructor](_errors_.pbkdferror.md#constructor) - -### Properties - -* [error](_errors_.pbkdferror.md#optional-readonly-error) -* [errorType](_errors_.pbkdferror.md#readonly-errortype) -* [iterations](_errors_.pbkdferror.md#readonly-iterations) -* [message](_errors_.pbkdferror.md#message) -* [name](_errors_.pbkdferror.md#name) -* [stack](_errors_.pbkdferror.md#optional-stack) - -## Constructors - -### constructor - -\+ **new PbkdfError**(`iterations`: number, `error?`: Error): *[PbkdfError](_errors_.pbkdferror.md)* - -*Overrides void* - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:74](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L74)* - -**Parameters:** - -Name | Type | ------- | ------ | -`iterations` | number | -`error?` | Error | - -**Returns:** *[PbkdfError](_errors_.pbkdferror.md)* - -## Properties - -### `Optional` `Readonly` error - -• **error**? : *Error* - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:75](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L75)* - -___ - -### `Readonly` errorType - -• **errorType**: *[PBKDF_ERROR](../enums/_errors_.backuperrortypes.md#pbkdf_error)* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[errorType](_errors_.authorizationerror.md#readonly-errortype)* - -Defined in packages/sdk/base/lib/result.d.ts:19 - -___ - -### `Readonly` iterations - -• **iterations**: *number* - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:75](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L75)* - -___ - -### message - -• **message**: *string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[message](_errors_.authorizationerror.md#message)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:974 - -___ - -### name - -• **name**: *string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[name](_errors_.authorizationerror.md#name)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:973 - -___ - -### `Optional` stack - -• **stack**? : *undefined | string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[stack](_errors_.authorizationerror.md#optional-stack)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:975 diff --git a/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.scrypterror.md b/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.scrypterror.md deleted file mode 100644 index 5e73876fb56..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.scrypterror.md +++ /dev/null @@ -1,103 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["errors"](../modules/_errors_.md) › [ScryptError](_errors_.scrypterror.md) - -# Class: ScryptError - -## Hierarchy - -* RootError‹[SCRYPT_ERROR](../enums/_errors_.backuperrortypes.md#scrypt_error)› - - ↳ **ScryptError** - -## Implements - -* BaseError‹[SCRYPT_ERROR](../enums/_errors_.backuperrortypes.md#scrypt_error)› - -## Index - -### Constructors - -* [constructor](_errors_.scrypterror.md#constructor) - -### Properties - -* [error](_errors_.scrypterror.md#optional-readonly-error) -* [errorType](_errors_.scrypterror.md#readonly-errortype) -* [message](_errors_.scrypterror.md#message) -* [name](_errors_.scrypterror.md#name) -* [options](_errors_.scrypterror.md#readonly-options) -* [stack](_errors_.scrypterror.md#optional-stack) - -## Constructors - -### constructor - -\+ **new ScryptError**(`options`: [ScryptOptions](../interfaces/_utils_.scryptoptions.md), `error?`: Error): *[ScryptError](_errors_.scrypterror.md)* - -*Overrides void* - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:80](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L80)* - -**Parameters:** - -Name | Type | ------- | ------ | -`options` | [ScryptOptions](../interfaces/_utils_.scryptoptions.md) | -`error?` | Error | - -**Returns:** *[ScryptError](_errors_.scrypterror.md)* - -## Properties - -### `Optional` `Readonly` error - -• **error**? : *Error* - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:81](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L81)* - -___ - -### `Readonly` errorType - -• **errorType**: *[SCRYPT_ERROR](../enums/_errors_.backuperrortypes.md#scrypt_error)* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[errorType](_errors_.authorizationerror.md#readonly-errortype)* - -Defined in packages/sdk/base/lib/result.d.ts:19 - -___ - -### message - -• **message**: *string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[message](_errors_.authorizationerror.md#message)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:974 - -___ - -### name - -• **name**: *string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[name](_errors_.authorizationerror.md#name)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:973 - -___ - -### `Readonly` options - -• **options**: *[ScryptOptions](../interfaces/_utils_.scryptoptions.md)* - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:81](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L81)* - -___ - -### `Optional` stack - -• **stack**? : *undefined | string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[stack](_errors_.authorizationerror.md#optional-stack)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:975 diff --git a/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.usageerror.md b/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.usageerror.md deleted file mode 100644 index c8382732fa4..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/classes/_errors_.usageerror.md +++ /dev/null @@ -1,93 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["errors"](../modules/_errors_.md) › [UsageError](_errors_.usageerror.md) - -# Class: UsageError - -## Hierarchy - -* RootError‹[USAGE_ERROR](../enums/_errors_.backuperrortypes.md#usage_error)› - - ↳ **UsageError** - -## Implements - -* BaseError‹[USAGE_ERROR](../enums/_errors_.backuperrortypes.md#usage_error)› - -## Index - -### Constructors - -* [constructor](_errors_.usageerror.md#constructor) - -### Properties - -* [error](_errors_.usageerror.md#optional-readonly-error) -* [errorType](_errors_.usageerror.md#readonly-errortype) -* [message](_errors_.usageerror.md#message) -* [name](_errors_.usageerror.md#name) -* [stack](_errors_.usageerror.md#optional-stack) - -## Constructors - -### constructor - -\+ **new UsageError**(`error?`: Error): *[UsageError](_errors_.usageerror.md)* - -*Overrides void* - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:86](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L86)* - -**Parameters:** - -Name | Type | ------- | ------ | -`error?` | Error | - -**Returns:** *[UsageError](_errors_.usageerror.md)* - -## Properties - -### `Optional` `Readonly` error - -• **error**? : *Error* - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:87](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L87)* - -___ - -### `Readonly` errorType - -• **errorType**: *[USAGE_ERROR](../enums/_errors_.backuperrortypes.md#usage_error)* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[errorType](_errors_.authorizationerror.md#readonly-errortype)* - -Defined in packages/sdk/base/lib/result.d.ts:19 - -___ - -### message - -• **message**: *string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[message](_errors_.authorizationerror.md#message)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:974 - -___ - -### name - -• **name**: *string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[name](_errors_.authorizationerror.md#name)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:973 - -___ - -### `Optional` stack - -• **stack**? : *undefined | string* - -*Inherited from [AuthorizationError](_errors_.authorizationerror.md).[stack](_errors_.authorizationerror.md#optional-stack)* - -Defined in node_modules/typescript/lib/lib.es5.d.ts:975 diff --git a/packages/docs/sdk/docs/encrypted-backup/classes/_odis_mock_.mockodis.md b/packages/docs/sdk/docs/encrypted-backup/classes/_odis_mock_.mockodis.md deleted file mode 100644 index 6c5f343bdc0..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/classes/_odis_mock_.mockodis.md +++ /dev/null @@ -1,137 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["odis.mock"](../modules/_odis_mock_.md) › [MockOdis](_odis_mock_.mockodis.md) - -# Class: MockOdis - -## Hierarchy - -* **MockOdis** - -## Index - -### Properties - -* [poprf](_odis_mock_.mockodis.md#readonly-poprf) -* [state](_odis_mock_.mockodis.md#readonly-state) -* [environment](_odis_mock_.mockodis.md#static-readonly-environment) - -### Methods - -* [install](_odis_mock_.mockodis.md#install) -* [installQuotaEndpoint](_odis_mock_.mockodis.md#installquotaendpoint) -* [installSignEndpoint](_odis_mock_.mockodis.md#installsignendpoint) -* [quota](_odis_mock_.mockodis.md#quota) -* [sign](_odis_mock_.mockodis.md#sign) - -## Properties - -### `Readonly` poprf - -• **poprf**: *any* = new PoprfServer(MOCK_ODIS_KEYPAIR.privateKey) - -*Defined in [packages/sdk/encrypted-backup/src/odis.mock.ts:32](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/odis.mock.ts#L32)* - -___ - -### `Readonly` state - -• **state**: *Record‹string, SequentialDelayDomainState›* - -*Defined in [packages/sdk/encrypted-backup/src/odis.mock.ts:31](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/odis.mock.ts#L31)* - -___ - -### `Static` `Readonly` environment - -▪ **environment**: *ServiceContext* = MOCK_ODIS_ENVIRONMENT - -*Defined in [packages/sdk/encrypted-backup/src/odis.mock.ts:29](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/odis.mock.ts#L29)* - -## Methods - -### install - -▸ **install**(`mock`: typeof fetchMock): *void* - -*Defined in [packages/sdk/encrypted-backup/src/odis.mock.ts:171](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/odis.mock.ts#L171)* - -**Parameters:** - -Name | Type | ------- | ------ | -`mock` | typeof fetchMock | - -**Returns:** *void* - -___ - -### installQuotaEndpoint - -▸ **installQuotaEndpoint**(`mock`: typeof fetchMock, `override?`: any): *void* - -*Defined in [packages/sdk/encrypted-backup/src/odis.mock.ts:137](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/odis.mock.ts#L137)* - -**Parameters:** - -Name | Type | ------- | ------ | -`mock` | typeof fetchMock | -`override?` | any | - -**Returns:** *void* - -___ - -### installSignEndpoint - -▸ **installSignEndpoint**(`mock`: typeof fetchMock, `override?`: any): *void* - -*Defined in [packages/sdk/encrypted-backup/src/odis.mock.ts:154](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/odis.mock.ts#L154)* - -**Parameters:** - -Name | Type | ------- | ------ | -`mock` | typeof fetchMock | -`override?` | any | - -**Returns:** *void* - -___ - -### quota - -▸ **quota**(`req`: DomainQuotaStatusRequest‹SequentialDelayDomain›): *object* - -*Defined in [packages/sdk/encrypted-backup/src/odis.mock.ts:34](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/odis.mock.ts#L34)* - -**Parameters:** - -Name | Type | ------- | ------ | -`req` | DomainQuotaStatusRequest‹SequentialDelayDomain› | - -**Returns:** *object* - -* **body**: *DomainQuotaStatusResponse* - -* **status**: *number* - -___ - -### sign - -▸ **sign**(`req`: DomainRestrictedSignatureRequest‹SequentialDelayDomain›): *object* - -*Defined in [packages/sdk/encrypted-backup/src/odis.mock.ts:61](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/odis.mock.ts#L61)* - -**Parameters:** - -Name | Type | ------- | ------ | -`req` | DomainRestrictedSignatureRequest‹SequentialDelayDomain› | - -**Returns:** *object* - -* **body**: *DomainRestrictedSignatureResponse* - -* **status**: *number* diff --git a/packages/docs/sdk/docs/encrypted-backup/enums/_config_.computationalhardeningfunction.md b/packages/docs/sdk/docs/encrypted-backup/enums/_config_.computationalhardeningfunction.md deleted file mode 100644 index a66fbef7c10..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/enums/_config_.computationalhardeningfunction.md +++ /dev/null @@ -1,26 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["config"](../modules/_config_.md) › [ComputationalHardeningFunction](_config_.computationalhardeningfunction.md) - -# Enumeration: ComputationalHardeningFunction - -## Index - -### Enumeration members - -* [PBKDF](_config_.computationalhardeningfunction.md#pbkdf) -* [SCRYPT](_config_.computationalhardeningfunction.md#scrypt) - -## Enumeration members - -### PBKDF - -• **PBKDF**: = "pbkdf2_sha256" - -*Defined in [packages/sdk/encrypted-backup/src/config.ts:58](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/config.ts#L58)* - -___ - -### SCRYPT - -• **SCRYPT**: = "scrypt" - -*Defined in [packages/sdk/encrypted-backup/src/config.ts:59](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/config.ts#L59)* diff --git a/packages/docs/sdk/docs/encrypted-backup/enums/_config_.environmentidentifier.md b/packages/docs/sdk/docs/encrypted-backup/enums/_config_.environmentidentifier.md deleted file mode 100644 index 691da1d14c3..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/enums/_config_.environmentidentifier.md +++ /dev/null @@ -1,26 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["config"](../modules/_config_.md) › [EnvironmentIdentifier](_config_.environmentidentifier.md) - -# Enumeration: EnvironmentIdentifier - -## Index - -### Enumeration members - -* [ALFAJORES](_config_.environmentidentifier.md#alfajores) -* [MAINNET](_config_.environmentidentifier.md#mainnet) - -## Enumeration members - -### ALFAJORES - -• **ALFAJORES**: = "ALFAJORES" - -*Defined in [packages/sdk/encrypted-backup/src/config.ts:253](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/config.ts#L253)* - -___ - -### MAINNET - -• **MAINNET**: = "MAINNET" - -*Defined in [packages/sdk/encrypted-backup/src/config.ts:252](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/config.ts#L252)* diff --git a/packages/docs/sdk/docs/encrypted-backup/enums/_errors_.backuperrortypes.md b/packages/docs/sdk/docs/encrypted-backup/enums/_errors_.backuperrortypes.md deleted file mode 100644 index b803de0f23c..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/enums/_errors_.backuperrortypes.md +++ /dev/null @@ -1,116 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["errors"](../modules/_errors_.md) › [BackupErrorTypes](_errors_.backuperrortypes.md) - -# Enumeration: BackupErrorTypes - -## Index - -### Enumeration members - -* [AUTHORIZATION_ERROR](_errors_.backuperrortypes.md#authorization_error) -* [DECODE_ERROR](_errors_.backuperrortypes.md#decode_error) -* [DECRYPTION_ERROR](_errors_.backuperrortypes.md#decryption_error) -* [ENCRYPTION_ERROR](_errors_.backuperrortypes.md#encryption_error) -* [FETCH_ERROR](_errors_.backuperrortypes.md#fetch_error) -* [INVALID_BACKUP_ERROR](_errors_.backuperrortypes.md#invalid_backup_error) -* [ODIS_RATE_LIMITING_ERROR](_errors_.backuperrortypes.md#odis_rate_limiting_error) -* [ODIS_SERVICE_ERROR](_errors_.backuperrortypes.md#odis_service_error) -* [ODIS_VERIFICATION_ERROR](_errors_.backuperrortypes.md#odis_verification_error) -* [PBKDF_ERROR](_errors_.backuperrortypes.md#pbkdf_error) -* [SCRYPT_ERROR](_errors_.backuperrortypes.md#scrypt_error) -* [USAGE_ERROR](_errors_.backuperrortypes.md#usage_error) - -## Enumeration members - -### AUTHORIZATION_ERROR - -• **AUTHORIZATION_ERROR**: = "AUTHORIZATION_ERROR" - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:6](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L6)* - -___ - -### DECODE_ERROR - -• **DECODE_ERROR**: = "DECODE_ERROR" - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:7](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L7)* - -___ - -### DECRYPTION_ERROR - -• **DECRYPTION_ERROR**: = "DECRYPTION_ERROR" - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:8](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L8)* - -___ - -### ENCRYPTION_ERROR - -• **ENCRYPTION_ERROR**: = "ENCRYPTION_ERROR" - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:9](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L9)* - -___ - -### FETCH_ERROR - -• **FETCH_ERROR**: = "FETCH_ERROR" - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:10](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L10)* - -___ - -### INVALID_BACKUP_ERROR - -• **INVALID_BACKUP_ERROR**: = "INVALID_BACKUP_ERROR" - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:11](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L11)* - -___ - -### ODIS_RATE_LIMITING_ERROR - -• **ODIS_RATE_LIMITING_ERROR**: = "ODIS_RATE_LIMITING_ERROR" - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:13](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L13)* - -___ - -### ODIS_SERVICE_ERROR - -• **ODIS_SERVICE_ERROR**: = "ODIS_SERVICE_ERROR" - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:12](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L12)* - -___ - -### ODIS_VERIFICATION_ERROR - -• **ODIS_VERIFICATION_ERROR**: = "ODIS_VERIFICATION_ERROR" - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:14](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L14)* - -___ - -### PBKDF_ERROR - -• **PBKDF_ERROR**: = "PBKDF_ERROR" - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:15](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L15)* - -___ - -### SCRYPT_ERROR - -• **SCRYPT_ERROR**: = "SCRYPT_ERROR" - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:16](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L16)* - -___ - -### USAGE_ERROR - -• **USAGE_ERROR**: = "USAGE_ERROR" - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:17](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L17)* diff --git a/packages/docs/sdk/docs/encrypted-backup/enums/_utils_.kdfinfo.md b/packages/docs/sdk/docs/encrypted-backup/enums/_utils_.kdfinfo.md deleted file mode 100644 index 7c315bea65e..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/enums/_utils_.kdfinfo.md +++ /dev/null @@ -1,73 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["utils"](../modules/_utils_.md) › [KDFInfo](_utils_.kdfinfo.md) - -# Enumeration: KDFInfo - -Info strings to separate distinct usages of the key derivation function - -## Index - -### Enumeration members - -* [FINALIZE](_utils_.kdfinfo.md#finalize) -* [FUSE_KEY](_utils_.kdfinfo.md#fuse_key) -* [ODIS_AUTH_KEY](_utils_.kdfinfo.md#odis_auth_key) -* [ODIS_KEY_HARDENING](_utils_.kdfinfo.md#odis_key_hardening) -* [PASSWORD](_utils_.kdfinfo.md#password) -* [PBKDF](_utils_.kdfinfo.md#pbkdf) -* [SCRYPT](_utils_.kdfinfo.md#scrypt) - -## Enumeration members - -### FINALIZE - -• **FINALIZE**: = "Celo Backup Key Finalization" - -*Defined in [packages/sdk/encrypted-backup/src/utils.ts:21](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/utils.ts#L21)* - -___ - -### FUSE_KEY - -• **FUSE_KEY**: = "Celo Backup Fuse Key" - -*Defined in [packages/sdk/encrypted-backup/src/utils.ts:16](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/utils.ts#L16)* - -___ - -### ODIS_AUTH_KEY - -• **ODIS_AUTH_KEY**: = "Celo Backup ODIS Request Authorization Key" - -*Defined in [packages/sdk/encrypted-backup/src/utils.ts:17](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/utils.ts#L17)* - -___ - -### ODIS_KEY_HARDENING - -• **ODIS_KEY_HARDENING**: = "Celo Backup ODIS Key Hardening" - -*Defined in [packages/sdk/encrypted-backup/src/utils.ts:18](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/utils.ts#L18)* - -___ - -### PASSWORD - -• **PASSWORD**: = "Celo Backup Password and Nonce" - -*Defined in [packages/sdk/encrypted-backup/src/utils.ts:15](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/utils.ts#L15)* - -___ - -### PBKDF - -• **PBKDF**: = "Celo Backup PBKDF Hardening" - -*Defined in [packages/sdk/encrypted-backup/src/utils.ts:19](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/utils.ts#L19)* - -___ - -### SCRYPT - -• **SCRYPT**: = "Celo Backup scrypt Hardening" - -*Defined in [packages/sdk/encrypted-backup/src/utils.ts:20](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/utils.ts#L20)* diff --git a/packages/docs/sdk/docs/encrypted-backup/interfaces/_backup_.backup.md b/packages/docs/sdk/docs/encrypted-backup/interfaces/_backup_.backup.md deleted file mode 100644 index d542d8f7fa2..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/interfaces/_backup_.backup.md +++ /dev/null @@ -1,140 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["backup"](../modules/_backup_.md) › [Backup](_backup_.backup.md) - -# Interface: Backup - -Backup structure encoding the information needed to implement the encrypted backup protocol. - -**`remarks`** The structure below and its related functions implement the encrypted backup protocol -designed for wallet account backups. More information about the protocol can be found in the -official [Celo documentation](https://docs.celo.org/celo-codebase/protocol/identity/encrypted-cloud-backup) - -## Hierarchy - -* **Backup** - -## Index - -### Properties - -* [computationalHardening](_backup_.backup.md#optional-computationalhardening) -* [encryptedData](_backup_.backup.md#encrypteddata) -* [encryptedFuseKey](_backup_.backup.md#optional-encryptedfusekey) -* [environment](_backup_.backup.md#optional-environment) -* [metadata](_backup_.backup.md#optional-metadata) -* [nonce](_backup_.backup.md#nonce) -* [odisDomain](_backup_.backup.md#optional-odisdomain) -* [version](_backup_.backup.md#version) - -## Properties - -### `Optional` computationalHardening - -• **computationalHardening**? : *[ComputationalHardeningConfig](../modules/_config_.md#computationalhardeningconfig)* - -*Defined in [packages/sdk/encrypted-backup/src/backup.ts:79](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/backup.ts#L79)* - -Options for local computational hardening of the encryption key through PBKDF or scrypt. - -**`remarks`** Adding computational hardening provides a measure of security from password guessing -when the password has a moderate amount of entropy (e.g. a password generated under good -guidelines). If the user secret has very low entropy, such as with a 6-digit PIN, -computational hardening does not add significant security. - -___ - -### encryptedData - -• **encryptedData**: *Buffer* - -*Defined in [packages/sdk/encrypted-backup/src/backup.ts:43](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/backup.ts#L43)* - -AES-128-GCM encryption of the user's secret backup data. - -**`remarks`** The backup key is derived from the user's password or PIN hardened with input from the -ODIS rate-limited hashing service and optionally a circuit breaker service. - -___ - -### `Optional` encryptedFuseKey - -• **encryptedFuseKey**? : *Buffer* - -*Defined in [packages/sdk/encrypted-backup/src/backup.ts:69](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/backup.ts#L69)* - -RSA-OAEP-256 encryption of a randomly chosen 128-bit value, the fuse key. - -**`remarks`** The fuse key, if provided, is combined with the password in local key derivation. -Encryption is under the public key of the circuit breaker service. In order to get the fuseKey -the client will send this ciphertext to the circuit breaker service for decryption. - -___ - -### `Optional` environment - -• **environment**? : *undefined | object* - -*Defined in [packages/sdk/encrypted-backup/src/backup.ts:105](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/backup.ts#L105)* - -Information including the URL and public keys of the ODIS and circuit breaker services. - -___ - -### `Optional` metadata - -• **metadata**? : *undefined | object* - -*Defined in [packages/sdk/encrypted-backup/src/backup.ts:102](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/backup.ts#L102)* - -Data provided by the backup creator to identify the backup and its context - -**`remarks`** Metadata is provided by, and only meaningful to, the SDK user. The intention is for -this metadata to be used for identifying the backup and providing any context needed in the -application - -**`example`** -```typescript -{ - // Address of the primary account stored a backup of an account key. Used to display the - // balance and latest transaction information for a given backup. - accountAddress: string - // Unix timestamp used to indicate when the backup was created. - timestamp: number -} -``` - -___ - -### nonce - -• **nonce**: *Buffer* - -*Defined in [packages/sdk/encrypted-backup/src/backup.ts:52](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/backup.ts#L52)* - -A randomly chosen 256-bit value. Ensures uniqueness of the password derived encryption key. - -**`remarks`** The nonce value is appended to the password for local key derivation. It is also used -to derive an authentication key to include in the ODIS Domain for domain separation and to -ensure quota cannot be consumed by parties without access to the backup. - -___ - -### `Optional` odisDomain - -• **odisDomain**? : *SequentialDelayDomain* - -*Defined in [packages/sdk/encrypted-backup/src/backup.ts:60](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/backup.ts#L60)* - -ODIS Domain instance to be included in the query to ODIS for password hardening, - -**`remarks`** Currently only SequentialDelayDomain is supported. Other ODIS domains intended for key -hardening may be supported in the future. - -___ - -### version - -• **version**: *string* - -*Defined in [packages/sdk/encrypted-backup/src/backup.ts:82](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/backup.ts#L82)* - -Version number for the backup feature. Used to facilitate backwards compatibility. diff --git a/packages/docs/sdk/docs/encrypted-backup/interfaces/_backup_.createbackupargs.md b/packages/docs/sdk/docs/encrypted-backup/interfaces/_backup_.createbackupargs.md deleted file mode 100644 index 9a353cd890e..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/interfaces/_backup_.createbackupargs.md +++ /dev/null @@ -1,48 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["backup"](../modules/_backup_.md) › [CreateBackupArgs](_backup_.createbackupargs.md) - -# Interface: CreateBackupArgs - -## Hierarchy - -* **CreateBackupArgs** - -## Index - -### Properties - -* [data](_backup_.createbackupargs.md#data) -* [hardening](_backup_.createbackupargs.md#hardening) -* [metadata](_backup_.createbackupargs.md#optional-metadata) -* [userSecret](_backup_.createbackupargs.md#usersecret) - -## Properties - -### data - -• **data**: *Buffer* - -*Defined in [packages/sdk/encrypted-backup/src/backup.ts:223](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/backup.ts#L223)* - -___ - -### hardening - -• **hardening**: *[HardeningConfig](_config_.hardeningconfig.md)* - -*Defined in [packages/sdk/encrypted-backup/src/backup.ts:225](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/backup.ts#L225)* - -___ - -### `Optional` metadata - -• **metadata**? : *undefined | object* - -*Defined in [packages/sdk/encrypted-backup/src/backup.ts:226](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/backup.ts#L226)* - -___ - -### userSecret - -• **userSecret**: *Buffer | string* - -*Defined in [packages/sdk/encrypted-backup/src/backup.ts:224](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/backup.ts#L224)* diff --git a/packages/docs/sdk/docs/encrypted-backup/interfaces/_backup_.createpasswordencryptedbackupargs.md b/packages/docs/sdk/docs/encrypted-backup/interfaces/_backup_.createpasswordencryptedbackupargs.md deleted file mode 100644 index 23fbf49036b..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/interfaces/_backup_.createpasswordencryptedbackupargs.md +++ /dev/null @@ -1,48 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["backup"](../modules/_backup_.md) › [CreatePasswordEncryptedBackupArgs](_backup_.createpasswordencryptedbackupargs.md) - -# Interface: CreatePasswordEncryptedBackupArgs - -## Hierarchy - -* **CreatePasswordEncryptedBackupArgs** - -## Index - -### Properties - -* [data](_backup_.createpasswordencryptedbackupargs.md#data) -* [environment](_backup_.createpasswordencryptedbackupargs.md#optional-environment) -* [metadata](_backup_.createpasswordencryptedbackupargs.md#optional-metadata) -* [password](_backup_.createpasswordencryptedbackupargs.md#password) - -## Properties - -### data - -• **data**: *Buffer* - -*Defined in [packages/sdk/encrypted-backup/src/backup.ts:171](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/backup.ts#L171)* - -___ - -### `Optional` environment - -• **environment**? : *[EnvironmentIdentifier](../enums/_config_.environmentidentifier.md)* - -*Defined in [packages/sdk/encrypted-backup/src/backup.ts:173](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/backup.ts#L173)* - -___ - -### `Optional` metadata - -• **metadata**? : *undefined | object* - -*Defined in [packages/sdk/encrypted-backup/src/backup.ts:174](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/backup.ts#L174)* - -___ - -### password - -• **password**: *string* - -*Defined in [packages/sdk/encrypted-backup/src/backup.ts:172](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/backup.ts#L172)* diff --git a/packages/docs/sdk/docs/encrypted-backup/interfaces/_backup_.createpinencryptedbackupargs.md b/packages/docs/sdk/docs/encrypted-backup/interfaces/_backup_.createpinencryptedbackupargs.md deleted file mode 100644 index 39a49ec31ad..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/interfaces/_backup_.createpinencryptedbackupargs.md +++ /dev/null @@ -1,48 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["backup"](../modules/_backup_.md) › [CreatePinEncryptedBackupArgs](_backup_.createpinencryptedbackupargs.md) - -# Interface: CreatePinEncryptedBackupArgs - -## Hierarchy - -* **CreatePinEncryptedBackupArgs** - -## Index - -### Properties - -* [data](_backup_.createpinencryptedbackupargs.md#data) -* [environment](_backup_.createpinencryptedbackupargs.md#optional-environment) -* [metadata](_backup_.createpinencryptedbackupargs.md#optional-metadata) -* [pin](_backup_.createpinencryptedbackupargs.md#pin) - -## Properties - -### data - -• **data**: *Buffer* - -*Defined in [packages/sdk/encrypted-backup/src/backup.ts:112](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/backup.ts#L112)* - -___ - -### `Optional` environment - -• **environment**? : *[EnvironmentIdentifier](../enums/_config_.environmentidentifier.md)* - -*Defined in [packages/sdk/encrypted-backup/src/backup.ts:114](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/backup.ts#L114)* - -___ - -### `Optional` metadata - -• **metadata**? : *undefined | object* - -*Defined in [packages/sdk/encrypted-backup/src/backup.ts:115](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/backup.ts#L115)* - -___ - -### pin - -• **pin**: *string* - -*Defined in [packages/sdk/encrypted-backup/src/backup.ts:113](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/backup.ts#L113)* diff --git a/packages/docs/sdk/docs/encrypted-backup/interfaces/_backup_.openbackupargs.md b/packages/docs/sdk/docs/encrypted-backup/interfaces/_backup_.openbackupargs.md deleted file mode 100644 index b749805265e..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/interfaces/_backup_.openbackupargs.md +++ /dev/null @@ -1,30 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["backup"](../modules/_backup_.md) › [OpenBackupArgs](_backup_.openbackupargs.md) - -# Interface: OpenBackupArgs - -## Hierarchy - -* **OpenBackupArgs** - -## Index - -### Properties - -* [backup](_backup_.openbackupargs.md#backup) -* [userSecret](_backup_.openbackupargs.md#usersecret) - -## Properties - -### backup - -• **backup**: *[Backup](_backup_.backup.md)* - -*Defined in [packages/sdk/encrypted-backup/src/backup.ts:383](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/backup.ts#L383)* - -___ - -### userSecret - -• **userSecret**: *Buffer | string* - -*Defined in [packages/sdk/encrypted-backup/src/backup.ts:384](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/backup.ts#L384)* diff --git a/packages/docs/sdk/docs/encrypted-backup/interfaces/_config_.circuitbreakerconfig.md b/packages/docs/sdk/docs/encrypted-backup/interfaces/_config_.circuitbreakerconfig.md deleted file mode 100644 index 852469863f4..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/interfaces/_config_.circuitbreakerconfig.md +++ /dev/null @@ -1,25 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["config"](../modules/_config_.md) › [CircuitBreakerConfig](_config_.circuitbreakerconfig.md) - -# Interface: CircuitBreakerConfig - -Configuration for usage of a circuit breaker to protect the encryption keys - -## Hierarchy - -* **CircuitBreakerConfig** - -## Index - -### Properties - -* [environment](_config_.circuitbreakerconfig.md#environment) - -## Properties - -### environment - -• **environment**: *CircuitBreakerServiceContext* - -*Defined in [packages/sdk/encrypted-backup/src/config.ts:54](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/config.ts#L54)* - -Environment information including the URL and public key of the circuit breaker service diff --git a/packages/docs/sdk/docs/encrypted-backup/interfaces/_config_.hardeningconfig.md b/packages/docs/sdk/docs/encrypted-backup/interfaces/_config_.hardeningconfig.md deleted file mode 100644 index 99b7194c76e..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/interfaces/_config_.hardeningconfig.md +++ /dev/null @@ -1,51 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["config"](../modules/_config_.md) › [HardeningConfig](_config_.hardeningconfig.md) - -# Interface: HardeningConfig - -## Hierarchy - -* **HardeningConfig** - -## Index - -### Properties - -* [circuitBreaker](_config_.hardeningconfig.md#optional-circuitbreaker) -* [computational](_config_.hardeningconfig.md#optional-computational) -* [odis](_config_.hardeningconfig.md#optional-odis) - -## Properties - -### `Optional` circuitBreaker - -• **circuitBreaker**? : *[CircuitBreakerConfig](_config_.circuitbreakerconfig.md)* - -*Defined in [packages/sdk/encrypted-backup/src/config.ts:33](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/config.ts#L33)* - -If provided, a circuit breaker will be used with the given configuration to protect the backup key - -___ - -### `Optional` computational - -• **computational**? : *[ComputationalHardeningConfig](../modules/_config_.md#computationalhardeningconfig)* - -*Defined in [packages/sdk/encrypted-backup/src/config.ts:25](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/config.ts#L25)* - -If provided, a computational hardening function (e.g. scrypt or PBKDF2) will be applied to -locally harden the backup encryption key. - -**`remarks`** Recommended for password-encrypted backups, especially if a circuit breaker is not in -use, as this provides some degree of protection in the event of an ODIS compromise. When -generating backups on low-power devices (e.g. budget smart phones) and encrypting with -low-entropy secrets (e.g. 6-digit PINs) local hardening cannot offer significant protection. - -___ - -### `Optional` odis - -• **odis**? : *[OdisHardeningConfig](_config_.odishardeningconfig.md)* - -*Defined in [packages/sdk/encrypted-backup/src/config.ts:28](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/config.ts#L28)* - -If provided, ODIS will be used with the given configuration to harden the backup key diff --git a/packages/docs/sdk/docs/encrypted-backup/interfaces/_config_.odishardeningconfig.md b/packages/docs/sdk/docs/encrypted-backup/interfaces/_config_.odishardeningconfig.md deleted file mode 100644 index 2b7dd43502a..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/interfaces/_config_.odishardeningconfig.md +++ /dev/null @@ -1,40 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["config"](../modules/_config_.md) › [OdisHardeningConfig](_config_.odishardeningconfig.md) - -# Interface: OdisHardeningConfig - -Configuration for usage of ODIS to harden the encryption keys - -## Hierarchy - -* **OdisHardeningConfig** - -## Index - -### Properties - -* [environment](_config_.odishardeningconfig.md#environment) -* [rateLimit](_config_.odishardeningconfig.md#ratelimit) - -## Properties - -### environment - -• **environment**: *OdisServiceContext* - -*Defined in [packages/sdk/encrypted-backup/src/config.ts:48](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/config.ts#L48)* - -Environment information including the URL and public key of the ODIS service - -___ - -### rateLimit - -• **rateLimit**: *SequentialDelayStage[]* - -*Defined in [packages/sdk/encrypted-backup/src/config.ts:45](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/config.ts#L45)* - -Rate limiting information used to construct the ODIS domain which will be used to harden the -encryption key through ODIS' domain password hardening service. - -**`remarks`** Currently supports the SequentialDelayDomain. In the future, as additional domains are -standardized for key hardening, they may be added here to allow a wider range of configuration. diff --git a/packages/docs/sdk/docs/encrypted-backup/interfaces/_config_.pbkdfconfig.md b/packages/docs/sdk/docs/encrypted-backup/interfaces/_config_.pbkdfconfig.md deleted file mode 100644 index a4a51c36c9a..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/interfaces/_config_.pbkdfconfig.md +++ /dev/null @@ -1,30 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["config"](../modules/_config_.md) › [PbkdfConfig](_config_.pbkdfconfig.md) - -# Interface: PbkdfConfig - -## Hierarchy - -* **PbkdfConfig** - -## Index - -### Properties - -* [function](_config_.pbkdfconfig.md#function) -* [iterations](_config_.pbkdfconfig.md#iterations) - -## Properties - -### function - -• **function**: *[PBKDF](../enums/_config_.computationalhardeningfunction.md#pbkdf)* - -*Defined in [packages/sdk/encrypted-backup/src/config.ts:63](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/config.ts#L63)* - -___ - -### iterations - -• **iterations**: *number* - -*Defined in [packages/sdk/encrypted-backup/src/config.ts:64](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/config.ts#L64)* diff --git a/packages/docs/sdk/docs/encrypted-backup/interfaces/_config_.scryptconfig.md b/packages/docs/sdk/docs/encrypted-backup/interfaces/_config_.scryptconfig.md deleted file mode 100644 index 032d6f7369b..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/interfaces/_config_.scryptconfig.md +++ /dev/null @@ -1,56 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["config"](../modules/_config_.md) › [ScryptConfig](_config_.scryptconfig.md) - -# Interface: ScryptConfig - -## Hierarchy - -* [ScryptOptions](_utils_.scryptoptions.md) - - ↳ **ScryptConfig** - -## Index - -### Properties - -* [blockSize](_config_.scryptconfig.md#optional-blocksize) -* [cost](_config_.scryptconfig.md#cost) -* [function](_config_.scryptconfig.md#function) -* [parallelization](_config_.scryptconfig.md#optional-parallelization) - -## Properties - -### `Optional` blockSize - -• **blockSize**? : *undefined | number* - -*Inherited from [ScryptOptions](_utils_.scryptoptions.md).[blockSize](_utils_.scryptoptions.md#optional-blocksize)* - -*Defined in [packages/sdk/encrypted-backup/src/utils.ts:117](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/utils.ts#L117)* - -___ - -### cost - -• **cost**: *number* - -*Inherited from [ScryptOptions](_utils_.scryptoptions.md).[cost](_utils_.scryptoptions.md#cost)* - -*Defined in [packages/sdk/encrypted-backup/src/utils.ts:116](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/utils.ts#L116)* - -___ - -### function - -• **function**: *[SCRYPT](../enums/_config_.computationalhardeningfunction.md#scrypt)* - -*Defined in [packages/sdk/encrypted-backup/src/config.ts:68](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/config.ts#L68)* - -___ - -### `Optional` parallelization - -• **parallelization**? : *undefined | number* - -*Inherited from [ScryptOptions](_utils_.scryptoptions.md).[parallelization](_utils_.scryptoptions.md#optional-parallelization)* - -*Defined in [packages/sdk/encrypted-backup/src/utils.ts:118](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/utils.ts#L118)* diff --git a/packages/docs/sdk/docs/encrypted-backup/interfaces/_utils_.scryptoptions.md b/packages/docs/sdk/docs/encrypted-backup/interfaces/_utils_.scryptoptions.md deleted file mode 100644 index 9e3f345958e..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/interfaces/_utils_.scryptoptions.md +++ /dev/null @@ -1,43 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["utils"](../modules/_utils_.md) › [ScryptOptions](_utils_.scryptoptions.md) - -# Interface: ScryptOptions - -Cost parameters for the scrypt computational hardening function. - -## Hierarchy - -* **ScryptOptions** - - ↳ [ScryptConfig](_config_.scryptconfig.md) - -## Index - -### Properties - -* [blockSize](_utils_.scryptoptions.md#optional-blocksize) -* [cost](_utils_.scryptoptions.md#cost) -* [parallelization](_utils_.scryptoptions.md#optional-parallelization) - -## Properties - -### `Optional` blockSize - -• **blockSize**? : *undefined | number* - -*Defined in [packages/sdk/encrypted-backup/src/utils.ts:117](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/utils.ts#L117)* - -___ - -### cost - -• **cost**: *number* - -*Defined in [packages/sdk/encrypted-backup/src/utils.ts:116](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/utils.ts#L116)* - -___ - -### `Optional` parallelization - -• **parallelization**? : *undefined | number* - -*Defined in [packages/sdk/encrypted-backup/src/utils.ts:118](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/utils.ts#L118)* diff --git a/packages/docs/sdk/docs/encrypted-backup/modules/_backup_.md b/packages/docs/sdk/docs/encrypted-backup/modules/_backup_.md deleted file mode 100644 index e35cdaa443c..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/modules/_backup_.md +++ /dev/null @@ -1,156 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["backup"](_backup_.md) - -# Module: "backup" - -## Index - -### Interfaces - -* [Backup](../interfaces/_backup_.backup.md) -* [CreateBackupArgs](../interfaces/_backup_.createbackupargs.md) -* [CreatePasswordEncryptedBackupArgs](../interfaces/_backup_.createpasswordencryptedbackupargs.md) -* [CreatePinEncryptedBackupArgs](../interfaces/_backup_.createpinencryptedbackupargs.md) -* [OpenBackupArgs](../interfaces/_backup_.openbackupargs.md) - -### Functions - -* [createBackup](_backup_.md#createbackup) -* [createPasswordEncryptedBackup](_backup_.md#createpasswordencryptedbackup) -* [createPinEncryptedBackup](_backup_.md#createpinencryptedbackup) -* [openBackup](_backup_.md#openbackup) - -## Functions - -### createBackup - -▸ **createBackup**(`__namedParameters`: object): *Promise‹Result‹[Backup](../interfaces/_backup_.backup.md), [BackupError](_errors_.md#backuperror)››* - -*Defined in [packages/sdk/encrypted-backup/src/backup.ts:247](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/backup.ts#L247)* - -Create a data backup, encrypting it with a hardened key derived from the given password or PIN. - -**`privateremarks`** Most of this functions code is devoted to key generation starting with the input -password or PIN and ending up with a hardened encryption key. It is important that the order and -inputs to each step in the derivation be well considered and implemented correctly. One important -requirement is that no output included in the backup acts as a "commitment" to the password or PIN -value, except the final ciphertext. An example of an issue with this would be if a hash of the -password and nonce were included in the backup. If a commitment to the password or PIN is -included, an attacker can locally brute force that commitment to recover the password, then use -that knowledge to complete the derivation. - -**Parameters:** - -▪ **__namedParameters**: *object* - -Name | Type | Description | ------- | ------ | ------ | -`data` | Buffer‹› | The secret data (e.g. BIP-39 mnemonic phrase) to be included in the encrypted backup. | -`hardening` | [HardeningConfig](../interfaces/_config_.hardeningconfig.md) | Configuration for how the password should be hardened in deriving the key. | -`metadata` | undefined | object | Arbitrary key-value data to include in the backup to identify it. | -`userSecret` | string | Buffer‹› | Password, PIN, or other user secret to use in deriving the encryption key. If a string is provided, it will be UTF-8 encoded into a Buffer before use. | - -**Returns:** *Promise‹Result‹[Backup](../interfaces/_backup_.backup.md), [BackupError](_errors_.md#backuperror)››* - -___ - -### createPasswordEncryptedBackup - -▸ **createPasswordEncryptedBackup**(`__namedParameters`: object): *Promise‹Result‹[Backup](../interfaces/_backup_.backup.md), [BackupError](_errors_.md#backuperror)››* - -*Defined in [packages/sdk/encrypted-backup/src/backup.ts:202](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/backup.ts#L202)* - -Create a data backup, encrypting it with a hardened key derived from the given password. - -**`remarks`** Because passwords have moderate entropy, the total number of guesses is restricted. - * The user initially gets 5 attempts without delay. - * Then the user gets two attempts every 5 seconds for up to 20 attempts. - * Then the user gets two attempts every 30 seconds for up to 20 attempts. - * Then the user gets two attempts every 5 minutes for up to 20 attempts. - * Then the user gets two attempts every hour for up to 20 attempts. - * Then the user gets two attempts every day for up to 20 attempts. - -Following guidelines in NIST-800-63-3 it is strongly recommended that the caller apply a password -blocklist to the users choice of password. - -In order to handle the event of an ODIS service compromise, this configuration additionally -hardens the password input with a computational hardening function. In particular, scrypt is used -with IETF recommended parameters [IETF recommended scrypt parameters](https://tools.ietf.org/id/draft-whited-kitten-password-storage-00.html#name-scrypt) - -**Parameters:** - -▪ **__namedParameters**: *object* - -Name | Type | Description | ------- | ------ | ------ | -`data` | Buffer‹› | The secret data (e.g. BIP-39 mnemonic phrase) to be included in the encrypted backup. | -`environment` | undefined | [MAINNET](../enums/_config_.environmentidentifier.md#mainnet) | [ALFAJORES](../enums/_config_.environmentidentifier.md#alfajores) | - | -`metadata` | undefined | object | Arbitrary key-value data to include in the backup to identify it. | -`password` | string | Password to use in deriving the encryption key. | - -**Returns:** *Promise‹Result‹[Backup](../interfaces/_backup_.backup.md), [BackupError](_errors_.md#backuperror)››* - -___ - -### createPinEncryptedBackup - -▸ **createPinEncryptedBackup**(`__namedParameters`: object): *Promise‹Result‹[Backup](../interfaces/_backup_.backup.md), [BackupError](_errors_.md#backuperror)››* - -*Defined in [packages/sdk/encrypted-backup/src/backup.ts:150](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/backup.ts#L150)* - -Create a data backup, encrypting it with a hardened key derived from the given PIN. - -**`remarks`** Using a 4 or 6 digit PIN for encryption requires an extremely restrictive rate limit for -attempts to guess the PIN. This is enforced by ODIS through the SequentialDelayDomain with -settings to allow the user (or an attacker) only a fixed number of attempts to guess their PIN. - -Because PINs have very little entropy, the total number of guesses is very restricted. - * On the first day, the client has 10 attempts. 5 within 10s. 5 more over roughly 45 minutes. - * On the second day, the client has 5 attempts over roughly 2 minutes. - * On the third day, the client has 3 attempts over roughly 40 seconds. - * On the fourth day, the client has 2 attempts over roughly 10 seconds. - * Overall, the client has 25 attempts over 4 days. All further attempts will be denied. - -It is strongly recommended that the calling application implement a PIN blocklist to prevent the -user from selecting a number of the most common PIN codes (e.g. blocking the top 25k PINs by -frequency of appearance in the HIBP Passwords dataset). An example implementation can be seen in -the Valora wallet. [PIN blocklist implementation](https://github.com/valora-inc/wallet/blob/3940661c40d08e4c5db952bd0abeaabb0030fc7a/packages/mobile/src/pincode/authentication.ts#L56-L108) - -In order to handle the event of an ODIS service compromise, this configuration additionally -includes a circuit breaker service run by Valora. In the event of an ODIS compromise, the Valora -team will take their service offline, preventing backups using the circuit breaker from being -opened. This ensures that an attacker who has compromised ODIS cannot leverage their attack to -forcibly open backups created with this function. - -**Parameters:** - -▪ **__namedParameters**: *object* - -Name | Type | Description | ------- | ------ | ------ | -`data` | Buffer‹› | The secret data (e.g. BIP-39 mnemonic phrase) to be included in the encrypted backup. | -`environment` | undefined | [MAINNET](../enums/_config_.environmentidentifier.md#mainnet) | [ALFAJORES](../enums/_config_.environmentidentifier.md#alfajores) | - | -`metadata` | undefined | object | Arbitrary key-value data to include in the backup to identify it. | -`pin` | string | PIN to use in deriving the encryption key. | - -**Returns:** *Promise‹Result‹[Backup](../interfaces/_backup_.backup.md), [BackupError](_errors_.md#backuperror)››* - -___ - -### openBackup - -▸ **openBackup**(`__namedParameters`: object): *Promise‹Result‹Buffer, [BackupError](_errors_.md#backuperror)››* - -*Defined in [packages/sdk/encrypted-backup/src/backup.ts:394](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/backup.ts#L394)* - -Open an encrypted backup file, using the provided password or PIN to derive the decryption key. - -**Parameters:** - -▪ **__namedParameters**: *object* - -Name | Type | Description | ------- | ------ | ------ | -`backup` | [Backup](../interfaces/_backup_.backup.md) | Backup structure including the ciphertext and key derivation information. | -`userSecret` | string | Buffer‹› | Password, PIN, or other user secret to use in deriving the encryption key. If a string is provided, it will be UTF-8 encoded into a Buffer before use. | - -**Returns:** *Promise‹Result‹Buffer, [BackupError](_errors_.md#backuperror)››* diff --git a/packages/docs/sdk/docs/encrypted-backup/modules/_config_.md b/packages/docs/sdk/docs/encrypted-backup/modules/_config_.md deleted file mode 100644 index f7cabd87e3a..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/modules/_config_.md +++ /dev/null @@ -1,137 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["config"](_config_.md) - -# Module: "config" - -## Index - -### Enumerations - -* [ComputationalHardeningFunction](../enums/_config_.computationalhardeningfunction.md) -* [EnvironmentIdentifier](../enums/_config_.environmentidentifier.md) - -### Interfaces - -* [CircuitBreakerConfig](../interfaces/_config_.circuitbreakerconfig.md) -* [HardeningConfig](../interfaces/_config_.hardeningconfig.md) -* [OdisHardeningConfig](../interfaces/_config_.odishardeningconfig.md) -* [PbkdfConfig](../interfaces/_config_.pbkdfconfig.md) -* [ScryptConfig](../interfaces/_config_.scryptconfig.md) - -### Type aliases - -* [ComputationalHardeningConfig](_config_.md#computationalhardeningconfig) - -### Object literals - -* [PASSWORD_HARDENING_ALFAJORES_CONFIG](_config_.md#const-password_hardening_alfajores_config) -* [PASSWORD_HARDENING_MAINNET_CONFIG](_config_.md#const-password_hardening_mainnet_config) -* [PIN_HARDENING_ALFAJORES_CONFIG](_config_.md#const-pin_hardening_alfajores_config) -* [PIN_HARDENING_MAINNET_CONFIG](_config_.md#const-pin_hardening_mainnet_config) - -## Type aliases - -### ComputationalHardeningConfig - -Ƭ **ComputationalHardeningConfig**: *[PbkdfConfig](../interfaces/_config_.pbkdfconfig.md) | [ScryptConfig](../interfaces/_config_.scryptconfig.md)* - -*Defined in [packages/sdk/encrypted-backup/src/config.ts:71](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/config.ts#L71)* - -## Object literals - -### `Const` PASSWORD_HARDENING_ALFAJORES_CONFIG - -### ▪ **PASSWORD_HARDENING_ALFAJORES_CONFIG**: *object* - -*Defined in [packages/sdk/encrypted-backup/src/config.ts:289](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/config.ts#L289)* - -▪ **computational**: *object* - -*Defined in [packages/sdk/encrypted-backup/src/config.ts:294](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/config.ts#L294)* - -* **blockSize**: *number* = 8 - -* **cost**: *number* = 32768 - -* **function**: *[SCRYPT](../enums/_config_.computationalhardeningfunction.md#scrypt)* = ComputationalHardeningFunction.SCRYPT - -* **parallelization**: *number* = 1 - -▪ **odis**: *object* - -*Defined in [packages/sdk/encrypted-backup/src/config.ts:290](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/config.ts#L290)* - -* **environment**: *ServiceContext* = ODIS_ALFAJORES_CONTEXT - -* **rateLimit**: *SequentialDelayStage[]* = PASSWORD_HARDENING_RATE_LIMIT - -___ - -### `Const` PASSWORD_HARDENING_MAINNET_CONFIG - -### ▪ **PASSWORD_HARDENING_MAINNET_CONFIG**: *object* - -*Defined in [packages/sdk/encrypted-backup/src/config.ts:276](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/config.ts#L276)* - -▪ **computational**: *object* - -*Defined in [packages/sdk/encrypted-backup/src/config.ts:281](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/config.ts#L281)* - -* **blockSize**: *number* = 8 - -* **cost**: *number* = 32768 - -* **function**: *[SCRYPT](../enums/_config_.computationalhardeningfunction.md#scrypt)* = ComputationalHardeningFunction.SCRYPT - -* **parallelization**: *number* = 1 - -▪ **odis**: *object* - -*Defined in [packages/sdk/encrypted-backup/src/config.ts:277](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/config.ts#L277)* - -* **environment**: *ServiceContext* = ODIS_MAINNET_CONTEXT - -* **rateLimit**: *SequentialDelayStage[]* = PASSWORD_HARDENING_RATE_LIMIT - -___ - -### `Const` PIN_HARDENING_ALFAJORES_CONFIG - -### ▪ **PIN_HARDENING_ALFAJORES_CONFIG**: *object* - -*Defined in [packages/sdk/encrypted-backup/src/config.ts:266](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/config.ts#L266)* - -▪ **circuitBreaker**: *object* - -*Defined in [packages/sdk/encrypted-backup/src/config.ts:271](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/config.ts#L271)* - -* **environment**: *CircuitBreakerServiceContext* = VALORA_ALFAJORES_CIRCUIT_BREAKER_ENVIRONMENT - -▪ **odis**: *object* - -*Defined in [packages/sdk/encrypted-backup/src/config.ts:267](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/config.ts#L267)* - -* **environment**: *ServiceContext* = ODIS_ALFAJORES_CONTEXT - -* **rateLimit**: *SequentialDelayStage[]* = PIN_HARDENING_RATE_LIMIT - -___ - -### `Const` PIN_HARDENING_MAINNET_CONFIG - -### ▪ **PIN_HARDENING_MAINNET_CONFIG**: *object* - -*Defined in [packages/sdk/encrypted-backup/src/config.ts:256](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/config.ts#L256)* - -▪ **circuitBreaker**: *object* - -*Defined in [packages/sdk/encrypted-backup/src/config.ts:261](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/config.ts#L261)* - -* **environment**: *CircuitBreakerServiceContext* = VALORA_MAINNET_CIRCUIT_BREAKER_ENVIRONMENT - -▪ **odis**: *object* - -*Defined in [packages/sdk/encrypted-backup/src/config.ts:257](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/config.ts#L257)* - -* **environment**: *ServiceContext* = ODIS_MAINNET_CONTEXT - -* **rateLimit**: *SequentialDelayStage[]* = PIN_HARDENING_RATE_LIMIT diff --git a/packages/docs/sdk/docs/encrypted-backup/modules/_errors_.md b/packages/docs/sdk/docs/encrypted-backup/modules/_errors_.md deleted file mode 100644 index 7e484fbb3aa..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/modules/_errors_.md +++ /dev/null @@ -1,36 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["errors"](_errors_.md) - -# Module: "errors" - -## Index - -### Enumerations - -* [BackupErrorTypes](../enums/_errors_.backuperrortypes.md) - -### Classes - -* [AuthorizationError](../classes/_errors_.authorizationerror.md) -* [DecodeError](../classes/_errors_.decodeerror.md) -* [DecryptionError](../classes/_errors_.decryptionerror.md) -* [EncryptionError](../classes/_errors_.encryptionerror.md) -* [FetchError](../classes/_errors_.fetcherror.md) -* [InvalidBackupError](../classes/_errors_.invalidbackuperror.md) -* [OdisRateLimitingError](../classes/_errors_.odisratelimitingerror.md) -* [OdisServiceError](../classes/_errors_.odisserviceerror.md) -* [OdisVerificationError](../classes/_errors_.odisverificationerror.md) -* [PbkdfError](../classes/_errors_.pbkdferror.md) -* [ScryptError](../classes/_errors_.scrypterror.md) -* [UsageError](../classes/_errors_.usageerror.md) - -### Type aliases - -* [BackupError](_errors_.md#backuperror) - -## Type aliases - -### BackupError - -Ƭ **BackupError**: *[AuthorizationError](../classes/_errors_.authorizationerror.md) | CircuitBreakerError | [DecodeError](../classes/_errors_.decodeerror.md) | [DecryptionError](../classes/_errors_.decryptionerror.md) | [EncryptionError](../classes/_errors_.encryptionerror.md) | [FetchError](../classes/_errors_.fetcherror.md) | [InvalidBackupError](../classes/_errors_.invalidbackuperror.md) | [OdisServiceError](../classes/_errors_.odisserviceerror.md) | [OdisRateLimitingError](../classes/_errors_.odisratelimitingerror.md) | [OdisVerificationError](../classes/_errors_.odisverificationerror.md) | [PbkdfError](../classes/_errors_.pbkdferror.md) | [ScryptError](../classes/_errors_.scrypterror.md) | [UsageError](../classes/_errors_.usageerror.md)* - -*Defined in [packages/sdk/encrypted-backup/src/errors.ts:92](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/errors.ts#L92)* diff --git a/packages/docs/sdk/docs/encrypted-backup/modules/_odis_.md b/packages/docs/sdk/docs/encrypted-backup/modules/_odis_.md deleted file mode 100644 index 90f7377e8dc..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/modules/_odis_.md +++ /dev/null @@ -1,82 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["odis"](_odis_.md) - -# Module: "odis" - -## Index - -### Functions - -* [buildOdisDomain](_odis_.md#buildodisdomain) -* [odisHardenKey](_odis_.md#odishardenkey) -* [odisQueryAuthorizer](_odis_.md#odisqueryauthorizer) - -## Functions - -### buildOdisDomain - -▸ **buildOdisDomain**(`config`: [OdisHardeningConfig](../interfaces/_config_.odishardeningconfig.md), `authorizer`: Address, `salt?`: undefined | string): *SequentialDelayDomain* - -*Defined in [packages/sdk/encrypted-backup/src/odis.ts:50](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/odis.ts#L50)* - -Builds an ODIS SequentialDelayDomain with the given hardening configuration. - -**Parameters:** - -Name | Type | Description | ------- | ------ | ------ | -`config` | [OdisHardeningConfig](../interfaces/_config_.odishardeningconfig.md) | - | -`authorizer` | Address | Address of the key that should authorize requests to ODIS. | -`salt?` | undefined | string | - | - -**Returns:** *SequentialDelayDomain* - -A SequentialDelayDomain with the provided rate limiting configuration. - -___ - -### odisHardenKey - -▸ **odisHardenKey**(`key`: Buffer, `domain`: SequentialDelayDomain, `environment`: OdisServiceContext, `wallet?`: [EIP712Wallet](_utils_.md#eip712wallet)): *Promise‹Result‹Buffer, [BackupError](_errors_.md#backuperror)››* - -*Defined in [packages/sdk/encrypted-backup/src/odis.ts:74](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/odis.ts#L74)* - -Returns a hardened key derived from the input key material and a POPRF evaluation on that keying -material under the given rate limiting domain. - -**Parameters:** - -Name | Type | Description | ------- | ------ | ------ | -`key` | Buffer | Input key material which will be the blinded input to the ODIS POPRF. | -`domain` | SequentialDelayDomain | Rate limiting configuration and domain input to the ODIS POPRF. | -`environment` | OdisServiceContext | Information for the targeted ODIS environment. | -`wallet?` | [EIP712Wallet](_utils_.md#eip712wallet) | Wallet with access to the authorizer signing key specified in the domain input. Should be provided if the input domain is authenticated. | - -**Returns:** *Promise‹Result‹Buffer, [BackupError](_errors_.md#backuperror)››* - -___ - -### odisQueryAuthorizer - -▸ **odisQueryAuthorizer**(`nonce`: Buffer): *object* - -*Defined in [packages/sdk/encrypted-backup/src/odis.ts:158](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/odis.ts#L158)* - -Derive from the nonce a private key and use it to instantiate a wallet for request signing - -**`remarks`** It is important that the auth key does not mix in entropy from the password value. If -it did, then the derived address and signatures would act as a commitment to the underlying -password value and would allow offline brute force attacks when combined with the other values -mixed into the key value. - -**Parameters:** - -Name | Type | ------- | ------ | -`nonce` | Buffer | - -**Returns:** *object* - -* **address**: *Address* - -* **wallet**: *[EIP712Wallet](_utils_.md#eip712wallet)* diff --git a/packages/docs/sdk/docs/encrypted-backup/modules/_odis_mock_.md b/packages/docs/sdk/docs/encrypted-backup/modules/_odis_mock_.md deleted file mode 100644 index bb93003cd27..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/modules/_odis_mock_.md +++ /dev/null @@ -1,33 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["odis.mock"](_odis_mock_.md) - -# Module: "odis.mock" - -## Index - -### Classes - -* [MockOdis](../classes/_odis_mock_.mockodis.md) - -### Object literals - -* [MOCK_ODIS_ENVIRONMENT](_odis_mock_.md#const-mock_odis_environment) - -## Object literals - -### `Const` MOCK_ODIS_ENVIRONMENT - -### ▪ **MOCK_ODIS_ENVIRONMENT**: *object* - -*Defined in [packages/sdk/encrypted-backup/src/odis.mock.ts:23](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/odis.mock.ts#L23)* - -### odisPubKey - -• **odisPubKey**: *string* = Buffer.from(MOCK_ODIS_KEYPAIR.publicKey).toString('base64') - -*Defined in [packages/sdk/encrypted-backup/src/odis.mock.ts:25](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/odis.mock.ts#L25)* - -### odisUrl - -• **odisUrl**: *string* = "https://mockodis.com" - -*Defined in [packages/sdk/encrypted-backup/src/odis.mock.ts:24](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/odis.mock.ts#L24)* diff --git a/packages/docs/sdk/docs/encrypted-backup/modules/_schema_.md b/packages/docs/sdk/docs/encrypted-backup/modules/_schema_.md deleted file mode 100644 index 07bc7e930f4..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/modules/_schema_.md +++ /dev/null @@ -1,122 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["schema"](_schema_.md) - -# Module: "schema" - -## Index - -### Variables - -* [BackupSchema](_schema_.md#const-backupschema) -* [BufferFromBase64](_schema_.md#const-bufferfrombase64) - -### Functions - -* [deserializeBackup](_schema_.md#deserializebackup) -* [serializeBackup](_schema_.md#serializebackup) - -## Variables - -### `Const` BackupSchema - -• **BackupSchema**: *Type‹[Backup](../interfaces/_backup_.backup.md), object›* = t.intersection([ - // Required fields - t.type({ - encryptedData: BufferFromBase64, - nonce: BufferFromBase64, - version: t.string, - }), - // Optional fields - // https://github.com/gcanti/io-ts/blob/master/index.md#mixing-required-and-optional-props - t.partial({ - odisDomain: SequentialDelayDomainSchema, - metadata: t.UnknownRecord, - encryptedFuseKey: BufferFromBase64, - computationalHardening: t.union([ - t.type({ - function: t.literal(ComputationalHardeningFunction.PBKDF), - iterations: t.number, - }), - t.intersection([ - t.type({ - function: t.literal(ComputationalHardeningFunction.SCRYPT), - cost: t.number, - }), - t.partial({ - blockSize: t.number, - parallelization: t.number, - }), - ]), - ]), - environment: t.partial({ - odis: t.type({ - odisUrl: t.string, - odisPubKey: t.string, - }), - circuitBreaker: t.type({ - url: t.string, - publicKey: t.string, - }), - }), - }), -]) - -*Defined in [packages/sdk/encrypted-backup/src/schema.ts:31](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/schema.ts#L31)* - -io-ts codec used to encode and decode backups from JSON objects - -___ - -### `Const` BufferFromBase64 - -• **BufferFromBase64**: *Type‹Buffer‹›, string, unknown›* = new t.Type( - 'BufferFromBase64', - Buffer.isBuffer, - (unk: unknown, context: t.Context) => - pipe( - t.string.validate(unk, context), - chain((str: string) => { - // Check that the string is base64 data and return the decoding if it is. - if (!BASE64_REGEXP.test(str)) { - return t.failure(unk, context, 'provided string is not base64') - } - return t.success(Buffer.from(str, 'base64')) - }) - ), - (buffer: Buffer) => buffer.toString('base64') -) - -*Defined in [packages/sdk/encrypted-backup/src/schema.ts:13](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/schema.ts#L13)* - -Utility type to leverage io-ts for encoding and decoding of buffers from base64 strings. - -## Functions - -### deserializeBackup - -▸ **deserializeBackup**(`data`: string): *Result‹[Backup](../interfaces/_backup_.backup.md), [DecodeError](../classes/_errors_.decodeerror.md)›* - -*Defined in [packages/sdk/encrypted-backup/src/schema.ts:77](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/schema.ts#L77)* - -**Parameters:** - -Name | Type | ------- | ------ | -`data` | string | - -**Returns:** *Result‹[Backup](../interfaces/_backup_.backup.md), [DecodeError](../classes/_errors_.decodeerror.md)›* - -___ - -### serializeBackup - -▸ **serializeBackup**(`backup`: [Backup](../interfaces/_backup_.backup.md)): *string* - -*Defined in [packages/sdk/encrypted-backup/src/schema.ts:73](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/schema.ts#L73)* - -**Parameters:** - -Name | Type | ------- | ------ | -`backup` | [Backup](../interfaces/_backup_.backup.md) | - -**Returns:** *string* diff --git a/packages/docs/sdk/docs/encrypted-backup/modules/_utils_.md b/packages/docs/sdk/docs/encrypted-backup/modules/_utils_.md deleted file mode 100644 index fda5c4bd23c..00000000000 --- a/packages/docs/sdk/docs/encrypted-backup/modules/_utils_.md +++ /dev/null @@ -1,169 +0,0 @@ -[@celo/encrypted-backup](../README.md) › ["utils"](_utils_.md) - -# Module: "utils" - -## Index - -### Enumerations - -* [KDFInfo](../enums/_utils_.kdfinfo.md) - -### Interfaces - -* [ScryptOptions](../interfaces/_utils_.scryptoptions.md) - -### Type aliases - -* [EIP712Wallet](_utils_.md#eip712wallet) - -### Functions - -* [computationalHardenKey](_utils_.md#computationalhardenkey) -* [decrypt](_utils_.md#decrypt) -* [deriveKey](_utils_.md#derivekey) -* [encrypt](_utils_.md#encrypt) -* [pbkdf2](_utils_.md#pbkdf2) -* [scrypt](_utils_.md#scrypt) - -## Type aliases - -### EIP712Wallet - -Ƭ **EIP712Wallet**: *Pick‹ReadOnlyWallet, "getAccounts" | "hasAccount" | "signTypedData"›* - -*Defined in [packages/sdk/encrypted-backup/src/utils.ts:11](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/utils.ts#L11)* - -Pared down ReadOnlyWallet type that supports the required functions of EIP-712 signing. - -## Functions - -### computationalHardenKey - -▸ **computationalHardenKey**(`key`: Buffer, `config`: [ComputationalHardeningConfig](_config_.md#computationalhardeningconfig)): *Promise‹Result‹Buffer, [PbkdfError](../classes/_errors_.pbkdferror.md) | [ScryptError](../classes/_errors_.scrypterror.md)››* - -*Defined in [packages/sdk/encrypted-backup/src/utils.ts:148](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/utils.ts#L148)* - -**Parameters:** - -Name | Type | ------- | ------ | -`key` | Buffer | -`config` | [ComputationalHardeningConfig](_config_.md#computationalhardeningconfig) | - -**Returns:** *Promise‹Result‹Buffer, [PbkdfError](../classes/_errors_.pbkdferror.md) | [ScryptError](../classes/_errors_.scrypterror.md)››* - -___ - -### decrypt - -▸ **decrypt**(`key`: Buffer, `ciphertext`: Buffer): *Result‹Buffer, [DecryptionError](../classes/_errors_.decryptionerror.md)›* - -*Defined in [packages/sdk/encrypted-backup/src/utils.ts:66](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/utils.ts#L66)* - -AES-256-GCM decrypt the given data with the given 32-byte key. -Ciphertext should be encoded as { iv || data || auth tag }. - -**Parameters:** - -Name | Type | ------- | ------ | -`key` | Buffer | -`ciphertext` | Buffer | - -**Returns:** *Result‹Buffer, [DecryptionError](../classes/_errors_.decryptionerror.md)›* - -___ - -### deriveKey - -▸ **deriveKey**(`info`: [KDFInfo](../enums/_utils_.kdfinfo.md), `sources`: Buffer[]): *Buffer* - -*Defined in [packages/sdk/encrypted-backup/src/utils.ts:34](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/utils.ts#L34)* - -Key derivation function for mixing source keying material. - -**`remarks`** This function does not add any hardening to the input keying material. It is used only -to mix the provided key material sources. It's output should not be used to directly derive a key -from a password or other low entropy sources. - -**Parameters:** - -Name | Type | Description | ------- | ------ | ------ | -`info` | [KDFInfo](../enums/_utils_.kdfinfo.md) | Fixed string value used for domain separation. | -`sources` | Buffer[] | An array of keying material source values (e.g. a password and a nonce). | - -**Returns:** *Buffer* - -___ - -### encrypt - -▸ **encrypt**(`key`: Buffer, `data`: Buffer): *Result‹Buffer, [EncryptionError](../classes/_errors_.encryptionerror.md)›* - -*Defined in [packages/sdk/encrypted-backup/src/utils.ts:51](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/utils.ts#L51)* - -AES-256-GCM encrypt the given data with the given 32-byte key. -Encode the ciphertext as { iv || data || auth tag } - -**Parameters:** - -Name | Type | ------- | ------ | -`key` | Buffer | -`data` | Buffer | - -**Returns:** *Result‹Buffer, [EncryptionError](../classes/_errors_.encryptionerror.md)›* - -___ - -### pbkdf2 - -▸ **pbkdf2**(`key`: Buffer, `iterations`: number): *Promise‹Result‹Buffer, [PbkdfError](../classes/_errors_.pbkdferror.md)››* - -*Defined in [packages/sdk/encrypted-backup/src/utils.ts:103](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/utils.ts#L103)* - -PBKDF2-SHA256 computational key hardening. - -**`remarks`** When possible, a memory hard function such as scrypt should be used instead. -No salt parameter is provided as the intended use case of this function is to harden a -key value which is derived from a password but already has the salt mixed in. - -**`see`** { @link -https://nodejs.org/api/crypto.html#cryptopbkdf2password-salt-iterations-keylen-digest-callback | -NodeJS crypto.pbkdf2 API } - -**Parameters:** - -Name | Type | Description | ------- | ------ | ------ | -`key` | Buffer | Key buffer to compute hardening against. Should have a salt or nonce mixed in. | -`iterations` | number | Number of PBKDF2 iterations to execute for key hardening. | - -**Returns:** *Promise‹Result‹Buffer, [PbkdfError](../classes/_errors_.pbkdferror.md)››* - -___ - -### scrypt - -▸ **scrypt**(`key`: Buffer, `options`: [ScryptOptions](../interfaces/_utils_.scryptoptions.md)): *Promise‹Result‹Buffer, [ScryptError](../classes/_errors_.scrypterror.md)››* - -*Defined in [packages/sdk/encrypted-backup/src/utils.ts:134](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/encrypted-backup/src/utils.ts#L134)* - -scrypt computational key hardening. - -**`remarks`** No salt parameter is provided as the intended use case of this function is to harden a -key value which is derived from a password but already has the salt mixed in. - -**`see`** { @link -https://nodejs.org/api/crypto.html#cryptoscryptpassword-salt-keylen-options-callback | -NodeJS crypto.scrypt API } - -**Parameters:** - -Name | Type | Description | ------- | ------ | ------ | -`key` | Buffer | Key buffer to compute hardening against. Should have a salt or nonce mixed in. | -`options` | [ScryptOptions](../interfaces/_utils_.scryptoptions.md) | Options to control the cost of the scrypt function. | - -**Returns:** *Promise‹Result‹Buffer, [ScryptError](../classes/_errors_.scrypterror.md)››* diff --git a/packages/docs/sdk/docs/phone-utils/modules/_getcountryemoji_.md b/packages/docs/sdk/docs/phone-utils/modules/_getcountryemoji_.md index 9f935271cce..1aee69d5bc2 100644 --- a/packages/docs/sdk/docs/phone-utils/modules/_getcountryemoji_.md +++ b/packages/docs/sdk/docs/phone-utils/modules/_getcountryemoji_.md @@ -12,7 +12,7 @@ ### getCountryEmoji -▸ **getCountryEmoji**(`e164PhoneNumber`: string, `countryCodePossible?`: undefined | number, `regionCodePossible?`: undefined | string): *string* +▸ **getCountryEmoji**(`e164PhoneNumber`: string, `countryCodePossible?`: undefined | number, `regionCodePossible?`: undefined | string): *[getCountryEmoji](_getcountryemoji_.md#getcountryemoji)* *Defined in [getCountryEmoji.ts:4](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/phone-utils/src/getCountryEmoji.ts#L4)* @@ -24,4 +24,4 @@ Name | Type | `countryCodePossible?` | undefined | number | `regionCodePossible?` | undefined | string | -**Returns:** *string* +**Returns:** *[getCountryEmoji](_getcountryemoji_.md#getcountryemoji)* diff --git a/packages/docs/sdk/docs/phone-utils/modules/_phonenumbers_.md b/packages/docs/sdk/docs/phone-utils/modules/_phonenumbers_.md index 85a1317bfc8..adf3d34643d 100644 --- a/packages/docs/sdk/docs/phone-utils/modules/_phonenumbers_.md +++ b/packages/docs/sdk/docs/phone-utils/modules/_phonenumbers_.md @@ -25,7 +25,7 @@ ### getCountryCode -▸ **getCountryCode**(`e164PhoneNumber`: string): *undefined | null | number* +▸ **getCountryCode**(`e164PhoneNumber`: string): *[getCountryCode](_phonenumbers_.md#getcountrycode)* *Defined in [phoneNumbers.ts:13](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/phone-utils/src/phoneNumbers.ts#L13)* @@ -35,13 +35,13 @@ Name | Type | ------ | ------ | `e164PhoneNumber` | string | -**Returns:** *undefined | null | number* +**Returns:** *[getCountryCode](_phonenumbers_.md#getcountrycode)* ___ ### getDisplayNumberInternational -▸ **getDisplayNumberInternational**(`e164PhoneNumber`: string): *string* +▸ **getDisplayNumberInternational**(`e164PhoneNumber`: string): *[getDisplayNumberInternational](_phonenumbers_.md#getdisplaynumberinternational)* *Defined in [phoneNumbers.ts:59](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/phone-utils/src/phoneNumbers.ts#L59)* @@ -51,13 +51,13 @@ Name | Type | ------ | ------ | `e164PhoneNumber` | string | -**Returns:** *string* +**Returns:** *[getDisplayNumberInternational](_phonenumbers_.md#getdisplaynumberinternational)* ___ ### getDisplayPhoneNumber -▸ **getDisplayPhoneNumber**(`phoneNumber`: string, `defaultCountryCode`: string): *string* +▸ **getDisplayPhoneNumber**(`phoneNumber`: string, `defaultCountryCode`: string): *[getDisplayPhoneNumber](_phonenumbers_.md#getdisplayphonenumber)* *Defined in [phoneNumbers.ts:49](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/phone-utils/src/phoneNumbers.ts#L49)* @@ -68,13 +68,13 @@ Name | Type | `phoneNumber` | string | `defaultCountryCode` | string | -**Returns:** *string* +**Returns:** *[getDisplayPhoneNumber](_phonenumbers_.md#getdisplayphonenumber)* ___ ### getE164DisplayNumber -▸ **getE164DisplayNumber**(`e164PhoneNumber`: string): *string* +▸ **getE164DisplayNumber**(`e164PhoneNumber`: string): *[getE164DisplayNumber](_phonenumbers_.md#gete164displaynumber)* *Defined in [phoneNumbers.ts:70](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/phone-utils/src/phoneNumbers.ts#L70)* @@ -84,13 +84,13 @@ Name | Type | ------ | ------ | `e164PhoneNumber` | string | -**Returns:** *string* +**Returns:** *[getE164DisplayNumber](_phonenumbers_.md#gete164displaynumber)* ___ ### getE164Number -▸ **getE164Number**(`phoneNumber`: string, `defaultCountryCode`: string): *null | string* +▸ **getE164Number**(`phoneNumber`: string, `defaultCountryCode`: string): *[getE164Number](_phonenumbers_.md#gete164number)* *Defined in [phoneNumbers.ts:75](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/phone-utils/src/phoneNumbers.ts#L75)* @@ -101,13 +101,13 @@ Name | Type | `phoneNumber` | string | `defaultCountryCode` | string | -**Returns:** *null | string* +**Returns:** *[getE164Number](_phonenumbers_.md#gete164number)* ___ ### getExampleNumber -▸ **getExampleNumber**(`regionCode`: string, `useOnlyZeroes`: boolean, `isInternational`: boolean): *undefined | string* +▸ **getExampleNumber**(`regionCode`: string, `useOnlyZeroes`: boolean, `isInternational`: boolean): *[getExampleNumber](_phonenumbers_.md#getexamplenumber)* *Defined in [phoneNumbers.ts:212](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/phone-utils/src/phoneNumbers.ts#L212)* @@ -119,13 +119,13 @@ Name | Type | Default | `useOnlyZeroes` | boolean | true | `isInternational` | boolean | false | -**Returns:** *undefined | string* +**Returns:** *[getExampleNumber](_phonenumbers_.md#getexamplenumber)* ___ ### getRegionCode -▸ **getRegionCode**(`e164PhoneNumber`: string): *undefined | null | string* +▸ **getRegionCode**(`e164PhoneNumber`: string): *[getRegionCode](_phonenumbers_.md#getregioncode)* *Defined in [phoneNumbers.ts:25](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/phone-utils/src/phoneNumbers.ts#L25)* @@ -135,13 +135,13 @@ Name | Type | ------ | ------ | `e164PhoneNumber` | string | -**Returns:** *undefined | null | string* +**Returns:** *[getRegionCode](_phonenumbers_.md#getregioncode)* ___ ### getRegionCodeFromCountryCode -▸ **getRegionCodeFromCountryCode**(`countryCode`: string): *null | string* +▸ **getRegionCodeFromCountryCode**(`countryCode`: string): *[getRegionCodeFromCountryCode](_phonenumbers_.md#getregioncodefromcountrycode)* *Defined in [phoneNumbers.ts:37](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/phone-utils/src/phoneNumbers.ts#L37)* @@ -151,13 +151,13 @@ Name | Type | ------ | ------ | `countryCode` | string | -**Returns:** *null | string* +**Returns:** *[getRegionCodeFromCountryCode](_phonenumbers_.md#getregioncodefromcountrycode)* ___ ### isE164NumberStrict -▸ **isE164NumberStrict**(`phoneNumber`: string): *boolean* +▸ **isE164NumberStrict**(`phoneNumber`: string): *[isE164NumberStrict](_phonenumbers_.md#ise164numberstrict)* *Defined in [phoneNumbers.ts:85](https://github.com/celo-org/celo-monorepo/blob/master/packages/sdk/phone-utils/src/phoneNumbers.ts#L85)* @@ -167,7 +167,7 @@ Name | Type | ------ | ------ | `phoneNumber` | string | -**Returns:** *boolean* +**Returns:** *[isE164NumberStrict](_phonenumbers_.md#ise164numberstrict)* ___ diff --git a/packages/env-tests/CHANGELOG.md b/packages/env-tests/CHANGELOG.md new file mode 100644 index 00000000000..8342b467ed9 --- /dev/null +++ b/packages/env-tests/CHANGELOG.md @@ -0,0 +1,35 @@ +# @celo/env-tests + +## 1.0.1 + +### Patch Changes + +- Updated dependencies [d48c68afc] +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [d48c68afc] + - @celo/contractkit@5.1.0 + - @celo/connect@5.1.0 + - @celo/cryptographic-utils@5.0.5 + - @celo/phone-utils@5.0.5 + - @celo/utils@5.0.5 + - @celo/base@5.0.5 + +## 1.0.1-beta.0 + +### Patch Changes + +- Updated dependencies [d48c68afc] +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [d48c68afc] + - @celo/contractkit@5.1.0-beta.0 + - @celo/connect@5.1.0-beta.0 + - @celo/cryptographic-utils@5.0.5-beta.0 + - @celo/phone-utils@5.0.5-beta.0 + - @celo/utils@5.0.5-beta.0 + - @celo/base@5.0.5-beta.0 diff --git a/packages/env-tests/jest.config.js b/packages/env-tests/jest.config.js index f8605f9a6c1..54dfe8710b4 100644 --- a/packages/env-tests/jest.config.js +++ b/packages/env-tests/jest.config.js @@ -1,8 +1,5 @@ -const { nodeFlakeTracking } = require('@celo/flake-tracker/src/jest/config.js') - module.exports = { preset: 'ts-jest', - ...nodeFlakeTracking, testMatch: ['/src/**/?(*.)+(spec|test).ts?(x)', '/src/monorepoRun.ts'], verbose: true, } diff --git a/packages/env-tests/package.json b/packages/env-tests/package.json index 5ab87bb9bff..c499ef301ef 100644 --- a/packages/env-tests/package.json +++ b/packages/env-tests/package.json @@ -1,17 +1,18 @@ { "name": "@celo/env-tests", - "version": "1.0.0", + "private": true, + "version": "1.0.1", "description": "Environment tests", "main": "index.js", "license": "MIT", "dependencies": { - "@celo/contractkit": "4.1.1-dev", - "@celo/utils": "4.1.1-dev", - "@celo/base": "4.1.1-dev", - "@celo/connect": "4.1.1-dev", - "@celo/identity": "4.1.1-dev", - "@celo/phone-utils": "4.1.1-dev", - "@celo/cryptographic-utils": "4.1.1-dev", + "@celo/contractkit": "^5.1.0", + "@celo/utils": "^5.0.5", + "@celo/base": "^5.0.5", + "@celo/connect": "^5.1.0", + "@celo/phone-utils": "^5.0.5", + "@celo/cryptographic-utils": "^5.0.5", + "bignumber.js": "^9.0.0", "bunyan": "1.8.12", "bunyan-gke-stackdriver": "0.1.2", "bunyan-debug-stream": "2.0.0", @@ -31,6 +32,7 @@ "staging-test": "CELO_ENV=staging CELO_PROVIDER=https://staging-forno.celo-networks-dev.org jest --runInBand" }, "devDependencies": { + "@jest/globals": "^29.5.0", "typescript": "4.4.3" } } \ No newline at end of file diff --git a/packages/flake-tracker/README.md b/packages/flake-tracker/README.md deleted file mode 100644 index f8e0d237239..00000000000 --- a/packages/flake-tracker/README.md +++ /dev/null @@ -1,69 +0,0 @@ -# FlakeTracker - -## Overview - -Flake Tracker integrates with Mocha and Jest to retry failed tests a configurable number of times. When tests pass on a retry, they are identified as flakey. Using the GitHub API, we automatically create new issues when flakey tests are found on `master` (i.e. only once they're merged). -Optionally, FlakeTracker can skip known flakey tests by fetching the list of flakey test issues from GitHub before each test suite. FlakeTracker authenticates with the GitHub API as a GitHub App, which allows it to provide rich feedback on PRs via GitHub Checks. - -## Configuration - -You can configure FlakeTracker via the following environment variables. - -- `FLAKEY` - - When running in CI, set `FLAKEY=false` to disable FlakeTracker, which will be enabled by default. - - When running locally, set `FLAKEY=true` to enable FlakeTracker, which will be disabled by default. -- `LOG_ALL_RETRY_ERRORS` - - Only relevant when flake tracker is enabled. - - Enables error logging after retries even for tests that never pass. Defaults to `false`. -- `NUM_RETRIES` - - Only relevant when flake tracker is enabled. - - Specifies how many retries should be performed before a test is failed. - - If you want a "fail fast without flakes" option (let's say you know a bunch of tests will fail, and you want to skip all the flakey tests without retrying each failed test a bunch of times) you can accomplish this by setting `NUM_RETRIES=0`. -- `SKIP_KNOWN_FLAKES` - - Only relevant when flake tracker is enabled and tests are run in CI. - - Set `SKIP_KNOWN_FLAKES=false` to disable the skipping of known flakes in CI. Defaults to `true`. -- `FLAKES_FAIL_CHECK_SUITE` - - If true, new flakey tests will be reported as 'failures' on GitHub Checks. Note this does not affect the CI workflow. - -## Disabling Skipping For Specific Tests - -To ensure that a specific known flakey test is run for your PR, simply include the link to the flakey test's issue anywhere in the body of your PR. - -## Still Seeing Flakey Tests? - -Sometimes a flakey test will fail all retries. You can try bumping up the number of retries that are attempted by setting the `NUM_RETRIES` env variable. - -Some flakey tests are not uncovered by retries. That is, if they fail the first time then every retry will also fail. If you encounter tests like this, please create an issue to track it. If you do so correctly, the flakey test will be disabled until the issue is closed. -To manually create a flakey test issue, mimic the format of issues created by the FlakeTracker bot. Specifically, make sure to add the `FLAKEY` label as well as labels for the package name and the ci job the test is run in. Also, include the `testID` of the test in the issue title. -The `testID` can be derived as follows: `jobName -> packageName -> rootDescribeBlockTitle -> ... -> testTitle`. The `testID` is printed each time the test is retried and can be found easily in the logs. Please also include the test error in the issue body. - -It is important to note that some flakiness might exist in setup/teardown steps like `before` and `after` hooks. FlakeTracker does not currently address these cases, but you should still create issues to track them! Make sure to include the string `FLAKEY SETUP` in the title of the issue (see example at ) so that FlakeTracker doesn't try to associate the issue with a test. - -## Tricks For Fixing Flakey Tests - -- You can configure FlakeTracker to print raw errors for all test retries (even those that don't eventually pass) by setting `LOG_ALL_RETRY_ERRORS=true`. -- You can test for flakiness locally by setting `FLAKEY=true`. -- You can save all FlakeTracker results to text files. See comments in `./db.js` - -## Obsolete Flakey Test Issues - -- If tests are removed, renamed or refactored their `testID` will change and any flakey test issues corresponding to them will be marked as obsolete. This happens when FlakeTracker no longer encounters the `testID` during the course of test execution. When obsolete issues are found on a PR that has not yet been merged, a GitHub Check will alert the PR's author and reviewers that the issue should be renamed with the updated `testID` if possible. Obsolete issues that are found on `master` (i.e. after the PR is merged) will be automatically closed. Note, changes to obsolete flakey test issues should occur only shortly before the PR is merged to avoid interfering with other workflows. - -## Slack Notifications - -To receive Slack notifications when new flakey tests are discovered, first [add GitHub to Slack](https://slack.github.com/). Then, send the following Slack command to the GitHub bot to subscribe to issues with the `FLAKEY` label. - -``` -/github subscribe celo-org/celo-monorepo issues +label:FLAKEY -``` - -To subscribe to flakey test issues for a specific job or package, just add the job or package name as another label filter - -``` -/github subscribe celo-org/celo-monorepo issues +label:FLAKEY +label:general-test +label:utils -``` - -## A Warning For Reviewers - -When FlakeTracker is enabled, reviewers should exercise caution on PRs that have skipped flakey tests. Note that tests can now be disabled by just creating a github issue, so we should inspect every test that is skipped and ensure it is not relevant to the PR. If you find that a relevant test was skipped, -just include the link to the corresponding flakey test issue in the PR body and run the build again. This will force the flakey test to run. If you wish to disable skipping flakey tests entirely for a given job you can do so by setting `SKIP_KNOWN_FLAKES=false`. diff --git a/packages/flake-tracker/package.json b/packages/flake-tracker/package.json deleted file mode 100644 index 0180c7a8bdb..00000000000 --- a/packages/flake-tracker/package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "@celo/flake-tracker", - "version": "0.0.1-dev", - "description": "A tool for tracking and disabling flakey tests.", - "private": true, - "author": "Celo", - "license": "Apache-2.0", - "homepage": "https://github.com/celo-org/celo-monorepo/tree/master/packages/flake-tracker", - "repository": "https://github.com/celo-org/celo-monorepo/tree/master/packages/flake-tracker", - "keywords": [ - "celo" - ], - "scripts": { - "build": "echo flake-tracker does not require build" - }, - "devDependencies": { - "@octokit/app": "^4.2.0", - "@octokit/plugin-retry": "^3.0.3", - "@octokit/rest": "^18.0.0", - "clone": "^2.1.2", - "detox": "^17.13.2", - "mocha": "^7.1.1", - "strip-ansi": "^6.0.0", - "tmp": "^0.2.1" - } -} \ No newline at end of file diff --git a/packages/flake-tracker/scripts/summary.js b/packages/flake-tracker/scripts/summary.js deleted file mode 100644 index 561fcc672ce..00000000000 --- a/packages/flake-tracker/scripts/summary.js +++ /dev/null @@ -1,8 +0,0 @@ -const GitHub = require('../src/github.js') - -const main = async () => { - const github = await GitHub.build() - await github.addSummaryCheck() -} - -main() diff --git a/packages/flake-tracker/src/db.js b/packages/flake-tracker/src/db.js deleted file mode 100644 index a2356da74b8..00000000000 --- a/packages/flake-tracker/src/db.js +++ /dev/null @@ -1,135 +0,0 @@ -const { fmtFlakeIssue, getTestSuiteDir } = require('./utils') -const { tmpdir } = require('os') -const { join } = require('path') -const fs = require('fs') -const tmp = require('tmp') -tmp.setGracefulCleanup() - -const flakeDir = 'flake-tracker_' + getTestSuiteDir().replace('/', '_') -const errDir = 'new-flakes' -const knownFlakeFile = 'known-flakes.txt' -const skippedFlakeFile = 'skipped-flakes.txt' - -const delim = '\n===============\n' - -const init = () => { - mkTmpDir(flakeDir) - mkDir(join(tmpdir(), flakeDir, errDir)) -} - -const writeErrors = (testID, errs) => { - writeError(testID, errs.join(delim)) -} - -const writeError = (testID, err) => { - const path = join(tmpdir(), flakeDir, errDir, fmtTestKey(testID)) - if (!fs.existsSync(path)) { - fs.writeFileSync(path, testID) - } - fs.appendFileSync(path, delim + err) -} - -const readErrors = (testID) => { - return readFileInFlakeDir(join(errDir, fmtTestKey(testID))) -} - -const readSkippedFlakes = () => { - return readFileInFlakeDir(skippedFlakeFile) -} - -const writeSkippedFlakes = (testIDs) => { - if (testIDs.length) { - writeSkippedFlake(testIDs.join(delim)) - } -} - -const writeSkippedFlake = (testID) => { - const path = join(tmpdir(), flakeDir, skippedFlakeFile) - if (!fs.existsSync(path)) { - fs.appendFileSync(path, testID) - } else { - fs.appendFileSync(path, delim + testID) - } -} - -const writeKnownFlakes = (flakes) => { - if (flakes.length) { - fs.writeFileSync( - join(tmpdir(), flakeDir, knownFlakeFile), - flakes.map((i) => JSON.stringify(i)).join(delim) - ) - } -} - -const readKnownFlakes = () => { - return readFileInFlakeDir(knownFlakeFile).map(JSON.parse) -} - -const readNewFlakes = () => { - return fs.readdirSync(join(tmpdir(), flakeDir, errDir)).map(parseFlakeFile) -} - -/* Helpers */ - -const parseFlakeFile = (fileName) => { - const errors = readFileInFlakeDir(join(errDir, fileName)) - const testID = errors.shift() - return fmtFlakeIssue(testID, errors) -} - -const mkDir = (path) => { - if (!fs.existsSync(path)) { - fs.mkdirSync(path) - } -} - -// Creates a directory that will be automatically removed on process exit. -const mkTmpDir = (name) => { - if (!fs.existsSync(join(tmpdir(), name))) { - // Add option `keep: true` to keep flakey test files after process exits. - // You can also specify a custom location for the files. This can be helpful for debugging. - // See https://www.npmjs.com/package/tmp - tmp.dirSync({ - name: name, - unsafeCleanup: true, - }) - } -} - -const readFileInFlakeDir = (file) => { - const path = join(tmpdir(), flakeDir, file) - return fs.existsSync(path) ? fs.readFileSync(path).toString().split(delim) : [] -} - -const fmtTestKey = (testID) => { - // Create unique file name by hashing test path and appending the test name. - // Hashing is necessary because the full test path is too long. - const titlePath = testID.split(' -> ') - const testTitle = titlePath[titlePath.length - 1] - const testKey = hashCode(testID).concat('_', testTitle).trim().replace(/(\W)/g, '_') - return testKey -} - -const hashCode = (str) => { - var hash = 0 - var chr - for (var i = 0; i < str.length; i++) { - chr = str.charCodeAt(i) - hash = (hash << 5) - hash + chr - hash |= 0 // Convert to 32bit integer - } - return hash.toString().trim() -} - -module.exports = { - readErrors: readErrors, - readKnownFlakes: readKnownFlakes, - readNewFlakes: readNewFlakes, - readSkippedFlakes: readSkippedFlakes, - init: init, - writeError: writeError, - writeErrors: writeErrors, - writeKnownFlakes: writeKnownFlakes, - writeSkippedFlake: writeSkippedFlake, - writeSkippedFlakes: writeSkippedFlakes, -} diff --git a/packages/flake-tracker/src/github.js b/packages/flake-tracker/src/github.js deleted file mode 100644 index 42edc81992e..00000000000 --- a/packages/flake-tracker/src/github.js +++ /dev/null @@ -1,362 +0,0 @@ -const { Octokit } = require('@octokit/rest') -const { App } = require('@octokit/app') -const { retry } = require('@octokit/plugin-retry') -const Client = Octokit.plugin(retry) -const stripAnsi = require('strip-ansi') -const config = require('./config') -const utils = require('./utils') - -const defaults = { - owner: config.org, - repo: config.repo, -} - -const FlakeLabel = 'FLAKEY' -const getLabels = () => { - const labels = [FlakeLabel, utils.getPackageName()] - if (config.isCI) { - labels.push(config.ciJob) - } - return labels -} - -class GitHub { - constructor(app, rest) { - if (app === undefined || rest === undefined) { - throw new Error('GitHub constructor should not be called directly. Please use GitHub.build()') - } - this.app = app - this.rest = rest - } - - static async build() { - const app = new App({ - id: config.flakeTrackerID, - privateKey: process.env.FLAKE_TRACKER_SECRET.replace(/\\n/gm, '\n'), - }) - const rest = await auth(app) - return new GitHub(app, rest) - } - - async renew() { - if (this === undefined) { - throw new Error('GitHub.renew() cannot be called before GitHub.build()') - } - this.rest = await auth(this.app) - } - - async report(flakes, skippedTests, obsoleteIssues) { - if (!config.isCI) return - const promises = [] - if (config.shouldCreateIssues) { - // Check list of ALL flakey issues to ensure no duplicates - // Note: we could technically still have duplicate issues if one is added by another build - // right after we fetch the list of issues. This seems unlikely so we'll leave it for now and - // revisit if duplicate issues start appearing. - const knownFlakes = (await this.fetchFlakeIssues()).map((i) => i.title) - const newFlakes = flakes.filter((flake) => !knownFlakes.includes(flake.title)) - if (newFlakes.length) { - promises.push(this.createIssues(newFlakes)) - } - } - if (config.shouldAddCheckToPR) { - promises.push(this.addFlakeCheck(flakes, skippedTests)) - if (config.shouldSkipKnownFlakes && obsoleteIssues.length) { - promises.push(this.handleObsoleteIssues(obsoleteIssues)) - } - } - console.log('\nSending flake tracker results to GitHub...\n') - // This is intentionally not atomic. That is, we don't mind if a flakey test is only partly reported. - return Promise.all(promises) - } - - async createIssues(flakes) { - if (!config.isCI) return - return Promise.all(flakes.map((f) => this.createIssue(f))) - } - - async createIssue(flake) { - if (!config.isCI) return - flake.body = 'Discovered at commit ' + config.sha + '\n\n' + flake.body + '\n' - const fn = () => - this.rest.issues.create({ - ...defaults, - title: flake.title, - body: stripAnsi(flake.body), - labels: getLabels(), - }) - const errMsg = 'Failed to create issue for flakey test. ' + 'Title: "' + flake.title + '",' - await this.safeExec(fn, errMsg) - } - - async fetchMandatoryTestsForPR() { - if (!config.isCI || config.branch === 'master') return [] - const prNumber = config.prNumber - const fn = () => - this.rest.pulls.get({ - ...defaults, - pull_number: prNumber, - }) - console.log('\nFetching mandatory tests for PR ' + prNumber + '...\n') - const errMsg = 'Failed to fetch mandatory tests for PR ' + prNumber - const prBody = (await this.safeExec(fn, errMsg)).data.body - return utils.parseMandatoryTestIssuesFromPullBody(prBody) - } - - async fetchFlakeIssues() { - const fn = () => - this.rest.paginate(this.rest.issues.listForRepo, { - ...defaults, - state: 'open', - labels: getLabels(), - }) - console.log('\nFetching known flakey tests from GitHub...') - const errMsg = 'Failed to fetch existing flakey test issues from GitHub.' - const issues = (await this.safeExec(fn, errMsg)) || [] - return issues.map(utils.parseDownFlakeIssue) - } - - async fetchKnownFlakesToSkip() { - const flakeIssues = await this.fetchFlakeIssues().catch(() => []) - const mandatoryTests = await this.fetchMandatoryTestsForPR().catch(() => []) - const knownFlakesToSkip = flakeIssues.filter( - // We filter out issues that have been referenced in the PR body or that correspond to - // setup/teardown steps rather than actual tests. - (i) => !i.title.includes('FLAKEY SETUP') && !mandatoryTests.includes(i.number.toString()) - ) - return knownFlakesToSkip - } - - async handleObsoleteIssues(obsoleteIssues) { - if (!config.isCI) return - const promises = [this.addObsoleteIssuesCheck(obsoleteIssues)] - if (config.branch === 'master') { - promises.push(this.closeIssues(obsoleteIssues)) - } - return Promise.all(promises) - } - - async closeIssues(issues) { - if (!config.isCI) return - return Promise.all(issues.map((i) => this.closeIssue(i))) - } - - async closeIssue(issue) { - if (!config.isCI) return - const fn = () => - this.rest.issues.update({ - ...defaults, - issue_number: issue.number, - state: 'closed', - body: 'FlakeTracker closed this issue after commit ' + config.sha + '\n\n' + issue.body, - }) - console.log('\nClosing obsolete issue ' + issue.number + '...') - const errMsg = 'Failed to close obsolete issue.' - await this.safeExec(fn, errMsg) - } - - async addObsoleteIssuesCheck(obsoleteIssues) { - if (!config.isCI) return - if (obsoleteIssues.length) { - await this.addCheckRun( - { - ...defaults, - name: utils.getTestSuiteTitles().join(' -> '), - head_sha: config.sha, - conclusion: 'neutral', - output: { - title: 'Obsolete Issues', - summary: 'Some flakey test issues no longer correspond to actual tests', - text: - (config.branch === 'master' - ? 'Because these flakey test issues are now obsolete on master, they have been automatically closed.' - : 'If tests have been refactored or renamed please update the following issues accordingly (but not too long before your PR is merged, as to avoid interfering with other concurrent workflows). If left unchanged, these issues will be automatically closed when this PR is merged.') + - '\n\n' + - obsoleteIssues.map((i) => i.html_url).join('\n\n'), - }, - }, - 'Failed to add obsolete issues check run.' - ) - } - } - - // addSummaryCheck is called in a final job added to the CI workflow. - // It provides a breakdown of where flakey tests are located. - async addSummaryCheck() { - if (!config.isCI) return - const title = 'Flakey Test Summary' - const optsBase = { ...defaults, name: 'Summary', head_sha: config.sha } - - // Get FlakeTracker check runs added so far - let fn = () => - this.rest.checks.listSuitesForRef({ - ...defaults, - ref: config.sha, - app_id: config.flakeTrackerID, - }) - - let errMsg = 'Failed to list check suites.' - const res = (await this.safeExec(fn, errMsg)).data - const numCheckSuites = res.total_count - - // If a check suite has not yet been created by the FlakeTracker app, then no - // flakiness has been detected in the earlier jobs. - let opts = { - ...optsBase, - conclusion: 'success', - output: { - title: title, - summary: utils.fmtSummary([], [], 0), - images: [utils.getRandomSuccessImage()], - }, - } - - if (numCheckSuites) { - // There should only be one check suite for the FlakeTracker app - const checkSuite = res.check_suites[0] - - fn = () => - this.rest.paginate(this.rest.checks.listForSuite, { - ...defaults, - check_suite_id: checkSuite.id, - }) - - errMsg = 'Failed to get check runs in suite.' - - const checkRuns = await this.safeExec(fn, errMsg) - - // If there exists a check run in the suite that is not just reporting obsolete issues, - // then add summary check run with breakdown of flakey tests across the workflow. - if (checkRuns.some((checkRun) => !checkRun.output.title.includes('Obsolete'))) { - // Get breakdowns by test suite (keys are `jobName -> packageName`) - const foundFlakes = {} - const skippedFlakes = {} - const totalFlakes = {} - checkRuns.forEach((checkRun) => { - const name = checkRun.name.slice(checkRun.name.search(/[a-zA-Z]/)) // remove emoji prefix if any - const skipped = utils.parseNumFlakes(checkRun.output.text, /[0-9]+\sflakey/) - const found = utils.parseNumFlakes(checkRun.output.text, /[0-9]+\snew\sflakey/) - if (skipped) skippedFlakes[name] = skipped - if (found) foundFlakes[name] = found - if (found || skipped) totalFlakes[name] = skipped + found - }) - - const text = utils.fmtWorkflowSummary(foundFlakes, skippedFlakes, totalFlakes) - - console.log(text) - - opts = { - ...optsBase, - conclusion: 'neutral', - output: { - title: title, - summary: utils.fmtSummary(Object.keys(foundFlakes), Object.keys(skippedFlakes), 0), - text: text, - }, - } - } - } - - await this.addCheckRun(opts, 'Failed to add summary check run.') - } - - async addFlakeCheck(flakes, skippedTests) { - if (!config.isCI) return - const conclusion = utils.getConclusion(flakes, skippedTests) - - // Only add checks when there's flakiness (otherwise check suite gets cluttered) - if (conclusion === 'success') return - - const summary_0 = utils.fmtSummary(flakes, skippedTests, 0) - const summary_3 = utils.fmtSummary(flakes, skippedTests, 3) - - const annotations = flakes.map((f) => { - const firstErr = utils.parseFirstErrFromFlakeBody(f.body) - const lineNumber = utils.parseErrLineNumberFromStack(firstErr) || 1 - const path = utils.parsePathFromStack(firstErr) - return { - title: f.title, - path: path.slice(path.indexOf('packages/')), - start_line: lineNumber, - end_line: lineNumber + 1, - annotation_level: 'warning', - message: stripAnsi(utils.parseFirstErrFromFlakeBody(f.body)), - } - }) - const output = { - title: utils.statuses[conclusion], - summary: stripAnsi(summary_0), - text: stripAnsi(summary_3), - annotations: annotations, - } - - let name = utils.getTestSuiteTitles().join(' -> ') - let conclusionToDisplay = conclusion - if (!config.newFlakesShouldFailCheckSuite && conclusion !== 'success') { - name = utils.emojis[conclusion] + ' ' + name - conclusionToDisplay = 'neutral' - } - - await this.addCheckRun( - { - ...defaults, - name: name, - head_sha: config.sha, - conclusion: conclusionToDisplay, - output: output, - }, - 'Failed to add check run.' - ) - } - - async addCheckRun(opts, errMsg) { - if (!config.isCI) return - const fn = () => { - return this.rest.checks.create(opts) - } - await this.safeExec(fn, errMsg) - } - - // Retries fn with fresh token if expired - async safeExec(fn, errMsg) { - for (let i = 0; i < 2; i++) { - try { - return await fn() - } catch (error) { - if (i > 0 || error !== 'HttpError: Bad credentials') { - console.error('\n' + errMsg + ' ' + error) - return - } - await this.renew() - } - } - } -} - -// Authenticate as an installation of the Flake Tracker GitHub App -const auth = async (app) => { - const rest = new Client({ - auth: app.getSignedJsonWebToken(), - }) - - try { - const installationId = ( - await rest.apps.getRepoInstallation({ - ...defaults, - }) - ).data.id - - const installationAccessToken = await app.getInstallationAccessToken({ - installationId, - }) - - return new Client({ - auth: installationAccessToken, - }) - } catch (error) { - console.error('Flake Tracker App failed to authenticate as an installation ' + error) - return rest // We're still authenticated by the JWT token, but it will expire sooner. - } -} - -module.exports = GitHub diff --git a/packages/flake-tracker/src/jest/config.js b/packages/flake-tracker/src/jest/config.js deleted file mode 100644 index 46e67bf823f..00000000000 --- a/packages/flake-tracker/src/jest/config.js +++ /dev/null @@ -1,61 +0,0 @@ -const { - numRetries, - shouldLogRetryErrorsOnFailure, - shouldSkipKnownFlakes, - shouldTrackFlakes, -} = require('../config') - -const base = { - // No flake tracking - setupFilesAfterEnv: [], - testRunner: 'jest-circus/runner', -} - -const flakeTracking = { - ...base, - globalSetup: require.resolve('./setup.global.js'), - globalTeardown: require.resolve('./teardown.global.js'), - setupFilesAfterEnv: [require.resolve('./setup.js')], -} - -const nodeFlakeTracking = shouldTrackFlakes - ? { - ...flakeTracking, - testEnvironment: require.resolve('./environments/node'), - } - : { - ...base, - testEnvironment: 'node', - } - -const jsdomFlakeTracking = shouldTrackFlakes //TODO(Alec): check that jsdom tests are covered - ? { - ...flakeTracking, - testEnvironment: require.resolve('./environments/jsdom'), - } - : { - ...base, - testEnvironment: 'jsdom', - } - -// Note that detox e2e tests require a config.json file, which means we currently can't use the object below. -// Presumably this will change in future versions of detox, so we've added this here anyway. -const detoxFlakeTracking = shouldTrackFlakes - ? { - ...flakeTracking, - testEnvironment: require.resolve('./environments/detox'), - } - : { - ...base, - testEnvironment: 'node', - } - -module.exports = { - detoxFlakeTracking: detoxFlakeTracking, - jsdomFlakeTracking: jsdomFlakeTracking, - nodeFlakeTracking: nodeFlakeTracking, - numRetries: numRetries, - shouldLogRetryErrorsOnFailure: shouldLogRetryErrorsOnFailure, - shouldSkipKnownFlakes: shouldSkipKnownFlakes, - shouldTrackFlakes: shouldTrackFlakes, -} diff --git a/packages/flake-tracker/src/jest/environments/detox.js b/packages/flake-tracker/src/jest/environments/detox.js deleted file mode 100644 index a3f4ecfd06c..00000000000 --- a/packages/flake-tracker/src/jest/environments/detox.js +++ /dev/null @@ -1,35 +0,0 @@ -const { - DetoxCircusEnvironment, - SpecReporter, - WorkerAssignReporter, -} = require('detox/runners/jest-circus') -const JestFlakeTracker = require('../tracker') - -class FlakeTrackingDetoxEnv extends DetoxCircusEnvironment { - constructor(config) { - super(config) - - this.tracker = new JestFlakeTracker(this.global) - - // Can be safely removed, if you are content with the default value (=300000ms) - this.initTimeout = 300000 - - // This takes care of generating status logs on a per-spec basis. By default, Jest only reports at file-level. - this.registerListeners({ - SpecReporter, - WorkerAssignReporter, - }) - } - - async setup() { - await super.setup() - await this.tracker.setup() - } - - async handleTestEvent(event, state) { - await super.handleTestEvent(event, state) - await this.tracker.handleTestEvent(event, state) - } -} - -module.exports = FlakeTrackingDetoxEnv diff --git a/packages/flake-tracker/src/jest/environments/jsdom.js b/packages/flake-tracker/src/jest/environments/jsdom.js deleted file mode 100644 index 108f3b4e43b..00000000000 --- a/packages/flake-tracker/src/jest/environments/jsdom.js +++ /dev/null @@ -1,20 +0,0 @@ -const JSDOMEnvironment = require('jest-environment-jsdom') -const JestFlakeTracker = require('../tracker') - -class FlakeTrackingJSDOMEnv extends JSDOMEnvironment { - constructor(config) { - super(config) - this.tracker = new JestFlakeTracker(this.global) - } - - async setup() { - await super.setup() - await this.tracker.setup() - } - - async handleTestEvent(event, state) { - await this.tracker.handleTestEvent(event, state) - } -} - -module.exports = FlakeTrackingJSDOMEnv diff --git a/packages/flake-tracker/src/jest/environments/node.js b/packages/flake-tracker/src/jest/environments/node.js deleted file mode 100644 index a07c69da315..00000000000 --- a/packages/flake-tracker/src/jest/environments/node.js +++ /dev/null @@ -1,20 +0,0 @@ -const NodeEnvironment = require('jest-environment-node') -const JestFlakeTracker = require('../tracker') - -class FlakeTrackingNodeEnv extends NodeEnvironment { - constructor(config) { - super(config) - this.tracker = new JestFlakeTracker(this.global) - } - - async setup() { - await super.setup() - await this.tracker.setup() - } - - async handleTestEvent(event, state) { - await this.tracker.handleTestEvent(event, state) - } -} - -module.exports = FlakeTrackingNodeEnv diff --git a/packages/flake-tracker/src/jest/setup.global.js b/packages/flake-tracker/src/jest/setup.global.js deleted file mode 100644 index ff603ea81c6..00000000000 --- a/packages/flake-tracker/src/jest/setup.global.js +++ /dev/null @@ -1,8 +0,0 @@ -require('regenerator-runtime/runtime') // See https://stackoverflow.com/questions/42535270/regeneratorruntime-is-not-defined-when-running-jest-test - -const FlakeManager = require('../manager') - -// This is called at the beginning of each jest test suite -module.exports = async function jestFlakeTrackerSetup() { - global.FlakeManager = await FlakeManager.build() // Note that this global variable is not available anywhere else besides global.teardown -} diff --git a/packages/flake-tracker/src/jest/setup.js b/packages/flake-tracker/src/jest/setup.js deleted file mode 100644 index e5c3e701877..00000000000 --- a/packages/flake-tracker/src/jest/setup.js +++ /dev/null @@ -1,3 +0,0 @@ -const { numRetries } = require('./config') -jest.retryTimes(numRetries) -// This is run at the beginning of each top-level describe block diff --git a/packages/flake-tracker/src/jest/teardown.global.js b/packages/flake-tracker/src/jest/teardown.global.js deleted file mode 100644 index 4fd2d2bdfcc..00000000000 --- a/packages/flake-tracker/src/jest/teardown.global.js +++ /dev/null @@ -1,4 +0,0 @@ -// This is called at the end of each jest test suite. -module.exports = async function jestFlakeTrackerTeardown() { - await global.FlakeManager.finish() -} diff --git a/packages/flake-tracker/src/jest/tracker.js b/packages/flake-tracker/src/jest/tracker.js deleted file mode 100644 index 14d8de0d286..00000000000 --- a/packages/flake-tracker/src/jest/tracker.js +++ /dev/null @@ -1,77 +0,0 @@ -require('jest-circus') // We need this in order for makeRunResult (below) to work -const { makeRunResult } = require('jest-circus/build/utils') -const db = require('../db') -const { getTestID, getTestIDFromTestPath, buildFlakeyDescribe } = require('./utils') -const { shouldLogRetryErrorsOnFailure, shouldSkipKnownFlakes, numRetries } = require('./config') -const clone = require('clone') - -class JestFlakeTracker { - async setup() { - // For each describe block, we cache errors in this map as tests execute and - // only write them to the db on 'run_finish'. - this.flakes = new Map() - this.skip = shouldSkipKnownFlakes ? db.readKnownFlakes().map((i) => i.title) : [] - this.skipped = [] - } - - async handleTestEvent(event, state) { - // This event is fired before every test retry but after errors are cleared - if (event.name === 'test_retry') { - console.log('Retry #' + event.test.invocations + ' for test: ' + getTestID(event.test)) - } - - // This event is fired at the end of each top-level describe block. - if (event.name === 'run_finish') { - // We make a fake describe block with all the accumulated errors and then use it - // to call `makeRunResult`. This converts our `TestEntry` objects into `TestResults` - // which have formatted errors - const describeBlock = buildFlakeyDescribe(clone(state.rootDescribeBlock), this.flakes) - try { - makeRunResult(describeBlock, state.unhandledErrors) - .testResults.filter((tr) => tr.status === 'flakey') - .forEach((tr) => db.writeErrors(getTestIDFromTestPath(tr.testPath), tr.errors)) - } catch {} // ignoring weird behavior: "TypeError: Cannot read property 'replace' of undefined" that sometimes throws here - - // The alternative to using the db would be to send results to github at the - // end of each describe block. Instead, we use the db and only message github - // on global.setup and global.teardown. - db.writeSkippedFlakes(this.skipped) - } - - // This event is fired at the end of each test (including retries) before errors are cleared - if (event.name === 'test_done') { - const testID = getTestID(event.test) - const failed = event.test.errors.length > 0 - const isFinalRetry = event.test.invocations === numRetries + 1 - - if (failed) { - if (isFinalRetry) { - // Test failed on every retry => not flakey - this.flakes.delete(testID) - } else { - if (shouldLogRetryErrorsOnFailure) { - // Note that this error will not be formatted - console.log('\n' + event.test.errors + '\n') - } - // Test will be retried => store error - const prevErrors = this.flakes.get(testID) - const errors = prevErrors || [] - errors.push(...event.test.errors) - this.flakes.set(testID, errors) - } - } - } - - // This event is fired right before a test is executed - if (event.name === 'test_start' && shouldSkipKnownFlakes) { - const testID = getTestID(event.test) - if (this.skip.some((knownFlake) => knownFlake.includes(testID))) { - console.log('Skipped known flakey test: ' + testID) - this.skipped.push(testID) - event.test.mode = 'skip' // This tricks jest into skipping the test - } - } - } -} - -module.exports = JestFlakeTracker diff --git a/packages/flake-tracker/src/jest/utils.js b/packages/flake-tracker/src/jest/utils.js deleted file mode 100644 index 5579c8cf8f7..00000000000 --- a/packages/flake-tracker/src/jest/utils.js +++ /dev/null @@ -1,65 +0,0 @@ -const { fmtFlakeIssue, fmtTestTitles } = require('../utils') - -// We build a fake describe block object where all flakey tests -// maintain references to the errors from their retries. Normally, -// jest wipes errors from retries and only remembers errors from full -// failures. We can feed this fake describe block object to jest and -// trick it into formatting all the retry errors nicely for us. -const buildFlakeyDescribe = (describeBlock, flakeMap) => { - for (const child of describeBlock.children) { - switch (child.type) { - case 'describeBlock': { - buildFlakeyDescribe(child, flakeMap) - break - } - case 'test': { - const testID = getTestID(child) - const flakeErrors = flakeMap.get(testID) - if (flakeErrors !== undefined) { - child.errors = flakeErrors - child.status = 'flakey' - } - break - } - } - } - return describeBlock -} - -const getTestID = (test) => { - // Note that fmtTestTitles prepends `circleJobName -> packageName` to the test titles - return fmtTestTitles(getTestTitles(test)) -} - -// Returns an array of titles starting with the test title and ending with -// the title of the top-level describe blockcontaining the test. This effectively -// provides a 'path' to the test. Note that while jest implements this logic -// for TestResult objects (the output of makeRunResult), we have to do it ourselves -// for TestEntry objects. -const getTestTitles = (test) => { - const titles = [] - let parent = test - - do { - titles.push(parent.name) - } while ((parent = parent.parent)) - - titles.pop() // Removes ROOT_DESCRIBE_BLOCK - - return titles.reverse() -} - -// If we already have the the array of titles (i.e. we are using a TestResult object) -// then we do not need the logic from getTestTitles() above. -const getTestIDFromTestPath = (testPath) => { - testPath.shift() // Removes ROOT_DESCRIBE_BLOCK - return fmtTestTitles(testPath) -} - -module.exports = { - buildFlakeyDescribe: buildFlakeyDescribe, - getTestID: getTestID, - getTestIDFromTestPath: getTestIDFromTestPath, - fmtFlakeIssue: fmtFlakeIssue, - fmtTestTitles: fmtTestTitles, -} diff --git a/packages/flake-tracker/src/manager.js b/packages/flake-tracker/src/manager.js deleted file mode 100644 index 246bc0b903e..00000000000 --- a/packages/flake-tracker/src/manager.js +++ /dev/null @@ -1,67 +0,0 @@ -const { shouldUseGitHub, shouldSkipKnownFlakes } = require('./config') -const db = require('./db') -const GitHub = require('./github') -const { fmtSummary, calcObsoleteFlakeIssues } = require('./utils') - -class FlakeManager { - constructor(github, setup) { - this.setup = setup - if (!setup) return - - if (github === undefined && shouldUseGitHub) { - throw new Error( - 'FlakeManager constructor should not be called directly. Please use FlakeManager.build()' - ) - } - this.github = github - } - - // Called at the beginning of each test suite - static async build() { - try { - db.init() - let github - if (shouldUseGitHub) { - github = await GitHub.build() - if (shouldSkipKnownFlakes) { - db.writeKnownFlakes( - await github.fetchKnownFlakesToSkip().catch(() => { - console.log('failed to fetch list of known flakey tests') - return [] - }) - ) - } - } - return new FlakeManager(github, true) - } catch (error) { - console.log('Flake tracker setup failed') - console.log(error) - return new FlakeManager(undefined, false) - } - } - - // Called at the end of each test suite - async finish() { - if (!this.setup) return - try { - const flakes = db.readNewFlakes() - let skippedTests = [] - if (shouldUseGitHub) { - let obsoleteIssues = [] - if (shouldSkipKnownFlakes) { - skippedTests = db.readSkippedFlakes() - obsoleteIssues = calcObsoleteFlakeIssues(skippedTests, db.readKnownFlakes()) - } - await this.github.report(flakes, skippedTests, obsoleteIssues).catch(() => { - console.log('failed to report flake tracker results') - }) - } - console.log(fmtSummary(flakes, skippedTests, 3)) - } catch (error) { - console.log('Flake tracker teardown failed') - console.log(error) - } - } -} - -module.exports = FlakeManager diff --git a/packages/flake-tracker/src/mocha/config.js b/packages/flake-tracker/src/mocha/config.js deleted file mode 100644 index ef9eaa95501..00000000000 --- a/packages/flake-tracker/src/mocha/config.js +++ /dev/null @@ -1,8 +0,0 @@ -const { shouldTrackFlakes, numRetries } = require('../config') - -module.exports = shouldTrackFlakes - ? { - reporter: require.resolve('./reporter'), - retries: numRetries, - } - : {} diff --git a/packages/flake-tracker/src/mocha/reporter.js b/packages/flake-tracker/src/mocha/reporter.js deleted file mode 100644 index 10eeeef1175..00000000000 --- a/packages/flake-tracker/src/mocha/reporter.js +++ /dev/null @@ -1,61 +0,0 @@ -const Mocha = require('mocha') -const FlakeManager = require('../manager') -const db = require('../db') -const { getTestID, fmtError } = require('./utils') -const { shouldLogRetryErrorsOnFailure, shouldSkipKnownFlakes } = require('../config') -const { Spec } = Mocha.reporters - -function FlakeReporter(runner) { - Spec.call(this, runner) - - let manager - let skip = [] - let skipped = [] - let currErrors = [] - - before('Fetch Flakey Tests', async function () { - manager = await FlakeManager.build() - skip = shouldSkipKnownFlakes ? db.readKnownFlakes().map((i) => i.title) : [] - }) - - after('Process Flakey Tests', async function () { - db.writeSkippedFlakes(skipped) - await manager.finish() - }) - - runner.on('retry', function (test, err) { - console.log('Retry #' + (test.currentRetry() + 1) + ' for test ' + getTestID(test)) - if (shouldLogRetryErrorsOnFailure) { - console.log('\n' + fmtError(err) + '\n') - } - currErrors.push(fmtError(err)) - }) - - runner.on('test', function (test) { - if (skip.length) { - let testID = getTestID(test) - if (skip.some((knownFlake) => knownFlake.includes(testID))) { - console.log('Skipped known flakey test: ' + testID) - skipped.push(testID) - test.pending = true - } - } - }) - - runner.on('pass', function (test) { - if (test.currentRetry() > 0) { - // Note that we could store these errors locally in a map and not use the db at all. - // We use the db here for consistency with jest which must use the db, and also because - // writing to a tmp file (the db) is convenient for debugging. This design could be improved. - db.writeErrors(getTestID(test), currErrors) - } - }) - - runner.on('test end', function (test) { - currErrors = [] - }) -} - -Mocha.utils.inherits(FlakeReporter, Mocha.reporters.Spec) - -module.exports = FlakeReporter diff --git a/packages/flake-tracker/src/mocha/utils.js b/packages/flake-tracker/src/mocha/utils.js deleted file mode 100644 index 850a331e51d..00000000000 --- a/packages/flake-tracker/src/mocha/utils.js +++ /dev/null @@ -1,23 +0,0 @@ -const { fmtFlakeIssue, fmtTestTitles } = require('../utils') - -// Returns a 'path' to the test the same way that getTestID in ../jest does. -// i.e. rootDescribeBlockTitle -> childDescribeBlockTitle -> testTitle -const getTestID = (runnable) => { - // Note that fmtTestTitles also prepends `circleJobName -> packageName` to the titlePath - return fmtTestTitles(runnable.titlePath()) -} - -const getTestIDFromSuite = (suite, testTitle) => { - return getTestID(suite).concat(' -> ', testTitle) -} - -const fmtError = (err) => { - return err.stack || err || 'err undefined' -} - -module.exports = { - getTestID: getTestID, - getTestIDFromSuite: getTestIDFromSuite, - fmtError: fmtError, - fmtFlakeIssue: fmtFlakeIssue, -} diff --git a/packages/flake-tracker/src/utils.js b/packages/flake-tracker/src/utils.js deleted file mode 100644 index 38ec92e23e5..00000000000 --- a/packages/flake-tracker/src/utils.js +++ /dev/null @@ -1,309 +0,0 @@ -const { config } = require('process') - -// Emojis -const fire = String.fromCodePoint(0x1f525) -const partyFace = String.fromCodePoint(0x1f973) -const stressFace = String.fromCodePoint(0x1f613) -const greenCheck = String.fromCodePoint(0x2705) -const redX = String.fromCodePoint(0x274c) -const snowflake = String.fromCharCode(0x2744) -const warning = String.fromCodePoint(0x26a0) -const relievedSmileFace = String.fromCodePoint(0x1f60c) -const hands = String.fromCodePoint(0x1f64c) -const warningLight = String.fromCodePoint(0x1f6a8) - -const flakeTitlePrefix = '[FLAKEY TEST] ' - -const statuses = { - failure: 'flakey tests were found', - neutral: 'flakey tests were skipped', - success: 'no flakey tests found!', -} - -const emojis = { - failure: redX, - neutral: warningLight, - success: greenCheck, -} - -function fmtFlakeIssue(testID, errors) { - return { - title: flakeTitlePrefix + testID, - body: fmtIssueBody(errors), - } -} - -function fmtIssueBody(errors) { - errors.push('Test Passed!') - let body = '' - for (let i = 0; i < errors.length; i++) { - body += 'Attempt No. ' + (i + 1) + ':\n\n' + errors[i] + '\n\n' - } - return body -} - -// Parses list of flakey test issues to ignore from the PR's body. -// The tests corresponding to these issues will not be skipped. -function parseMandatoryTestIssuesFromPullBody(prBody) { - const urls = prBody.match(/https?[\S]+issues\/[0-9]+/g) || [] - const issueNumbers = urls.map((url) => - url.slice(url.lastIndexOf('/') + 1).replace(/[^0-9]+/g, '') - ) - return issueNumbers -} - -function getPackageName() { - const testSuiteDir = getTestSuiteDir() - const i = testSuiteDir.indexOf('/') - return i == -1 ? testSuiteDir : testSuiteDir.slice(0, i) -} - -function getTestSuiteDir() { - const rootDelim = 'packages/' - return process.cwd().slice(process.cwd().lastIndexOf(rootDelim) + rootDelim.length) -} - -function getTestSuiteTitles() { - const titles = [getTestSuiteDir()] - if (config.isCI) { - titles.unshift(config.ciJob) - } - return titles -} - -function fmtTestTitles(titles) { - titles.unshift(...getTestSuiteTitles()) - return titles.join(' -> ').trim() -} - -function parseFirstErrFromFlakeBody(body) { - return body.split(/Attempt No\. [0-9]+:/g)[1] -} - -function parseFirstLineOfStack(stack) { - return stack.split('at ')[1] -} - -function parsePathFromStack(stack) { - return parseFirstLineOfStack(stack).split(':')[0] -} - -function parseErrLineNumberFromStack(stack) { - return Number(parseFirstLineOfStack(stack).split(':')[1]) -} - -function parseTestIdFromFlakeTitle(title) { - return title.replace(flakeTitlePrefix, '').trim() -} - -function parseDownFlakeIssue(issue) { - return (({ title, html_url, number, body }) => ({ - title, - html_url, - number, - body, - }))(issue) -} - -function getConclusion(flakes, skippedTests) { - let conclusion = 'failure' - if (!flakes.length) { - conclusion = skippedTests.length ? 'neutral' : 'success' - } - return conclusion -} - -// You can set verbosity to either 0, 1, 2, 3, or 4 -// 4 => header + list of skipped tests + errors from every retry for each new flakey test -// 3 => header + list of skipped tests + 1 errror for each new flakey test -// 2 => list skipped tests + list new flakey tests -// 1 => count of skipped tests + count of new flakey tests -// 0 => short one line status -function fmtSummary(flakes, skippedTests, verbosity) { - if (![0, 1, 2, 3, 4].includes(verbosity)) { - verbosity = 0 // default is lowest verbosity - } - - if (verbosity == 0) { - if (flakes.length) { - return 'New flakiness detected ' + redX - } - if (skippedTests.length) { - return 'Some tests were skipped due to flakiness. No new flakey tests were found.' - } - return 'We have achieved zero flakiness ' + hands + ' ' + greenCheck - } - - let summary = verbosity > 2 ? '\n_____FlakeTracker_____\n' : '' - - if (skippedTests.length) { - summary += '\n' + warning + ' ' - if (skippedTests.length === 1) { - summary += '1 flakey test was skipped \n' - } else { - summary += skippedTests.length + ' flakey tests were skipped \n' - } - if (verbosity > 1) { - skippedTests.forEach((skip) => { - summary += '\n' + skip + '\n' - }) - } - } else { - summary += '\n' + fire + ' no flakey tests were skipped\n' - } - - if (flakes.length) { - summary += '\n' + stressFace + ' ' - if (flakes.length === 1) { - summary += '1 new flakey test found\n' - } else { - summary += flakes.length + ' new flakey tests found\n' - } - switch (verbosity) { - case 2: - flakes.forEach((f) => { - summary += '\n' + f.title + '\n' - }) - break - case 3: - flakes.forEach((f) => { - summary += '\n' + f.title + parseFirstErrFromFlakeBody(f.body) + '\n' - }) - break - case 4: - let i = 0 - flakes.forEach((f) => { - summary += '\n' + ++i + ')\n\n' + f.title + '\n\n' + f.body + '\n' - }) - break - default: - break - } - } else { - summary += '\n' + partyFace + ' no new flakey tests found!\n' - } - - return summary -} - -function sumVals(obj) { - return Object.values(obj).reduce((acc, x) => acc + x, 0) -} - -// Use i == 0 for breakdowns by job, i == 1 for breakdowns by package -function parseBreakdown(breakdownByTestSuite, i) { - const breakdown = {} - Object.keys(breakdownByTestSuite).forEach((testSuite) => { - const key = testSuite.split(' -> ')[i] // We id test suites by `jobName -> packageName` - breakdown[key] = breakdown[key] - ? breakdown[key] + breakdownByTestSuite[testSuite] - : breakdownByTestSuite[testSuite] - }) - return breakdown -} - -function fmtBreakdown(breakdownByTestSuite, total) { - let text = '' - const breakdownByPackage = parseBreakdown(breakdownByTestSuite, 1) - const breakdownByJob = parseBreakdown(breakdownByTestSuite, 0) - - const fmtPercentage = (n, d) => { - return ((n / d) * 100).toFixed(1) + '%' - } - - text += '\n\tBy Package:\n' - Object.keys(breakdownByPackage).forEach((pkg) => { - const num = breakdownByPackage[pkg] - text += '\n\t\t' + pkg + ': ' + num + ' (' + fmtPercentage(num, total) + ')\n' - }) - - text += '\n\tBy Job:\n' - Object.keys(breakdownByJob).forEach((job) => { - const num = breakdownByJob[job] - text += '\n\t\t' + job + ': ' + num + ' (' + fmtPercentage(num, total) + ')\n' - }) - - return text -} - -function parseNumFlakes(text, regex) { - return (text.match(regex) || []) - .map((str) => str.replace(/[^0-9]+/, '')) - .reduce((acc, x) => acc + Number(x), 0) -} - -// This is used only by the flakey-test-summary job added to the end of the workflow -function fmtWorkflowSummary(foundFlakes, skippedFlakes, totalFlakes) { - let summary = '\n_____FlakeTracker Workflow Summary_____\n' - - const total = sumVals(totalFlakes) - const found = sumVals(foundFlakes) - const skipped = sumVals(skippedFlakes) - - summary += - '\nTotal flakey tests in this workflow: ' + - total + - ' ' + - '(discovered: ' + - found + - ', skipped: ' + - skipped + - ')\n' - - if (total) { - summary += '\nBreakdown of all flakey tests:\n' + fmtBreakdown(totalFlakes, total) - if (found) { - summary += '\nBreakdown of new flakey tests:\n' + fmtBreakdown(foundFlakes, found) - } - if (skipped) { - summary += '\nBreakdown of skipped flakey tests:\n' + fmtBreakdown(skippedFlakes, skipped) - } - } - - return summary -} - -function getRandomSuccessImage() { - const fmtImage = (url) => { - return { - image_url: url, - alt: 'Hooray! ' + url, - } - } - const images = [ - // Please add more gifs :) - 'https://media.giphy.com/media/mQG644PY8O7rG/source.gif', - 'https://media.giphy.com/media/4xpB3eE00FfBm/source.gif', - 'https://media.giphy.com/media/kBZBlLVlfECvOQAVno/source.gif', - 'https://media.giphy.com/media/l4JySAWfMaY7w88sU/source.gif', - 'https://media.giphy.com/media/2fQ1Gq3KOpvNs4NTmu/source.gif', - ].map(fmtImage) - return images[Math.floor(Math.random() * images.length)] -} - -function calcObsoleteFlakeIssues(skippedTests, knownFlakes) { - return knownFlakes.filter((i) => !skippedTests.some((skipped) => i.title.includes(skipped))) -} - -module.exports = { - calcObsoleteFlakeIssues: calcObsoleteFlakeIssues, - emojis: emojis, - fmtFlakeIssue: fmtFlakeIssue, - fmtSummary: fmtSummary, - fmtTestTitles: fmtTestTitles, - fmtWorkflowSummary: fmtWorkflowSummary, - getConclusion: getConclusion, - getPackageName: getPackageName, - getRandomSuccessImage: getRandomSuccessImage, - getTestSuiteDir: getTestSuiteDir, - getTestSuiteTitles: getTestSuiteTitles, - parseDownFlakeIssue: parseDownFlakeIssue, - parseErrLineNumberFromStack: parseErrLineNumberFromStack, - parseFirstErrFromFlakeBody: parseFirstErrFromFlakeBody, - parseFirstLineOfStack: parseFirstLineOfStack, - parseMandatoryTestIssuesFromPullBody: parseMandatoryTestIssuesFromPullBody, - parseNumFlakes: parseNumFlakes, - parsePathFromStack: parsePathFromStack, - parseTestIdFromFlakeTitle: parseTestIdFromFlakeTitle, - statuses: statuses, -} diff --git a/packages/helm-charts/eksportisto/Chart.yaml b/packages/helm-charts/eksportisto/Chart.yaml deleted file mode 100644 index 29e1c562211..00000000000 --- a/packages/helm-charts/eksportisto/Chart.yaml +++ /dev/null @@ -1,11 +0,0 @@ -name: eksportisto -version: "3.0" -description: Chart which is used to run eksportisto -keywords: -- ethereum -- blockchain -- monitoring -dependencies: -- name: redis - version: 12.8.3 - repository: https://raw.githubusercontent.com/bitnami/charts/archive-full-index/bitnami diff --git a/packages/helm-charts/eksportisto/templates/configmap.yaml b/packages/helm-charts/eksportisto/templates/configmap.yaml deleted file mode 100644 index 068f7a68596..00000000000 --- a/packages/helm-charts/eksportisto/templates/configmap.yaml +++ /dev/null @@ -1,44 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: eksportisto-config -data: - service-account.json: {{ .Values.serviceAccountBase64 | b64dec | quote }} - config_monitor.yaml: | - monitoring: - port: 8080 - address: 0.0.0.0 - requestTimeoutSeconds: 25 - - config.yaml: | - monitoring: - port: 8080 - address: 0.0.0.0 - requestTimeoutSeconds: 25 - - redis: - address: {{ .Release.Name }}-redis-headless:6379 - password: "" - db: 0 - - publisher: - backfill: - enabled: true - startBlock: 0 - batchSize: 40000 - tipBuffer: 3 - sleepIntervalMilliseconds: 2000 - chainTip: - enabled: true - - indexer: - concurrency: 10 - mode: both - bigquery: - projectID: celo-testnet-production - dataset: {{ .Values.bigquery.dataset }} - table: data - destination: bigquery - dequeueTimeoutMilliseconds: 5000 - blockRetryAttempts: 1 - blockRetryDelayMilliseconds: 1000 diff --git a/packages/helm-charts/eksportisto/templates/statefulset-indexer.yaml b/packages/helm-charts/eksportisto/templates/statefulset-indexer.yaml deleted file mode 100644 index 025ec2714e3..00000000000 --- a/packages/helm-charts/eksportisto/templates/statefulset-indexer.yaml +++ /dev/null @@ -1,86 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: eksportisto-indexer - labels: - component: eksportisto - release: {{ .Release.Name }} -spec: - clusterIP: None - selector: - component: eksportisto - release: {{ .Release.Name }} ---- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: eksportisto-indexer - labels: - app: eksportisto - chart: eksportisto - release: {{ .Release.Name }} - heritage: {{ .Release.Service }} - component: eksportisto -spec: - serviceName: eksportisto-indexer - podManagementPolicy: Parallel - replicas: 8 - selector: - matchLabels: - app: eksportisto - release: {{ .Release.Name }} - component: eksportisto - template: - metadata: - labels: - app: eksportisto - release: {{ .Release.Name }} - component: eksportisto - annotations: - prometheus.io/path: /metrics - prometheus.io/port: "8080" - prometheus.io/scrape: "true" - configHash: "{{ .Values.configHash }}" - spec: - containers: - - name: eksportisto - image: {{ .Values.imageRepository }}:{{ .Values.imageTag }} - imagePullPolicy: {{ .Values.imagePullPolicy }} - args: - - "indexer" - - "--config=/var/config/config.yaml" - - "--celo-node-uri=http://rc1-fullnodes-rpc-internal-lb:8545" - livenessProbe: - exec: - # "eksportisto monitor" expects more parameters and will always - # fail when invoked like this. This is on purpose, as the - # livenessProbe is being used to restart the container every - # initialDelaySeconds seconds. The eksportisto binary is being - # used as it's the only binary present on the container image - # (regular user-space tools are not available). - command: - - /app/eksportisto - - monitor - initialDelaySeconds: 1800 - failureThreshold: 1 - env: - - name: GOOGLE_APPLICATION_CREDENTIALS - value: "/var/config/service-account.json" - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - resources: - requests: - memory: 100Mi - cpu: 100m - ports: - - name: prometheus - containerPort: 8080 - volumeMounts: - - name: config - mountPath: /var/config - volumes: - - name: config - configMap: - name: eksportisto-config diff --git a/packages/helm-charts/eksportisto/templates/statefulset-monitor.yaml b/packages/helm-charts/eksportisto/templates/statefulset-monitor.yaml deleted file mode 100644 index 1bb90b67b71..00000000000 --- a/packages/helm-charts/eksportisto/templates/statefulset-monitor.yaml +++ /dev/null @@ -1,69 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: eksportisto-monitor - labels: - component: eksportisto - release: {{ .Release.Name }} -spec: - clusterIP: None - selector: - component: eksportisto - release: {{ .Release.Name }} ---- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: eksportisto-monitor - labels: - app: eksportisto - chart: eksportisto - release: {{ .Release.Name }} - heritage: {{ .Release.Service }} - component: eksportisto -spec: - serviceName: eksportisto-monitor - podManagementPolicy: Parallel - replicas: 1 - selector: - matchLabels: - app: eksportisto - release: {{ .Release.Name }} - component: eksportisto - template: - metadata: - labels: - app: eksportisto - release: {{ .Release.Name }} - component: eksportisto - annotations: - prometheus.io/path: /metrics - prometheus.io/port: "8080" - prometheus.io/scrape: "true" - configHash: "{{ .Values.configHash }}" - spec: - containers: - - name: eksportisto - image: {{ .Values.imageRepository }}:{{ .Values.imageTag }} - imagePullPolicy: {{ .Values.imagePullPolicy }} - args: - - "monitor" - - "--config=/var/config/config_monitor.yaml" - - "--celo-node-uri=ws://rc1-fullnodes-rpc-internal-lb:8545" - env: - - name: GOOGLE_APPLICATION_CREDENTIALS - value: "/var/config/service-account.json" - resources: - requests: - memory: 300Mi - cpu: 200m - ports: - - name: prometheus - containerPort: 8080 - volumeMounts: - - name: config - mountPath: /var/config - volumes: - - name: config - configMap: - name: eksportisto-config diff --git a/packages/helm-charts/eksportisto/templates/statefulset-publisher.yaml b/packages/helm-charts/eksportisto/templates/statefulset-publisher.yaml deleted file mode 100644 index 0035c6f22ec..00000000000 --- a/packages/helm-charts/eksportisto/templates/statefulset-publisher.yaml +++ /dev/null @@ -1,69 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: eksportisto-publisher - labels: - component: eksportisto - release: {{ .Release.Name }} -spec: - clusterIP: None - selector: - component: eksportisto - release: {{ .Release.Name }} ---- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: eksportisto-publisher - labels: - app: eksportisto - chart: eksportisto - release: {{ .Release.Name }} - heritage: {{ .Release.Service }} - component: eksportisto -spec: - serviceName: eksportisto-publisher - podManagementPolicy: Parallel - replicas: 1 - selector: - matchLabels: - app: eksportisto - release: {{ .Release.Name }} - component: eksportisto - template: - metadata: - labels: - app: eksportisto - release: {{ .Release.Name }} - component: eksportisto - annotations: - prometheus.io/path: /metrics - prometheus.io/port: "8080" - prometheus.io/scrape: "true" - configHash: "{{ .Values.configHash }}" - spec: - containers: - - name: eksportisto - image: {{ .Values.imageRepository }}:{{ .Values.imageTag }} - imagePullPolicy: {{ .Values.imagePullPolicy }} - args: - - "publisher" - - "--config=/var/config/config.yaml" - - "--celo-node-uri=ws://rc1-fullnodes-rpc-internal-lb:8545" - env: - - name: GOOGLE_APPLICATION_CREDENTIALS - value: "/var/config/service-account.json" - resources: - requests: - memory: 300Mi - cpu: 200m - ports: - - name: prometheus - containerPort: 8080 - volumeMounts: - - name: config - mountPath: /var/config - volumes: - - name: config - configMap: - name: eksportisto-config diff --git a/packages/helm-charts/eksportisto/values.yaml b/packages/helm-charts/eksportisto/values.yaml deleted file mode 100644 index 82468832812..00000000000 --- a/packages/helm-charts/eksportisto/values.yaml +++ /dev/null @@ -1,10 +0,0 @@ -imagePullPolicy: IfNotPresent -configBase64: config.yaml in base64 -serviceAccountBase64: service account in base 64 - -configHash: "0" - -redis: - cluster: - enabled: false - usePassword: false diff --git a/packages/helm-charts/komenci-rbac/Chart.yaml b/packages/helm-charts/komenci-rbac/Chart.yaml deleted file mode 100644 index 5fa277e324a..00000000000 --- a/packages/helm-charts/komenci-rbac/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -appVersion: "1.0" -description: A Helm chart to get the RBAC token needed by the komenci to reach the K8s API server -name: komenci-rbac -version: 0.1.0 diff --git a/packages/helm-charts/komenci-rbac/templates/_helper.tpl b/packages/helm-charts/komenci-rbac/templates/_helper.tpl deleted file mode 100644 index 1527741e2c3..00000000000 --- a/packages/helm-charts/komenci-rbac/templates/_helper.tpl +++ /dev/null @@ -1,15 +0,0 @@ -{{- define "name" -}} -{{- .Values.environment.name -}}-komenci-rbac-{{- .index -}} -{{- end -}} - -{{- define "komenci-pod-name" -}} -{{- .Values.environment.name -}}-relayer-{{- .index -}} -{{- end -}} - -{{- define "rewards-name" -}} -{{- .Values.environment.name -}}-komenci-rewards-rbac-{{- .index -}} -{{- end -}} - -{{- define "komenci-rewards-pod-name" -}} -{{- .Values.environment.name -}}-rewards-relayer-{{- .index -}} -{{- end -}} \ No newline at end of file diff --git a/packages/helm-charts/komenci-rbac/templates/role.yaml b/packages/helm-charts/komenci-rbac/templates/role.yaml deleted file mode 100644 index df04f4e72b0..00000000000 --- a/packages/helm-charts/komenci-rbac/templates/role.yaml +++ /dev/null @@ -1,27 +0,0 @@ -{{ range $index, $e := until (.Values.relayer.replicas | int) }} -{{- $index_counter := (dict "Values" $.Values "index" $index) -}} -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: {{ template "name" $index_counter }} -rules: -- apiGroups: [""] - resources: ["pods"] - resourceNames: ["{{ template "komenci-pod-name" $index_counter }}"] - verbs: ["get", "patch"] ---- -{{ end }} - -{{ range $index, $e := until (.Values.rewards.relayer.replicas | int) }} -{{- $index_counter := (dict "Values" $.Values "index" $index) -}} -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: {{ template "rewards-name" $index_counter }} -rules: -- apiGroups: [""] - resources: ["pods"] - resourceNames: ["{{ template "komenci-rewards-pod-name" $index_counter }}"] - verbs: ["get", "patch"] ---- -{{ end }} diff --git a/packages/helm-charts/komenci-rbac/templates/rolebinding.yaml b/packages/helm-charts/komenci-rbac/templates/rolebinding.yaml deleted file mode 100644 index f9ebfb24826..00000000000 --- a/packages/helm-charts/komenci-rbac/templates/rolebinding.yaml +++ /dev/null @@ -1,31 +0,0 @@ -{{ range $index, $e := until (.Values.relayer.replicas | int) }} -{{- $index_counter := (dict "Values" $.Values "index" $index) -}} -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: {{ template "name" $index_counter }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: {{ template "name" $index_counter }} -subjects: -- kind: ServiceAccount - name: {{ template "name" $index_counter }} ---- -{{ end }} - -{{ range $index, $e := until (.Values.rewards.relayer.replicas | int) }} -{{- $index_counter := (dict "Values" $.Values "index" $index) -}} -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: {{ template "rewards-name" $index_counter }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: {{ template "rewards-name" $index_counter }} -subjects: -- kind: ServiceAccount - name: {{ template "rewards-name" $index_counter }} ---- -{{ end }} diff --git a/packages/helm-charts/komenci-rbac/templates/service-account.yaml b/packages/helm-charts/komenci-rbac/templates/service-account.yaml deleted file mode 100644 index da8ea5730db..00000000000 --- a/packages/helm-charts/komenci-rbac/templates/service-account.yaml +++ /dev/null @@ -1,17 +0,0 @@ -{{ range $index, $e := until (.Values.relayer.replicas | int) }} -{{- $index_counter := (dict "Values" $.Values "index" $index) -}} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ template "name" $index_counter}} ---- -{{ end }} - -{{ range $index, $e := until (.Values.rewards.relayer.replicas | int) }} -{{- $index_counter := (dict "Values" $.Values "index" $index) -}} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ template "rewards-name" $index_counter}} ---- -{{ end }} diff --git a/packages/helm-charts/komenci-rbac/values.yaml b/packages/helm-charts/komenci-rbac/values.yaml deleted file mode 100644 index 8b4bfd1fc52..00000000000 --- a/packages/helm-charts/komenci-rbac/values.yaml +++ /dev/null @@ -1,5 +0,0 @@ -environment: - name: default - -relayer: - replicas: 2 diff --git a/packages/helm-charts/komenci/Chart.yaml b/packages/helm-charts/komenci/Chart.yaml deleted file mode 100644 index d4b70b8f480..00000000000 --- a/packages/helm-charts/komenci/Chart.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: v1 -appVersion: "0.2.0" -description: A Helm chart for the Komenci app -name: komenci -version: 0.1.0 -dependencies: - - name: common - repository: oci://us-west1-docker.pkg.dev/devopsre/clabs-public-oci - version: 0.2.0 diff --git a/packages/helm-charts/komenci/templates/_helpers.tpl b/packages/helm-charts/komenci/templates/_helpers.tpl deleted file mode 100644 index df8bc9c4a72..00000000000 --- a/packages/helm-charts/komenci/templates/_helpers.tpl +++ /dev/null @@ -1,92 +0,0 @@ -{{/* -The name of the deployment -*/}} -{{- define "name" -}} -{{- .Values.environment.name -}}-relayer -{{- end -}} - -{{- define "rewards-relayer-name" -}} -{{- .Values.environment.name -}}-rewards-relayer -{{- end -}} - -{{- define "komenci-onboarding-fullname" -}} -{{- .Values.environment.name -}}-onboarding -{{- end -}} - -{{- define "komenci-rewards-fullname" -}} -{{- .Values.environment.name -}}-rewards -{{- end -}} - -{{/* -Common labels that are recommended to be used by Helm and Kubernetes -*/}} -{{- define "labels" -}} -app.kubernetes.io/name: {{ template "name" . }} -helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end -}} - -{{/* -Annotations to indicate to the prometheus server that this node should be scraped for metrics -*/}} -{{- define "metric-annotations" -}} -prometheus.io/scrape: "true" -prometheus.io/port: "{{ .Values.relayer.metrics.prometheusPort }}" -{{- end -}} - -{{/* -Label specific to the komenci relayer component -*/}} -{{- define "komenci-relayer-component-label" -}} -app.kubernetes.io/component: komenci-relayer -{{- end -}} - -{{/* -Label specific to the komenci onboarding component -*/}} -{{- define "komenci-onboarding-component-label" -}} -app.kubernetes.io/component: komenci-onboarding -{{- end -}} - -{{/* -Label specific to the komenci rewards component -*/}} -{{- define "komenci-rewards-component-label" -}} -app.kubernetes.io/component: komenci-rewards -{{- end -}} - -{{/* -Label specific to the komenci rewards relayer component -*/}} -{{- define "komenci-rewards-relayer-component-label" -}} -app.kubernetes.io/component: komenci-rewards-relayer -{{- end -}} - -{{/* -The name of the azure identity binding for all relayers -*/}} -{{- define "azure-identity-binding-name" -}} -{{- with .dot -}}{{ template "name" . }}{{- end -}}-{{ .index }}-identity-binding -{{- end -}} - -{{/* -The name of the azure identity binding for all rewards relayers -*/}} -{{- define "azure-rewards-identity-binding-name" -}} -{{- with .dot -}}{{ template "rewards-relayer-name" . }}{{- end -}}-{{ .index }}-identity-binding -{{- end -}} - -{{/* -The name of the azure identity for all relayers -*/}} -{{- define "azure-identity-name" -}} -{{- with .dot -}}{{ template "name" . }}{{- end -}}-{{ .index }}-identity -{{- end -}} - -{{/* -The name of the azure identity for all rewards relayers -*/}} -{{- define "azure-rewards-identity-name" -}} -{{- with .dot -}}{{ template "rewards-relayer-name" . }}{{- end -}}-{{ .index }}-identity -{{- end -}} \ No newline at end of file diff --git a/packages/helm-charts/komenci/templates/azure-identity-binding.yaml b/packages/helm-charts/komenci/templates/azure-identity-binding.yaml deleted file mode 100644 index 6160db69102..00000000000 --- a/packages/helm-charts/komenci/templates/azure-identity-binding.yaml +++ /dev/null @@ -1,25 +0,0 @@ -{{- range $index, $identity := .Values.relayer.identities -}} -{{ if (hasKey $identity "azure") }} -apiVersion: "aadpodidentity.k8s.io/v1" -kind: AzureIdentityBinding -metadata: - name: {{ template "azure-identity-binding-name" (dict "dot" $ "index" $index) }} -spec: - azureIdentity: {{ template "azure-identity-name" (dict "dot" $ "index" $index) }} - selector: {{ template "azure-identity-binding-name" (dict "dot" $ "index" $index) }} ---- -{{ end }} -{{ end }} - -{{- range $index, $identity := .Values.rewards.relayer.identities -}} -{{ if (hasKey $identity "azure") }} -apiVersion: "aadpodidentity.k8s.io/v1" -kind: AzureIdentityBinding -metadata: - name: {{ template "azure-rewards-identity-binding-name" (dict "dot" $ "index" $index) }} -spec: - azureIdentity: {{ template "azure-rewards-identity-name" (dict "dot" $ "index" $index) }} - selector: {{ template "azure-rewards-identity-binding-name" (dict "dot" $ "index" $index) }} ---- -{{ end }} -{{ end }} diff --git a/packages/helm-charts/komenci/templates/azure-identity.yaml b/packages/helm-charts/komenci/templates/azure-identity.yaml deleted file mode 100644 index b93930624c4..00000000000 --- a/packages/helm-charts/komenci/templates/azure-identity.yaml +++ /dev/null @@ -1,31 +0,0 @@ -{{- range $index, $identity := .Values.relayer.identities -}} -{{ if (hasKey $identity "azure") }} -apiVersion: aadpodidentity.k8s.io/v1 -kind: AzureIdentity -metadata: - name: {{ template "azure-identity-name" (dict "dot" $ "index" $index) }} - annotations: - aadpodidentity.k8s.io/Behavior: namespaced -spec: - type: 0 - resourceID: {{ $identity.azure.id }} - clientID: {{ $identity.azure.clientId }} ---- -{{ end }} -{{ end }} - -{{- range $index, $identity := .Values.rewards.relayer.identities -}} -{{ if (hasKey $identity "azure") }} -apiVersion: aadpodidentity.k8s.io/v1 -kind: AzureIdentity -metadata: - name: {{ template "azure-rewards-identity-name" (dict "dot" $ "index" $index) }} - annotations: - aadpodidentity.k8s.io/Behavior: namespaced -spec: - type: 0 - resourceID: {{ $identity.azure.id }} - clientID: {{ $identity.azure.clientId }} ---- -{{ end }} -{{ end }} diff --git a/packages/helm-charts/komenci/templates/logging-agent-config.yaml b/packages/helm-charts/komenci/templates/logging-agent-config.yaml deleted file mode 100644 index 2130bad90f1..00000000000 --- a/packages/helm-charts/komenci/templates/logging-agent-config.yaml +++ /dev/null @@ -1,124 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: logging-agent-config - namespace: default -data: - credentials-json: | -{{ .Values.loggingAgent.credentials | b64dec | indent 4 }} - app-conf: | - # Tails the container logs into standard JSON - - @type tail - path /var/log/containers/*.log - path_key file_path - pos_file /opt/containers.log.pos - tag komenci.app.{{ .Values.environment.network }} - limit_recently_modified 1d - - @type multi_format - - format json - time_key time - time_format %Y-%m-%dT%H:%M:%S.%NZ - - - format /^(? - - - - # Parses the kubernetes context from the container log name - - @type parser - format /var\/log\/containers\/(?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)_(?[^_]+)_(?.+)-(?[a-z0-9]{64})\.log$/ - reserve_data true - emit_invalid_record_to_error false - key_name file_path - - - - @type grep - - key namespace - pattern /{{ .Values.environment.name }}/ - - - - - @type parser - key_name log - reserve_time true - reserve_data true - - - @type multi_format - - format json - - - format none - - - - - # Transforms the container log into a user friendly format - - @type record_transformer - enable_ruby - - message ${record['message'] || record['msg'] || record['log']} - severity ${record['severity'] || if record['stream'] == 'stderr' then 'ERROR' else 'INFO' end} - "logging.googleapis.com/local_resource_id" ${"k8s_container.#{record['namespace']}.#{record['pod_name']}.#{record['container_name']}"} - - remove_keys stream,log,file_path,msg,namespace,pod_name,container_name - - - google-fluentd-conf: | - @include config.d/*.conf - - # Prometheus monitoring. - - @type prometheus - port 24231 - - - @type prometheus_monitor - - - # Do not collect fluentd's own logs to avoid infinite loops. - - - # Add a unique insertId to each log entry that doesn't already have it. - # This helps guarantee the order and prevent log duplication. - - @type add_insert_ids - - - # Configure all sources to output to Google Cloud Logging - - @type google_cloud - label_map { - "container_id": "container_id" - } - use_metadata_service false - buffer_type file - buffer_path /var/log/google-fluentd/buffers - buffer_chunk_limit 512KB - flush_interval 5s - disable_retry_limit false - retry_limit 3 - retry_wait 10 - max_retry_wait 300 - num_threads 8 - detect_json true - use_grpc true - k8s_cluster_name {{ .Values.environment.cluster.name }} - k8s_cluster_location {{ .Values.environment.cluster.location }} - - diff --git a/packages/helm-charts/komenci/templates/logging-agent.yaml b/packages/helm-charts/komenci/templates/logging-agent.yaml deleted file mode 100644 index 7513771194f..00000000000 --- a/packages/helm-charts/komenci/templates/logging-agent.yaml +++ /dev/null @@ -1,84 +0,0 @@ -apiVersion: apps/v1 -kind: DaemonSet -metadata: - name: fluentd-log-agent - namespace: default -spec: - selector: - matchLabels: - name: fluentd-log-agent - template: - metadata: - creationTimestamp: null - annotations: - checksum/config: {{ include (print $.Template.BasePath "/logging-agent-config.yaml") . | sha256sum }} - labels: - name: fluentd-log-agent - spec: - serviceAccountName: default - tolerations: - - key: node-role.kubernetes.io/master - effect: NoSchedule - containers: - - image: celotestnet.azurecr.io/fluentd/google-fluentd:latest - imagePullPolicy: Always - name: fluentd-log-agent - resources: - limits: - memory: "250Mi" - cpu: 250m - requests: - memory: "250Mi" - cpu: 100m - volumeMounts: - - mountPath: /var/log - name: varlog - - mountPath: /var/lib/docker/containers - name: varlibdockercontainers - readOnly: true - - mountPath: /opt/credentials.json - name: credentials-json - subPath: credentials-json-path - readOnly: true - - mountPath: /etc/google-fluentd/google-fluentd.conf - name: google-fluentd-conf - subPath: google-fluentd-conf-path - readOnly: true - - mountPath: /etc/google-fluentd/config.d/app.conf - name: app-conf - subPath: app-conf-path - readOnly: true - env: - - name: GOOGLE_APPLICATION_CREDENTIALS - value: /opt/credentials.json - restartPolicy: Always - terminationGracePeriodSeconds: 30 - volumes: - - hostPath: - path: /var/log - name: varlog - - hostPath: - path: /var/lib/docker/containers - name: varlibdockercontainers - - name: credentials-json - configMap: - name: logging-agent-config - items: - - key: credentials-json - path: credentials-json-path - - name: app-conf - configMap: - name: logging-agent-config - items: - - key: app-conf - path: app-conf-path - - name: google-fluentd-conf - configMap: - name: logging-agent-config - items: - - key: google-fluentd-conf - path: google-fluentd-conf-path - - - - diff --git a/packages/helm-charts/komenci/templates/onboarding-deployment.yaml b/packages/helm-charts/komenci/templates/onboarding-deployment.yaml deleted file mode 100644 index fd6bf98290e..00000000000 --- a/packages/helm-charts/komenci/templates/onboarding-deployment.yaml +++ /dev/null @@ -1,84 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "komenci-onboarding-fullname" . }} - labels: -{{- include "komenci-onboarding-component-label" . | nindent 4 }} -spec: - replicas: {{ .Values.onboarding.replicaCount }} - selector: - matchLabels: - {{- include "komenci-onboarding-component-label" . | nindent 6 }} - template: - metadata: - labels: -{{- include "komenci-onboarding-component-label" . | nindent 8 }} - spec: - containers: - - name: komenci-onboarding - securityContext: - {{- toYaml .Values.securityContext | nindent 12 }} - image: {{ .Values.image.repository }}:{{ .Values.image.tag }} - imagePullPolicy: Always - ports: - - name: http - containerPort: 3000 - command: - - bash - - "-c" - - | - node packages/apps/api/dist/main.js - resources: - {{- toYaml .Values.onboarding.resources | nindent 12 }} - env: - - name: REPLICA_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name -{{ include "common.env-var" (dict "name" "RULE_SIGNATURE_ENABLED" "dict" .Values.onboarding.ruleEnabled "value_name" "signature") | indent 10 }} -{{ include "common.env-var" (dict "name" "RULE_CAPTCHA_ENABLED" "dict" .Values.onboarding.ruleEnabled "value_name" "captcha") | indent 10 }} -{{ include "common.env-var" (dict "name" "RULE_CAPTCHA_CONFIG_BYPASS_ENABLED" "dict" .Values.onboarding.ruleConfig.captcha "value_name" "bypassEnabled") | indent 10 }} -{{ include "common.env-var" (dict "name" "RULE_CAPTCHA_CONFIG_BYPASS_TOKEN" "dict" .Values.onboarding.ruleConfig.captcha "value_name" "bypassToken") | indent 10 }} -{{ include "common.env-var" (dict "name" "RECAPTCHA_TOKEN" "dict" .Values.onboarding "value_name" "recaptchaToken") | indent 10 }} -{{ include "common.env-var" (dict "name" "QUOTA_DISTRIBUTED_BLINDED_PEPPER" "dict" .Values.onboarding.quota "value_name" "distributedBlindePepper") | indent 10 }} -{{ include "common.env-var" (dict "name" "QUOTA_REQUEST_SUBSIDISED_ATTESTATION" "dict" .Values.onboarding.quota "value_name" "requestSubsidisedAttestation") | indent 10 }} -{{ include "common.env-var" (dict "name" "QUOTA_SUBMIT_META_TRANSACTION" "dict" .Values.onboarding.quota "value_name" "submitMetaTransaction") | indent 10 }} -{{ include "common.env-var" (dict "name" "DB_HOST" "dict" .Values.onboarding.db "value_name" "host" "optional" true) | indent 10 }} -{{ include "common.env-var" (dict "name" "DB_PORT" "dict" .Values.onboarding.db "value_name" "port" "optional" true) | indent 10 }} -{{ include "common.env-var" (dict "name" "DB_USERNAME" "dict" .Values.onboarding.db "value_name" "username") | indent 10 }} -{{ include "common.env-var" (dict "name" "DB_PASSWORD" "dict" .Values.onboarding.db "value_name" "password") | indent 10 }} -{{ include "common.env-var" (dict "name" "DB_DATABASE" "dict" .Values.onboarding.db "value_name" "database") | indent 10 }} -{{ include "common.env-var" (dict "name" "DB_SYNCHRONIZE" "dict" .Values.onboarding.db "value_name" "synchronize") | indent 10 }} -{{ include "common.env-var" (dict "name" "DB_SSL" "dict" .Values.onboarding.db "value_name" "ssl") | indent 10 }} -{{ include "common.env-var" (dict "name" "NODE_ENV" "dict" .Values.onboarding "value_name" "node_env") | indent 10 }} -{{ include "common.env-var" (dict "name" "RELAYER_HOST" "dict" .Values.onboarding.relayer "value_name" "host") | indent 10 }} -{{ include "common.env-var" (dict "name" "RELAYER_PORT" "dict" .Values.onboarding.relayer "value_name" "port") | indent 10 }} -{{ include "common.env-var" (dict "name" "RELAYER_RPC_TIMEOUT_MS" "dict" .Values.onboarding.relayer "value_name" "rpcTimeoutMs") | indent 10 }} -{{ include "common.env-var" (dict "name" "THROTTLE_TTL" "dict" .Values.onboarding.throttle "value_name" "ttl") | indent 10 }} -{{ include "common.env-var" (dict "name" "THROTTLE_LIMIT" "dict" .Values.onboarding.throttle "value_name" "limit") | indent 10 }} -{{ include "common.env-var" (dict "name" "NETWORK" "dict" .Values.environment "value_name" "network") | indent 10 }} -{{ include "common.env-var" (dict "name" "PUBLIC_URL" "dict" .Values.onboarding "value_name" "publicUrl") | indent 10 }} - readinessProbe: - httpGet: - path: /v1/health - port: 3000 - initialDelaySeconds: 3 - periodSeconds: 3 - livenessProbe: - httpGet: - path: /v1/health - port: 3000 - initialDelaySeconds: 3 - periodSeconds: 10 - {{- with .Values.onboarding.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.onboarding.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.onboarding.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} diff --git a/packages/helm-charts/komenci/templates/onboarding-ingress.yaml b/packages/helm-charts/komenci/templates/onboarding-ingress.yaml deleted file mode 100644 index d1269a49d57..00000000000 --- a/packages/helm-charts/komenci/templates/onboarding-ingress.yaml +++ /dev/null @@ -1,30 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: komenci-onboarding-ingress - annotations: - kubernetes.io/tls-acme: "true" -spec: - ingressClassName: {{ default "nginx" .Values.ingressClassName }} - tls: - - hosts: - - {{ .Values.onboarding.publicHostname }} - secretName: {{ .Release.Namespace }}-web-tls - rules: - - host: {{ .Values.onboarding.publicHostname }} - http: - paths: - - path: /rewards - pathType: Prefix - backend: - service: - name: {{ .Release.Namespace }}-rewards - port: - number: 3000 - - path: / - pathType: Prefix - backend: - service: - name: {{ .Release.Namespace }}-onboarding - port: - number: 3000 diff --git a/packages/helm-charts/komenci/templates/onboarding-service.yaml b/packages/helm-charts/komenci/templates/onboarding-service.yaml deleted file mode 100644 index 0928dd40e53..00000000000 --- a/packages/helm-charts/komenci/templates/onboarding-service.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ include "komenci-onboarding-fullname" . }} - labels: -{{ include "labels" . | indent 4 }} -{{ include "komenci-onboarding-component-label" . | indent 4 }} -spec: - clusterIP: None - selector: -{{ include "komenci-onboarding-component-label" . | indent 4 }} - ports: - - name: http - port: 3000 \ No newline at end of file diff --git a/packages/helm-charts/komenci/templates/relayer-statefulset.yaml b/packages/helm-charts/komenci/templates/relayer-statefulset.yaml deleted file mode 100644 index a6e9fd4dea9..00000000000 --- a/packages/helm-charts/komenci/templates/relayer-statefulset.yaml +++ /dev/null @@ -1,135 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ template "name" . }} - labels: -{{ include "labels" . | indent 4 }} -{{ include "komenci-relayer-component-label" . | indent 4 }} -spec: - ports: - - name: http - port: 3000 - clusterIP: None - selector: -{{ include "komenci-relayer-component-label" . | indent 4 }} ---- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: {{ template "name" . }} - labels: -{{ include "labels" . | indent 4 }} -{{ include "komenci-relayer-component-label" . | indent 4 }} -spec: - podManagementPolicy: Parallel - updateStrategy: - type: RollingUpdate - replicas: {{ .Values.relayer.replicas }} - serviceName: relayer - selector: - matchLabels: -{{ include "labels" . | indent 6 }} -{{ include "komenci-relayer-component-label" . | indent 6 }} - template: - metadata: - labels: -{{ include "labels" . | indent 8 }} -{{ include "komenci-relayer-component-label" . | indent 8 }} - annotations: -{{ if .Values.relayer.metrics.enabled }} -{{ include "metric-annotations" . | indent 8 }} -{{ end }} - spec: - initContainers: - - name: set-label - image: {{ .Values.kubectl.image.repository }}:{{ .Values.kubectl.image.tag }} - command: - - /bin/bash - - -c - args: - - | - RID=${POD_NAME##*-} - TOKEN_ENV_VAR_NAME="TOKEN_$RID" - kubectl \ - --namespace "$POD_NAMESPACE" \ - --server="https://kubernetes.default.svc" \ - --token="${!TOKEN_ENV_VAR_NAME}" \ - --certificate-authority="/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" \ - label pod "$POD_NAME" \ - --overwrite \ - "aadpodidbinding=$POD_NAME-identity-binding" - env: - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - {{ range $index, $e := .Values.kube.serviceAccountSecretNames }} - - name: TOKEN_{{ $index }} - valueFrom: - secretKeyRef: - key: token - name: {{ $e }} - {{ end }} - containers: - - name: komenci-relayer - image: {{ .Values.image.repository }}:{{ .Values.image.tag }} - imagePullPolicy: Always - ports: - - name: prometheus - containerPort: {{ .Values.relayer.metrics.prometheusPort }} - - name: relayer - containerPort: 3000 - command: - - bash - - "-c" - - | - [[ $REPLICA_NAME =~ -([0-9]+)$ ]] || exit 1 - RID=${BASH_REMATCH[1]} - - # Set the private key path. If Azure HSM signing is specified, - # it will take precedence. - export PRIVATE_KEY_PATH="/private-keys/private-key-$RID" - - # Get the correct key vault name. If this relayer's identity is not - # using Azure HSM signing, the key vault name will be empty and ignored - AZURE_KEY_VAULT_NAMES={{- range $index, $identity := .Values.relayer.identities -}}{{- if (hasKey $identity "azure" ) -}}{{ $identity.azure.keyVaultName | default "" }}{{- end }},{{- end }} - export AZURE_KEY_NAME=`echo -n $AZURE_KEY_VAULT_NAMES | cut -d ',' -f $((RID + 1))` - export AZURE_VAULT_NAME=`echo -n $AZURE_KEY_VAULT_NAMES | cut -d ',' -f $((RID + 1))` - - # Get the correct relayer account address - ADDRESSES={{- range $index, $identity := .Values.relayer.identities -}}{{ $identity.address }},{{- end }} - export WALLET_ADDRESS=`echo -n $ADDRESSES | cut -d ',' -f $((RID + 1))` - - node packages/apps/relayer/dist/main.js - env: - - name: REPLICA_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name -{{ include "common.env-var" (dict "name" "AZURE_HSM_INIT_TRY_COUNT" "dict" .Values.relayer.azureHsm "value_name" "initTryCount") | indent 8 }} -{{ include "common.env-var" (dict "name" "AZURE_HSM_INIT_MAX_RETRY_BACKOFF_MS" "dict" .Values.relayer.azureHsm "value_name" "initMaxRetryBackoffMs") | indent 8 }} -{{ include "common.env-var" (dict "name" "METRICS" "dict" .Values.relayer.metrics "value_name" "enabled") | indent 8 }} -{{ include "common.env-var" (dict "name" "OVERRIDE_INDEX" "dict" .Values.relayer "value_name" "overrideIndex" "optional" true) | indent 8 }} -{{ include "common.env-var" (dict "name" "OVERRIDE_ORACLE_COUNT" "dict" .Values.relayer "value_name" "overrideOracleCount" "optional" true) | indent 8 }} -{{ include "common.env-var" (dict "name" "PRIVATE_KEY_PATH" "dict" .Values.relayer "value_name" "privateKeyPath" "optional" true) | indent 8 }} -{{ include "common.env-var" (dict "name" "PROMETHEUS_PORT" "dict" .Values.relayer.metrics "value_name" "prometheusPort") | indent 8 }} -{{ include "common.env-var" (dict "name" "NODE_ENV" "dict" .Values.relayer "value_name" "node_env") | indent 8 }} -{{ include "common.env-var" (dict "name" "RELAYER_PORT" "dict" .Values.relayer "value_name" "port") | indent 8 }} -{{ include "common.env-var" (dict "name" "NETWORK" "dict" .Values.environment "value_name" "network") | indent 8 }} -{{ include "common.env-var" (dict "name" "WALLET_TYPE" "dict" .Values.relayer "value_name" "walletType") | indent 8 }} -{{ include "common.env-var" (dict "name" "GAS_PRICE_UPDATE_INTERVAL_MS" "dict" .Values.relayer "value_name" "gasPriceUpdateIntervalMs") | indent 8 }} -{{ include "common.env-var" (dict "name" "GAS_PRICE_MULTIPLIER" "dict" .Values.relayer "value_name" "gasPriceMultiplier") | indent 8 }} - readinessProbe: - tcpSocket: - port: 3000 - initialDelaySeconds: 5 - periodSeconds: 10 - livenessProbe: - tcpSocket: - port: 3000 - initialDelaySeconds: 15 - periodSeconds: 20 diff --git a/packages/helm-charts/komenci/templates/rewards-deployment.yaml b/packages/helm-charts/komenci/templates/rewards-deployment.yaml deleted file mode 100644 index 61f77c8d3b8..00000000000 --- a/packages/helm-charts/komenci/templates/rewards-deployment.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "komenci-rewards-fullname" . }} - labels: -{{- include "komenci-rewards-component-label" . | nindent 4 }} -spec: - replicas: {{ .Values.rewards.replicaCount }} - selector: - matchLabels: - {{- include "komenci-rewards-component-label" . | nindent 6 }} - template: - metadata: - labels: -{{- include "komenci-rewards-component-label" . | nindent 8 }} - spec: - containers: - - name: komenci-rewards - securityContext: - {{- toYaml .Values.securityContext | nindent 12 }} - image: {{ .Values.image.repository }}:{{ .Values.image.tag }} - imagePullPolicy: Always - ports: - - name: http - containerPort: 3000 - command: - - bash - - "-c" - - | - node packages/apps/rewards/dist/main.js - env: - - name: REPLICA_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name -{{ include "common.env-var" (dict "name" "DB_HOST" "dict" .Values.rewards.db "value_name" "host" "optional" true) | indent 10 }} -{{ include "common.env-var" (dict "name" "DB_PORT" "dict" .Values.rewards.db "value_name" "port" "optional" true) | indent 10 }} -{{ include "common.env-var" (dict "name" "DB_USERNAME" "dict" .Values.rewards.db "value_name" "username") | indent 10 }} -{{ include "common.env-var" (dict "name" "DB_PASSWORD" "dict" .Values.rewards.db "value_name" "password") | indent 10 }} -{{ include "common.env-var" (dict "name" "DB_DATABASE" "dict" .Values.rewards.db "value_name" "database") | indent 10 }} -{{ include "common.env-var" (dict "name" "DB_SYNCHRONIZE" "dict" .Values.rewards.db "value_name" "synchronize") | indent 10 }} -{{ include "common.env-var" (dict "name" "DB_SSL" "dict" .Values.rewards.db "value_name" "ssl") | indent 10 }} -{{ include "common.env-var" (dict "name" "RELAYER_HOST" "dict" .Values.rewards.relayer "value_name" "host") | indent 10 }} -{{ include "common.env-var" (dict "name" "RELAYER_PORT" "dict" .Values.rewards.relayer "value_name" "port") | indent 10 }} -{{ include "common.env-var" (dict "name" "NETWORK" "dict" .Values.environment "value_name" "network") | indent 10 }} -{{ include "common.env-var" (dict "name" "SEGMENT_API_KEY" "dict" .Values.rewards "value_name" "segmentApiKey") | indent 10 }} -{{ include "common.env-var" (dict "name" "SHOULD_SEND_REWARDS" "dict" .Values.rewards "value_name" "shouldSendRewards") | indent 10 }} diff --git a/packages/helm-charts/komenci/templates/rewards-relayer-statefulset.yaml b/packages/helm-charts/komenci/templates/rewards-relayer-statefulset.yaml deleted file mode 100644 index 70fc0e4a129..00000000000 --- a/packages/helm-charts/komenci/templates/rewards-relayer-statefulset.yaml +++ /dev/null @@ -1,128 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ template "rewards-relayer-name" . }} - labels: -{{ include "labels" . | indent 4 }} -{{ include "komenci-rewards-relayer-component-label" . | indent 4 }} -spec: - ports: - - name: http - port: 3000 - clusterIP: None - selector: -{{ include "komenci-rewards-relayer-component-label" . | indent 4 }} ---- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: {{ template "rewards-relayer-name" . }} - labels: -{{ include "labels" . | indent 4 }} -{{ include "komenci-rewards-relayer-component-label" . | indent 4 }} -spec: - podManagementPolicy: Parallel - updateStrategy: - type: RollingUpdate - replicas: {{ .Values.rewards.relayer.replicas }} - serviceName: relayer - selector: - matchLabels: -{{ include "labels" . | indent 6 }} -{{ include "komenci-rewards-relayer-component-label" . | indent 6 }} - template: - metadata: - labels: -{{ include "labels" . | indent 8 }} -{{ include "komenci-rewards-relayer-component-label" . | indent 8 }} - annotations: -{{ if .Values.rewards.relayer.metrics.enabled }} -{{ include "metric-annotations" . | indent 8 }} -{{ end }} - spec: - initContainers: - - name: set-label - image: {{ .Values.kubectl.image.repository }}:{{ .Values.kubectl.image.tag }} - command: - - /bin/bash - - -c - args: - - | - RID=${POD_NAME##*-} - TOKEN_ENV_VAR_NAME="TOKEN_$RID" - kubectl \ - --namespace "$POD_NAMESPACE" \ - --server="https://kubernetes.default.svc" \ - --token="${!TOKEN_ENV_VAR_NAME}" \ - --certificate-authority="/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" \ - label pod "$POD_NAME" \ - --overwrite \ - "aadpodidbinding=$POD_NAME-identity-binding" - env: - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - {{ range $index, $e := .Values.kube.rewardsServiceAccountSecretNames }} - - name: TOKEN_{{ $index }} - valueFrom: - secretKeyRef: - key: token - name: {{ $e }} - {{ end }} - containers: - - name: komenci-rewards-relayer - image: {{ .Values.image.repository }}:{{ .Values.image.tag }} - imagePullPolicy: Always - ports: - - name: prometheus - containerPort: {{ .Values.rewards.relayer.metrics.prometheusPort }} - - name: relayer - containerPort: 3000 - command: - - bash - - "-c" - - | - [[ $REPLICA_NAME =~ -([0-9]+)$ ]] || exit 1 - RID=${BASH_REMATCH[1]} - - # Get the correct key vault name. If this relayer's identity is not - # using Azure HSM signing, the key vault name will be empty and ignored - AZURE_KEY_VAULT_NAMES={{- range $index, $identity := .Values.rewards.relayer.identities -}}{{- if (hasKey $identity "azure" ) -}}{{ $identity.azure.keyVaultName | default "" }}{{- end }},{{- end }} - export AZURE_KEY_NAME=`echo -n $AZURE_KEY_VAULT_NAMES | cut -d ',' -f $((RID + 1))` - export AZURE_VAULT_NAME=`echo -n $AZURE_KEY_VAULT_NAMES | cut -d ',' -f $((RID + 1))` - - # Get the correct relayer account address - ADDRESSES={{- range $index, $identity := .Values.rewards.relayer.identities -}}{{ $identity.address }},{{- end }} - export WALLET_ADDRESS=`echo -n $ADDRESSES | cut -d ',' -f $((RID + 1))` - - node packages/apps/relayer/dist/main.js - env: - - name: REPLICA_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name -{{ include "common.env-var" (dict "name" "AZURE_HSM_INIT_TRY_COUNT" "dict" .Values.komenci.azureHsm "value_name" "initTryCount") | indent 8 }} -{{ include "common.env-var" (dict "name" "AZURE_HSM_INIT_MAX_RETRY_BACKOFF_MS" "dict" .Values.komenci.azureHsm "value_name" "initMaxRetryBackoffMs") | indent 8 }} -{{ include "common.env-var" (dict "name" "METRICS" "dict" .Values.rewards.relayer.metrics "value_name" "enabled") | indent 8 }} -{{ include "common.env-var" (dict "name" "OVERRIDE_INDEX" "dict" .Values.rewards.relayer "value_name" "overrideIndex" "optional" true) | indent 8 }} -{{ include "common.env-var" (dict "name" "PROMETHEUS_PORT" "dict" .Values.rewards.relayer.metrics "value_name" "prometheusPort") | indent 8 }} -{{ include "common.env-var" (dict "name" "NODE_ENV" "dict" .Values.rewards.relayer "value_name" "node_env") | indent 8 }} -{{ include "common.env-var" (dict "name" "RELAYER_PORT" "dict" .Values.rewards.relayer "value_name" "port") | indent 8 }} -{{ include "common.env-var" (dict "name" "NETWORK" "dict" .Values.environment "value_name" "network") | indent 8 }} -{{ include "common.env-var" (dict "name" "WALLET_TYPE" "dict" .Values.rewards.relayer "value_name" "walletType") | indent 8 }} -{{ include "common.env-var" (dict "name" "GAS_PRICE_UPDATE_INTERVAL_MS" "dict" .Values.rewards.relayer "value_name" "gasPriceUpdateIntervalMs") | indent 8 }} - readinessProbe: - tcpSocket: - port: 3000 - initialDelaySeconds: 5 - periodSeconds: 10 - livenessProbe: - tcpSocket: - port: 3000 - initialDelaySeconds: 15 - periodSeconds: 20 \ No newline at end of file diff --git a/packages/helm-charts/komenci/templates/rewards-service.yaml b/packages/helm-charts/komenci/templates/rewards-service.yaml deleted file mode 100644 index 0653de5454a..00000000000 --- a/packages/helm-charts/komenci/templates/rewards-service.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ include "komenci-rewards-fullname" . }} - labels: -{{ include "labels" . | indent 4 }} -{{ include "komenci-rewards-component-label" . | indent 4 }} -spec: - clusterIP: None - selector: -{{ include "komenci-rewards-component-label" . | indent 4 }} - ports: - - name: http - port: 3000 diff --git a/packages/helm-charts/komenci/values.yaml b/packages/helm-charts/komenci/values.yaml deleted file mode 100644 index fc557f48fb7..00000000000 --- a/packages/helm-charts/komenci/values.yaml +++ /dev/null @@ -1,125 +0,0 @@ -# This file is intended to show the expected value structure with placeholder values. -# Many values are optional, and the defaults are left up to the client. -# These values are commented out in this file, but show the correct structure -# if they were to be specified. - -ingressClassName: nginx - -kube: - serviceAccountSecretNames: - - "secret1" - - "secret2" - -environment: - name: test - network: alfajores - cluster: - name: test-cluster - location: location - -image: - repository: celotestnet.azurecr.io/komenci/komenci - tag: 5f2ef23e6c51eda8e5288490eab8ec2cbd058b11 - -kubectl: - image: - repository: bitnami/kubectl - tag: 1.17.4 - -relayer: - node_env: production - image: - repository: celotestnet.azurecr.io/komenci/relayer - tag: dae43ddce108a73da07dce73875b980ff077c7d4 - replicas: 2 - port: 3000 - identities: - - address: 0x00454cac6dae53f8800f71395b9a174f07a784b1 - azure: - id: defaultId - clientId: defaultClientId - keyVaultName: staging-komenci-eus - - address: 0xc6f0f9bfb1aed83620ece3eac0add98a65a8574e - azure: - id: defaultId1 - clientId: defaultClientId1 - keyVaultName: staging-komenci-eus - azureHsm: - initTryCount: 5 - initMaxRetryBackoffMs: 30000 - metrics: - enabled: true - prometheusPort: 9090 - walletType: azure-hsm - gasPriceUpdateIntervalMs: "1200000" - gasPriceMultiplier: 5 - maxGasPrice: "30000000000" # 30 gwei - -onboarding: - node_env: production - image: - repository: celotestnet.azurecr.io/komenci/onboarding - tag: dae43ddce108a73da07dce73875b980ff077c7d4 - replicas: 2 - throttle: - ttl: 60 - limit: 25 - ruleEnabled: - signature: true - captcha: true - ruleConfig: - captcha: - bypassEnabled: false - bypassToken: "special-bypass-captcha-token" - recaptchaToken: 'from-secret' - quota: - distributedBlindePepper: 1 - requestSubsidisedAttestation: 10 - submitMetaTransaction: 20 - relayer: - host: alfajores-relayer - port: 3000 - rpcTimeoutMs: 12000 - db: - host: komenci-komenci-postgresql - port: 5432 - username: 'postgres' - database: 'postgres' - synchronize: true - ssl: true - -rewards: - segmentApiKey: 'writeApiKey' - shouldSendRewards: false - relayer: - node_env: production - image: - repository: celotestnet.azurecr.io/komenci/relayer - tag: dae43ddce108a73da07dce73875b980ff077c7d4 - replicas: 2 - port: 3000 - identities: - - address: 0xb04390478A57E3C2147599D5380434f25fa5234d - privateKey: 0x000 - azure: - id: defaultId - clientId: defaultClientId - keyVaultName: staging-komenci-rewards - azureHsm: - initTryCount: 5 - initMaxRetryBackoffMs: 30000 - metrics: - enabled: true - prometheusPort: 9090 - walletType: azure-hsm - gasPriceUpdateIntervalMs: "1200000" - db: - host: komenci-komenci-postgresql - port: 5432 - username: 'postgres' - database: 'postgres' - synchronize: true - ssl: true - -loggingAgent: - credentials: eydleGFtcGxlJzogJ2NyZWRlbnRpYWxzJ30K # base64 credentials.json of a gcloud service account \ No newline at end of file diff --git a/packages/helm-charts/kong/.helmignore b/packages/helm-charts/kong/.helmignore deleted file mode 100644 index f0c13194444..00000000000 --- a/packages/helm-charts/kong/.helmignore +++ /dev/null @@ -1,21 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj diff --git a/packages/helm-charts/kong/Chart.lock b/packages/helm-charts/kong/Chart.lock deleted file mode 100644 index ec9d6b4435e..00000000000 --- a/packages/helm-charts/kong/Chart.lock +++ /dev/null @@ -1,12 +0,0 @@ -dependencies: -- name: postgresql - repository: https://charts.bitnami.com/bitnami - version: 10.4.6 -- name: cassandra - repository: https://charts.bitnami.com/bitnami - version: 7.5.5 -- name: common - repository: https://charts.bitnami.com/bitnami - version: 1.5.2 -digest: sha256:821ac1123fff0767b608586096c4b200cefb37cd1b27541f901d70adffcefbc3 -generated: "2021-05-22T08:23:02.029976706Z" diff --git a/packages/helm-charts/kong/Chart.yaml b/packages/helm-charts/kong/Chart.yaml deleted file mode 100644 index 9e4a5cd483a..00000000000 --- a/packages/helm-charts/kong/Chart.yaml +++ /dev/null @@ -1,37 +0,0 @@ -annotations: - category: Infrastructure -apiVersion: v2 -appVersion: 2.4.1 -dependencies: - - condition: postgresql.enabled - name: postgresql - repository: https://charts.bitnami.com/bitnami - version: 10.x.x - - condition: cassandra.enabled - name: cassandra - repository: https://charts.bitnami.com/bitnami - version: 7.x.x - - name: common - repository: https://charts.bitnami.com/bitnami - version: 1.x.x -description: Kong is a scalable, open source API layer (aka API gateway or API middleware) that runs in front of any RESTful API. Extra functionalities beyond the core platform are extended through plugins. Kong is built on top of reliable technologies like NGINX and provides an easy-to-use RESTful API to operate and configure the system. -engine: gotpl -home: https://github.com/bitnami/charts/tree/master/bitnami/kong -icon: https://bitnami.com/assets/stacks/kong/img/kong-stack-220x234.png -keywords: - - kong - - ingress - - openresty - - controller - - http - - web - - www - - reverse proxy -maintainers: - - email: containers@bitnami.com - name: Bitnami -name: kong -sources: - - https://github.com/bitnami/bitnami-docker-kong - - https://konghq.com/ -version: 3.7.4 diff --git a/packages/helm-charts/kong/README.md b/packages/helm-charts/kong/README.md deleted file mode 100644 index 8995564c29b..00000000000 --- a/packages/helm-charts/kong/README.md +++ /dev/null @@ -1,502 +0,0 @@ -# Kong - -[Kong](https://konghq.com/kong/) is a scalable, open source API layer (aka API gateway or API middleware) that runs in front of any RESTful API. Extra functionalities beyond the core platform are extended through plugins. Kong is built on top of reliable technologies like NGINX and provides an easy-to-use RESTful API to operate and configure the system. - -## TL;DR - -```console - helm repo add bitnami https://charts.bitnami.com/bitnami - helm install my-release bitnami/kong -``` - -## Introduction - -This chart bootstraps a [kong](https://github.com/bitnami/bitnami-docker-kong) deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. It also includes the [kong-ingress-controller](https://github.com/bitnami/bitnami-docker-kong-ingress-controller) container for managing Ingress resources using Kong. - -Bitnami charts can be used with [Kubeapps](https://kubeapps.com/) for deployment and management of Helm Charts in clusters. - -## Prerequisites - -- Kubernetes 1.12+ -- Helm 3.1.0 -- PV provisioner support in the underlying infrastructure - -## Installing the Chart - -To install the chart with the release name `my-release`: - -```console - helm repo add bitnami https://charts.bitnami.com/bitnami - helm install my-release bitnami/kong -``` - -These commands deploy kong on the Kubernetes cluster in the default configuration. The [Parameters](#parameters) section lists the parameters that can be configured during installation. - -> **Tip**: List all releases using `helm list` - -## Uninstalling the Chart - -To uninstall/delete the `my-release` deployment: - -```console - helm delete my-release -``` - -## Parameters - -The following tables list the configurable parameters of the kong chart and their default values per section/component: - -### Global Parameters - -| Parameter | Description | Default | -|---------------------------|-------------------------------------------------|---------------------------------------------------------| -| `global.imageRegistry` | Global Docker image registry | `nil` | -| `global.imagePullSecrets` | Global Docker registry secret names as an array | `[]` (does not add image pull secrets to deployed pods) | -| `global.storageClass` | Global storage class for dynamic provisioning | `nil` | - -### Common Parameters - -| Parameter | Description | Default | -|---------------------|---------------------------------------------------------------------------------------------------|-----------------| -| `nameOverride` | String to partially override kong.fullname template with a string (will prepend the release name) | `nil` | -| `fullnameOverride` | String to fully override kong.fullname template with a string | `nil` | -| `commonLabels` | Labels to add to all deployed objects | `nil` | -| `commonAnnotations` | Annotations to add to all deployed objects | `[]` | -| `clusterDomain` | Kubernetes cluster domain | `cluster.local` | -| `kubeVersion` | Force target Kubernetes version (using Helm capabilities if not set) | `nil` | - -### Deployment Parameters - -| Parameter | Description | Default | -|------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------| -| `image.registry` | kong image registry | `docker.io` | -| `image.repository` | kong image name | `bitnami/kong` | -| `image.tag` | kong image tag | `{TAG_NAME}` | -| `image.pullPolicy` | kong image pull policy | `IfNotPresent` | -| `image.pullSecrets` | Specify docker-registry secret names as an array | `[]` (does not add image pull secrets to deployed pods) | -| `useDaemonset` | Use a daemonset instead of a deployment. `replicaCount` will not take effect. | `false` | -| `replicaCount` | Number of replicas of the kong Pod | `2` | -| `updateStrategy` | Update strategy for deployment | `{type: "RollingUpdate"}` | -| `schedulerName` | Alternative scheduler | `nil` | -| `database` | Select which database backend Kong will use. Can be 'postgresql' or 'cassandra' | `postgresql` | -| `containerSecurityContext` | Container security podSecurityContext | `{ runAsUser: 1001, runAsNonRoot: true}` | -| `podSecurityContext` | Pod security context | `{}` | -| `hostAliases` | Add deployment host aliases | `[]` | -| `nodeSelector` | Node labels for pod assignment | `{}` | -| `tolerations` | Tolerations for pod assignment | `[]` | -| `affinity` | Affinity for pod assignment | `{}` | -| `podAffinityPreset` | Pod affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` | `""` | -| `podAntiAffinityPreset` | Pod anti-affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` | `soft` | -| `nodeAffinityPreset.type` | Node affinity preset type. Ignored if `affinity` is set. Allowed values: `soft` or `hard` | `""` | -| `nodeAffinityPreset.key` | Node label key to match Ignored if `affinity` is set. | `""` | -| `nodeAffinityPreset.values` | Node label values to match. Ignored if `affinity` is set. | `[]` | -| `podAnnotations` | Pod annotations | `{}` | -| `podLabels` | Pod labels | `{}` | -| `sidecars` | Attach additional containers to the pod (evaluated as a template) | `nil` | -| `initContainers` | Add additional init containers to the pod (evaluated as a template) | `nil` | -| `pdb.enabled` | Deploy a pdb object for the Kong pod | `false` | -| `pdb.maxUnavailable` | Maximum unavailable Kong replicas (expressed in percentage) | `50%` | -| `autoscaling.enabled` | Deploy a HorizontalPodAutoscaler object for the Kong deployment | `false` | -| `autoscaling.apiVersion` | API Version of the HPA object (for compatibility with Openshift) | `v1beta1` | -| `autoscaling.minReplicas` | Minimum number of replicas to scale back | `2` | -| `autoscaling.maxReplicas` | Maximum number of replicas to scale out | `2` | -| `autoscaling.metrics` | Metrics to use when deciding to scale the deployment (evaluated as a template) | `Check values.yaml` | -| `extraVolumes` | Array of extra volumes to be added to the Kong deployment deployment (evaluated as template). Requires setting `extraVolumeMounts` | `nil` | -| `kong.livenessProbe` | Liveness probe (kong container) | `Check values.yaml` | -| `kong.readinessProbe` | Readiness probe (kong container) | `Check values.yaml` | -| `kong.lifecycleHooks` | Lifecycle hooks (kong container) | `Check deployment.yaml` | -| `kong.customLivenessProbe` | Override default liveness probe (kong container) | `nil` | -| `kong.customReadinessProbe` | Override default readiness probe (kong container) | `nil` | -| `kong.resources` | Configure resource requests and limits (kong container) | `nil` | -| `kong.extraVolumeMounts` | Array of extra volume mounts to be added to the Kong Container (evaluated as template). Normally used with `extraVolumes`. | `nil` | -| `ingressController.livenessProbe` | Liveness probe (kong ingress controller container) | `Check values.yaml` | -| `ingressController.readinessProbe` | Readiness probe (kong ingress controller container) | `Check values.yaml` | -| `ingressController.customLivenessProbe` | Override default liveness probe (kong ingress controller container) | `nil` | -| `ingressController.customReadinessProbe` | Override default readiness probe (kong ingress controller container) | `nil` | -| `ingressController.resources` | Configure resource requests and limits (kong ingress controller container) | `nil` | -| `ingressController.extraVolumeMounts` | Array of extra volume mounts to be added to the Kong Ingress Controller container (evaluated as template). Normally used with `extraVolumes`. | `nil` | -| `migration.resources` | Configure resource requests and limits (migration container) | `nil` | -| `migration.hostAliases` | Add deployment host aliases | `[]` | -| `migration.extraVolumeMounts` | Array of extra volume mounts to be added to the Kong Container (evaluated as template). Normally used with `extraVolumes`. | `nil` | -| `extraDeploy` | Array of extra objects to deploy with the release (evaluated as a template). | `nil` | - -### Traffic Exposure Parameters - -| Parameter | Description | Default | -|----------------------------------|------------------------------------------------------------------|--------------------------------| -| `service.type` | Kubernetes Service type | `ClusterIP` | -| `service.externalTrafficPolicy` | external traffic policy managing client source IP preservation | `Cluster` | -| `service.exposeAdmin` | Add the Kong Admin ports to the service | `false` | -| `service.proxyHttpPort` | kong proxy HTTP service port port | `80` | -| `service.proxyHttpsPort` | kong proxy HTTPS service port port | `443` | -| `service.adminHttpPort` | kong admin HTTPS service port (only if service.exposeAdmin=true) | `8001` | -| `service.adminHttpsPort` | kong admin HTTPS service port (only if service.exposeAdmin=true) | `8443` | -| `service.proxyHttpNodePort` | Port to bind to for NodePort service type (proxy HTTP) | `nil` | -| `service.proxyHttpsNodePort` | Port to bind to for NodePort service type (proxy HTTPS) | `nil` | -| `service.adminHttpNodePort` | Port to bind to for NodePort service type (admin HTTP) | `nil` | -| `service.aminHttpsNodePort` | Port to bind to for NodePort service type (proxy HTTP) | `nil` | -| `service.annotations` | Annotations for kong service | `{}` | -| `service.clusterIP` | Cluster internal IP of the service | `nil` | -| `service.loadBalancerIP` | loadBalancerIP if kong service type is `LoadBalancer` | `nil` | -| `ingress.enabled` | Enable ingress controller resource | `false` | -| `ingress.certManager` | Add annotations for cert-manager | `false` | -| `ingress.hostname` | Default host for the ingress resource | `kong.local` | -| `ingress.apiVersion` | Force Ingress API version (automatically detected if not set) | `` | -| `ingress.path` | Ingress path | `/` | -| `ingress.pathType` | Ingress path type | `ImplementationSpecific` | -| `ingress.tls` | Create TLS Secret | `false` | -| `ingress.annotations` | Ingress annotations | `[]` (evaluated as a template) | -| `ingress.extraHosts[0].name` | Additional hostnames to be covered | `nil` | -| `ingress.extraHosts[0].path` | Additional hostnames to be covered | `nil` | -| `ingress.extraPaths` | Additional arbitrary path/backend objects | `nil` | -| `ingress.extraTls[0].hosts[0]` | TLS configuration for additional hostnames to be covered | `nil` | -| `ingress.extraTls[0].secretName` | TLS configuration for additional hostnames to be covered | `nil` | -| `ingress.secrets[0].name` | TLS Secret Name | `nil` | -| `ingress.secrets[0].certificate` | TLS Secret Certificate | `nil` | -| `ingress.secrets[0].key` | TLS Secret Key | `nil` | - -### Kong Container Parameters - -| Parameter | Description | Default | -|---------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|---------| -| `kong.extraEnvVars` | Array containing extra env vars to configure Kong | `nil` | -| `kong.extraEnvVarsCM` | ConfigMap containing extra env vars to configure Kong | `nil` | -| `kong.extraEnvVarsSecret` | Secret containing extra env vars to configure Kong (in case of sensitive data) | `nil` | -| `kong.command` | Override default container command (useful when using custom images) | `nil` | -| `kong.args` | Override default container args (useful when using custom images) | `nil` | -| `kong.initScriptsCM` | ConfigMap containing `/docker-entrypoint-initdb.d` scripts to be executed at initialization time (evaluated as a template) | `nil` | -| `kong.initScriptsSecret` | Secret containing `/docker-entrypoint-initdb.d` scripts to be executed at initialization time (that contain sensitive data). Evaluated as a template. | `nil` | - -### Kong Migration job Parameters - -| Parameter | Description | Default | -|--------------------------------|--------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------| -| `migration.image.registry` | Override Kong migration job image registry (Kong image if not set) | `nil` | -| `migration.image.repository` | Override Kong migration job image name (Kong image if not set) | `nil` | -| `migration.image.tag` | Override Kong migration job image tag (Kong image if not set) | `nil` | -| `migration.extraEnvVars` | Array containing extra env vars to configure the Kong migration job | `nil` | -| `migration.extraEnvVarsCM` | ConfigMap containing extra env vars to configure the Kong migration job | `nil` | -| `migration.extraEnvVarsSecret` | Secret containing extra env vars to configure the Kong migration job (in case of sensitive data) | `nil` | -| `migration.command` | Override default container command (useful when using custom images) | `nil` | -| `migration.args` | Override default container args (useful when using custom images) | `nil` | -| `migration.annotations` | Add annotations to the job | `helm.sh/hook: post-install, post-upgrade, helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded` | - -### Kong Ingress Controller Container Parameters - -| Parameter | Description | Default | -|-------------------------------------------------|---------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------| -| `ingressController.enabled` | Enable/disable the Kong Ingress Controller | `true` | -| `ingressController.image.registry` | Kong Ingress Controller image registry | `docker.io` | -| `ingressController.image.repository` | Kong Ingress Controller image name | `bitnami/kong` | -| `ingressController.image.tag` | Kong Ingress Controller image tag | `{TAG_NAME}` | -| `ingressController.image.pullPolicy` | kong ingress controller image pull policy | `IfNotPresent` | -| `ingressController.image.pullSecrets` | Specify docker-registry secret names as an array | `[]` (does not add image pull secrets to deployed pods) | -| `ingressController.proxyReadyTimeout` | Maximum time (in seconds) to wait for the Kong container to be ready | `300` | -| `ingressController.extraEnvVars` | Array containing extra env vars to configure Kong | `nil` | -| `ingressController.extraEnvVarsCM` | ConfigMap containing extra env vars to configure Kong Ingress Controller | `nil` | -| `ingressController.extraEnvVarsSecret` | Secret containing extra env vars to configure Kong Ingress Controller (in case of sensitive data) | `nil` | -| `ingressController.rbac.create` | Create the necessary Service Accounts, Roles and Rolebindings for the Ingress Controller to work | `true` | -| `ingressController.rbac.existingServiceAccount` | Use an existing service account for all the RBAC operations | `nil` | -| `ingressController.customResourceDeletePolicy` | Add custom CRD resource delete policy (for Helm 2 support) | `nil` | -| `ingressController.rbac.existingServiceAccount` | Use an existing service account for all the RBAC operations | `nil` | -| `ingressController.ingressClass` | Name of the class to register Kong Ingress Controller (useful when having other Ingress Controllers in the cluster) | `nil` | -| `ingressController.command` | Override default container command (useful when using custom images) | `nil` | -| `ingressController.args` | Override default container args (useful when using custom images) | `nil` | - -### PostgreSQL Parameters - -| Parameter | Description | Default | -|---------------------------------|--------------------------------------------------------------------------------------------------------------------------------|---------| -| `postgresql.enabled` | Deploy the PostgreSQL sub-chart | `true` | -| `postgresql.usePasswordFile` | Mount the PostgreSQL secret as a file | `no` | -| `postgresql.existingSecret` | Use an existing secret file with the PostgreSQL password (can be used with the bundled chart or with an existing installation) | `nil` | -| `postgresql.postgresqlDatabase` | Database name to be used by Kong | `kong` | -| `postgresql.postgresqlUsername` | Username to be created by the PostgreSQL bundled chart | `kong` | -| `postgresql.external.host` | Host of an external PostgreSQL installation | `nil` | -| `postgresql.external.user` | Username of the external PostgreSQL installation | `nil` | -| `postgresql.external.password` | Password of the external PostgreSQL installation | `nil` | - -### Cassandra Parameters - -| Parameter | Description | Default | -|-------------------------------|-------------------------------------------------------------------------------------------------------------------------------|---------| -| `cassandra.enabled` | Deploy the Cassandra sub-chart | `false` | -| `cassandra.usePasswordFile` | Mount the Cassandra secret as a file | `no` | -| `cassandra.existingSecret` | Use an existing secret file with the Cassandra password (can be used with the bundled chart or with an existing installation) | `nil` | -| `cassandra.dbUser.user` | Username to be created by the cassandra bundled chart | `kong` | -| `cassandra.external.hosts` | Hosts of an external cassandra installation | `nil` | -| `cassandra.external.port` | Port of an external cassandra installation | `nil` | -| `cassandra.external.user` | Username of the external cassandra installation | `nil` | -| `cassandra.external.password` | Password of the external cassandra installation | `nil` | - -### Metrics Parameters - -| Parameter | Description | Default | -|-----------------------------------------|--------------------------------------------------------------------------------------------------------|-------------------------------------------| -| `metrics.enabled` | Enable the export of Prometheus metrics | `false` | -| `metrics.service.type` | Type of the Prometheus metrics service | `ClusterIP file` | -| `metrics.service.port` | Port of the Prometheus metrics service | `9119` | -| `metrics.service.annotations` | Port for Prometheus metrics service | `9119` | -| `metrics.service.annotations` | Annotations for Prometheus metrics service | `Check values.yaml file` | -| `metrics.serviceMonitor.enabled` | if `true`, creates a Prometheus Operator ServiceMonitor (also requires `metrics.enabled` to be `true`) | `false` | -| `metrics.serviceMonitor.namespace` | Namespace in which Prometheus is running | `nil` | -| `metrics.serviceMonitor.serviceAccount` | Service account used by Prometheus | `nil` | -| `metrics.serviceMonitor.rbac.create` | if `true`, creates a Role and Role binding for Prometheus so it can reach kong's namespace | `true` | -| `metrics.serviceMonitor.interval` | Interval at which metrics should be scraped. | `nil` (Prometheus Operator default value) | -| `metrics.serviceMonitor.scrapeTimeout` | Timeout after which the scrape is ended | `nil` (Prometheus Operator default value) | -| `metrics.serviceMonitor.selector` | Prometheus instance selector labels | `nil` | - -Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example, - -```console - helm install my-release \ - --set service.exposeAdmin=true bitnami/kong -``` - -The above command exposes the Kong admin ports inside the Kong service. - -Alternatively, a YAML file that specifies the values for the parameters can be provided while installing the chart. For example, - -```console - helm install my-release -f values.yaml bitnami/kong -``` - -> **Tip**: You can use the default [values.yaml](values.yaml) - -## Configuration and installation details - -### [Rolling VS Immutable tags](https://docs.bitnami.com/containers/how-to/understand-rolling-tags-containers/) - -It is strongly recommended to use immutable tags in a production environment. This ensures your deployment does not change automatically if the same tag is updated with a different image. - -Bitnami will release a new chart updating its containers if a new version of the main container, significant changes, or critical vulnerabilities exist. - -### Database backend - -The Bitnami Kong chart allows setting two database backends: PostgreSQL or Cassandra. For each option, there are two extra possibilities: deploy a sub-chart with the database installation or use an existing one. The list below details the different options (replace the placeholders specified between _UNDERSCORES_): - -- Deploy the PostgreSQL sub-chart (default) - -```console - helm install my-release bitnami/kong -``` - -- Use an external PostgreSQL database - -```console - helm install my-release bitnami/kong \ - --set postgresql.enabled=false \ - --set postgresql.external.host=_HOST_OF_YOUR_POSTGRESQL_INSTALLATION_ \ - --set postgresql.external.password=_PASSWORD_OF_YOUR_POSTGRESQL_INSTALLATION_ \ - --set postgresql.external.user=_USER_OF_YOUR_POSTGRESQL_INSTALLATION_ -``` - -- Deploy the Cassandra sub-chart - -```console - helm install my-release bitnami/kong \ - --set database=cassandra \ - --set postgresql.enabled=false \ - --set cassandra.enabled=true -``` - -- Use an existing Cassandra installation - -```console - helm install my-release bitnami/kong \ - --set database=cassandra \ - --set postgresql.enabled=false \ - --set cassandra.enabled=false \ - --set cassandra.external.hosts[0]=_CONTACT_POINT_0_OF_YOUR_CASSANDRA_CLUSTER_ \ - --set cassandra.external.hosts[1]=_CONTACT_POINT_1_OF_YOUR_CASSANDRA_CLUSTER_ \ - ... - --set cassandra.external.user=_USER_OF_YOUR_CASSANDRA_INSTALLATION_ \ - --set cassandra.external.password=_PASSWORD_OF_YOUR_CASSANDRA_INSTALLATION_ -``` - -### DB-less - -Kong 1.1 added the capability to run Kong without a database, using only in-memory storage for entities: we call this DB-less mode. When running Kong DB-less, the configuration of entities is done in a second configuration file, in YAML or JSON, using declarative configuration (ref. [Link](https://docs.konghq.com/gateway-oss/1.1.x/db-less-and-declarative-config/)). -As is said in step 4 of [kong official docker installation](https://docs.konghq.com/install/docker#db-less-mode), just add the env variable "KONG_DATABASE=off". - -#### How to enable it - -1. Set `database` value with any value other than "postgresql" or "cassandra". For example `database: "off"` -2. Use `kong.extraEnvVars` value to set the `KONG_DATABASE` environment variable: -```yaml -kong.extraEnvVars: -- name: KONG_DATABASE - value: "off" -``` - -### Sidecars and Init Containers - -If you have a need for additional containers to run within the same pod as Kong (e.g. an additional metrics or logging exporter), you can do so via the `sidecars` config parameter. Simply define your container according to the Kubernetes container spec. - -```yaml -sidecars: - - name: your-image-name - image: your-image - imagePullPolicy: Always - ports: - - name: portname - containerPort: 1234 -``` - -Similarly, you can add extra init containers using the `initContainers` parameter. - -```yaml -initContainers: - - name: your-image-name - image: your-image - imagePullPolicy: Always - ports: - - name: portname - containerPort: 1234 -``` - -### Adding extra environment variables - -In case you want to add extra environment variables (useful for advanced operations like custom init scripts), you can use the `kong.extraEnvVars` property. - -```yaml -kong: - extraEnvVars: - - name: KONG_LOG_LEVEL - value: error -``` - -Alternatively, you can use a ConfigMap or a Secret with the environment variables. To do so, use the `kong.extraEnvVarsCM` or the `kong.extraEnvVarsSecret` values. - -The Kong Ingress Controller and the Kong Migration job also allow this kind of configuration via the `ingressController.extraEnvVars`, `ingressController.extraEnvVarsCM`, `ingressController.extraEnvVarsSecret`, `migration.extraEnvVars`, `migration.extraEnvVarsCM` and `migration.extraEnvVarsSecret` values. - -### Using custom init scripts - -For advanced operations, the Bitnami Kong charts allows using custom init scripts that will be mounted in `/docker-entrypoint.init-db`. You can use a ConfigMap or a Secret (in case of sensitive data) for mounting these extra scripts. Then use the `kong.initScriptsCM` and `kong.initScriptsSecret` values. - -```console -elasticsearch.hosts[0]=elasticsearch-host -elasticsearch.port=9200 -initScriptsCM=special-scripts -initScriptsSecret=special-scripts-sensitive -``` - -### Deploying extra resources - -There are cases where you may want to deploy extra objects, such as KongPlugins, KongConsumers, amongst others. For covering this case, the chart allows adding the full specification of other objects using the `extraDeploy` parameter. The following example would activate a plugin at deployment time. - -```yaml -## Extra objects to deploy (value evaluated as a template) -## -extraDeploy: |- - - apiVersion: configuration.konghq.com/v1 - kind: KongPlugin - metadata: - name: {{ include "common.names.fullname" . }}-plugin-correlation - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 6 }} - config: - header_name: my-request-id - plugin: correlation-id -``` - -### Setting Pod's affinity - -This chart allows you to set your custom affinity using the `affinity` parameter. Find more information about Pod's affinity in the [kubernetes documentation](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity). - -As an alternative, you can use of the preset configurations for pod affinity, pod anti-affinity, and node affinity available at the [bitnami/common](https://github.com/bitnami/charts/tree/master/bitnami/common#affinities) chart. To do so, set the `podAffinityPreset`, `podAntiAffinityPreset`, or `nodeAffinityPreset` parameters. - -## Troubleshooting - -Find more information about how to deal with common errors related to Bitnami’s Helm charts in [this troubleshooting guide](https://docs.bitnami.com/general/how-to/troubleshoot-helm-chart-issues). - -## Upgrading - -It's necessary to specify the existing passwords while performing a upgrade to ensure the secrets are not updated with invalid randomly generated passwords. Remember to specify the existing values of the `postgresql.postgresqlPassword` or `cassandra.password` parameters when upgrading the chart: - -```bash -$ helm upgrade my-release bitnami/kong \ - --set database=postgresql - --set postgresql.enabled=true - --set - --set postgresql.postgresqlPassword=[POSTGRESQL_PASSWORD] -``` - -> Note: you need to substitute the placeholders _[POSTGRESQL_PASSWORD]_ with the values obtained from instructions in the installation notes. - -### To 3.1.0 - -Kong Ingress Controller version was bumped to new major version, `1.x.x`. The associated CRDs were updated accordingly. - -### To 3.0.0 - -[On November 13, 2020, Helm v2 support was formally finished](https://github.com/helm/charts#status-of-the-project), this major version is the result of the required changes applied to the Helm Chart to be able to incorporate the different features added in Helm v3 and to be consistent with the Helm project itself regarding the Helm v2 EOL. - -**What changes were introduced in this major version?** - -- Previous versions of this Helm Chart use `apiVersion: v1` (installable by both Helm 2 and 3), this Helm Chart was updated to `apiVersion: v2` (installable by Helm 3 only). [Here](https://helm.sh/docs/topics/charts/#the-apiversion-field) you can find more information about the `apiVersion` field. -- Move dependency information from the *requirements.yaml* to the *Chart.yaml* -- After running `helm dependency update`, a *Chart.lock* file is generated containing the same structure used in the previous *requirements.lock* -- The different fields present in the *Chart.yaml* file has been ordered alphabetically in a homogeneous way for all the Bitnami Helm Charts -- This chart depends on the **PostgreSQL 10** instead of **PostgreSQL 9**. Apart from the same changes that are described in this section, there are also other major changes due to the master/slave nomenclature was replaced by primary/readReplica. [Here](https://github.com/bitnami/charts/pull/4385) you can find more information about the changes introduced. - -**Considerations when upgrading to this version** - -- If you want to upgrade to this version using Helm v2, this scenario is not supported as this version doesn't support Helm v2 anymore -- If you installed the previous version with Helm v2 and wants to upgrade to this version with Helm v3, please refer to the [official Helm documentation](https://helm.sh/docs/topics/v2_v3_migration/#migration-use-cases) about migrating from Helm v2 to v3 -- If you want to upgrade to this version from a previous one installed with Helm v3, it should be done reusing the PVC used to hold the PostgreSQL data on your previous release. To do so, follow the instructions below (the following example assumes that the release name is `kong`): - -> NOTE: Please, create a backup of your database before running any of those actions. - -##### Export secrets and required values to update - -```console -$ export POSTGRESQL_PASSWORD=$(kubectl get secret --namespace default kong-postgresql -o jsonpath="{.data.postgresql-password}" | base64 --decode) -$ export POSTGRESQL_PVC=$(kubectl get pvc -l app.kubernetes.io/instance=kong,app.kubernetes.io/name=postgresql,role=master -o jsonpath="{.items[0].metadata.name}") -``` - -##### Delete statefulsets - -Delete PostgreSQL statefulset. Notice the option `--cascade=false`: - -``` -$ kubectl delete statefulsets.apps kong-postgresql --cascade=false -``` - -##### Upgrade the chart release - -```console -$ helm upgrade kong bitnami/kong \ - --set postgresql.postgresqlPassword=$POSTGRESQL_PASSWORD \ - --set postgresql.persistence.existingClaim=$POSTGRESQL_PVC -``` - -##### Force new statefulset to create a new pod for postgresql - -```console -$ kubectl delete pod kong-postgresql-0 -``` -Finally, you should see the lines below in MariaDB container logs: - -```console -$ kubectl logs $(kubectl get pods -l app.kubernetes.io/instance=postgresql,app.kubernetes.io/name=postgresql,role=primary -o jsonpath="{.items[0].metadata.name}") -... -postgresql 08:05:12.59 INFO ==> Deploying PostgreSQL with persisted data... -... -``` - -**Useful links** - -- https://docs.bitnami.com/tutorials/resolve-helm2-helm3-post-migration-issues/ -- https://helm.sh/docs/topics/v2_v3_migration/ -- https://helm.sh/blog/migrate-from-helm-v2-to-helm-v3/ - -### To 2.0.0 - -PostgreSQL and Cassandra dependencies versions were bumped to new major versions, `9.x.x` and `6.x.x` respectively. Both of these include breaking changes and hence backwards compatibility is no longer guaranteed. - -In order to properly migrate your data to this new version: - -* If you were using PostgreSQL as your database, please refer to the [PostgreSQL Upgrade Notes](https://github.com/bitnami/charts/tree/master/bitnami/postgresql#900). - -* If you were using Cassandra as your database, please refer to the [Cassandra Upgrade Notes](https://github.com/bitnami/charts/tree/master/bitnami/cassandra#to-600). diff --git a/packages/helm-charts/kong/ci/values-editing-containers.yaml b/packages/helm-charts/kong/ci/values-editing-containers.yaml deleted file mode 100644 index f8a1225bce0..00000000000 --- a/packages/helm-charts/kong/ci/values-editing-containers.yaml +++ /dev/null @@ -1,116 +0,0 @@ -## Edit kong container -## -kong: - command: - - sleep - args: - - "3600" - initScriptsCM: kong-initscripts - initScriptsSecret: kong-initscripts-secret - extraEnvVars: - - name: KONG_LOG_LEVEL - value: error - extraEnvVarsCM: kong-extraenv-cm - extraEnvVarsSecret: kong-extraenv-secret - extraVolumeMounts: - - name: kong-certs - mountPath: /bitnami/kong/certs - resources: - limits: - cpu: 500m - memory: 1Gi - -## Edit migration container -## -migration: - command: - - echo - args: - - test - extraEnvVars: - - name: KONG_CASSANDRA_USER - value: cassandra - extraEnvVarsCM: kong-migrate-extraenv-cm - extraEnvVarsSecret: kong-migrate-extraenv-secret - extraVolumeMounts: - - name: kong-migrate-credentials - mountPath: /bitnami/kong/credentials - resources: - limits: - cpu: 300m - memory: 2Gi - -## Edit migration container -## -ingressController: - command: - - echo - args: - - hello - extraEnvVars: - - name: CONTROLLER_LOG_LEVEL - value: error - extraEnvVarsCM: kong-controller-extraenv-cm - extraEnvVarsSecret: kong-controller-extraenv-secret - extraVolumeMounts: - - name: kong-controller-credentials - mountPath: /bitnami/kong/credentials - resources: - limits: - cpu: 1000m - memory: 2Gi - -sidecars: |- - - name: test-sidecar - image: bitnami/minideb - command: - - echo - - hi - -initContainers: |- - - name: test-init - image: bitnami/git - command: - - git - - clone - - github.com/bitnami/bitnami-docker-kong" - -volumes: - - name: kong-controller-credentials - hostPath: /tmp/credentials - - name: kong-migrate-credentials - hostPath: /tmp/migrate/credentials - - name: kong-certs - persistentVolumeClaim: - claimName: kong-certs-pvc - -nodeSelector: - disktype: ssd - -tolerations: - - key: "key" - operator: "Equal" - value: "value" - effect: "NoSchedule" - -affinity: |- - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: kubernetes.io/e2e-az-name - operator: In - values: - - e2e-az1 - - e2e-az2 - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 1 - preference: - matchExpressions: - - key: another-node-label-key - operator: In - values: - - another-node-label-value - -podAnnotations: - k8s/annotation: test diff --git a/packages/helm-charts/kong/ci/values-external-cassandra.yaml b/packages/helm-charts/kong/ci/values-external-cassandra.yaml deleted file mode 100644 index 22fa2caa590..00000000000 --- a/packages/helm-charts/kong/ci/values-external-cassandra.yaml +++ /dev/null @@ -1,13 +0,0 @@ -database: cassandra - -postgresql: - enabled: false -cassandra: - enabled: false - external: - hosts: - - test-cassandra1 - - test-cassandra2 - - test-cassandra3 - user: test - password: test diff --git a/packages/helm-charts/kong/ci/values-external-postgresql.yaml b/packages/helm-charts/kong/ci/values-external-postgresql.yaml deleted file mode 100644 index 31de2af466b..00000000000 --- a/packages/helm-charts/kong/ci/values-external-postgresql.yaml +++ /dev/null @@ -1,8 +0,0 @@ -database: postgresql - -postgresql: - enabled: false - external: - host: test-postgresql - user: test - password: test diff --git a/packages/helm-charts/kong/ci/values-ingress.yaml b/packages/helm-charts/kong/ci/values-ingress.yaml deleted file mode 100644 index f6ccc628a90..00000000000 --- a/packages/helm-charts/kong/ci/values-ingress.yaml +++ /dev/null @@ -1,2 +0,0 @@ -ingress: - enabled: true diff --git a/packages/helm-charts/kong/ci/values-metrics-hpa-pdb.yaml b/packages/helm-charts/kong/ci/values-metrics-hpa-pdb.yaml deleted file mode 100644 index b5177a061f3..00000000000 --- a/packages/helm-charts/kong/ci/values-metrics-hpa-pdb.yaml +++ /dev/null @@ -1,7 +0,0 @@ -metrics: - enabled: true -autoscaling: - enabled: true - -pdb: - enabled: true diff --git a/packages/helm-charts/kong/crds/custom-resource-definitions.yaml b/packages/helm-charts/kong/crds/custom-resource-definitions.yaml deleted file mode 100644 index 33ec2805527..00000000000 --- a/packages/helm-charts/kong/crds/custom-resource-definitions.yaml +++ /dev/null @@ -1,426 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: kongconsumers.configuration.konghq.com -spec: - group: configuration.konghq.com - version: v1 - scope: Namespaced - names: - kind: KongConsumer - plural: kongconsumers - shortNames: - - kc - additionalPrinterColumns: - - name: Username - type: string - description: Username of a Kong Consumer - JSONPath: .username - - name: Age - type: date - description: Age - JSONPath: .metadata.creationTimestamp - validation: - openAPIV3Schema: - properties: - username: - type: string - custom_id: - type: string - credentials: - type: array - items: - type: string - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: kongplugins.configuration.konghq.com -spec: - group: configuration.konghq.com - version: v1 - scope: Namespaced - names: - kind: KongPlugin - plural: kongplugins - shortNames: - - kp - additionalPrinterColumns: - - name: Plugin-Type - type: string - description: Name of the plugin - JSONPath: .plugin - - name: Age - type: date - description: Age - JSONPath: .metadata.creationTimestamp - - name: Disabled - type: boolean - description: Indicates if the plugin is disabled - JSONPath: .disabled - priority: 1 - - name: Config - type: string - description: Configuration of the plugin - JSONPath: .config - priority: 1 - validation: - openAPIV3Schema: - required: - - plugin - properties: - plugin: - type: string - disabled: - type: boolean - config: - type: object - configFrom: - type: object - properties: - secretKeyRef: - required: - - name - - key - type: object - properties: - name: - type: string - key: - type: string - run_on: - type: string - enum: - - first - - second - - all - protocols: - type: array - items: - type: string - enum: - - http - - https - - grpc - - grpcs - - tcp - - tls - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: kongclusterplugins.configuration.konghq.com -spec: - group: configuration.konghq.com - version: v1 - scope: Cluster - names: - kind: KongClusterPlugin - plural: kongclusterplugins - shortNames: - - kcp - additionalPrinterColumns: - - name: Plugin-Type - type: string - description: Name of the plugin - JSONPath: .plugin - - name: Age - type: date - description: Age - JSONPath: .metadata.creationTimestamp - - name: Disabled - type: boolean - description: Indicates if the plugin is disabled - JSONPath: .disabled - priority: 1 - - name: Config - type: string - description: Configuration of the plugin - JSONPath: .config - priority: 1 - validation: - openAPIV3Schema: - required: - - plugin - properties: - plugin: - type: string - disabled: - type: boolean - config: - type: object - configFrom: - type: object - properties: - secretKeyRef: - required: - - name - - namespace - - key - type: object - properties: - namespace: - type: string - name: - type: string - key: - type: string - run_on: - type: string - enum: - - first - - second - - all - protocols: - type: array - items: - type: string - enum: - - http - - https - - grpc - - grpcs - - tcp - - tls - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: kongingresses.configuration.konghq.com -spec: - group: configuration.konghq.com - version: v1 - scope: Namespaced - names: - kind: KongIngress - plural: kongingresses - shortNames: - - ki - validation: - openAPIV3Schema: - properties: - route: - properties: - methods: - type: array - items: - type: string - headers: - type: object - additionalProperties: - type: array - items: - type: string - regex_priority: - type: integer - strip_path: - type: boolean - preserve_host: - type: boolean - path_handling: - type: string - enum: - - "v0" - - "v1" - protocols: - type: array - items: - type: string - enum: - - http - - https - - grpc - - grpcs - - tcp - - tls - https_redirect_status_code: - type: integer - proxy: - type: object - properties: - protocol: - type: string - enum: - - http - - https - - grpc - - grpcs - - tcp - - tls - path: - type: string - pattern: ^/.*$ - retries: - type: integer - minimum: 0 - connect_timeout: - type: integer - minimum: 0 - read_timeout: - type: integer - minimum: 0 - write_timeout: - type: integer - minimum: 0 - upstream: - type: object - properties: - algorithm: - type: string - enum: - - "round-robin" - - "consistent-hashing" - - "least-connections" - host_header: - type: string - hash_on: - type: string - hash_on_cookie: - type: string - hash_on_cookie_path: - type: string - hash_on_header: - type: string - hash_fallback_header: - type: string - hash_fallback: - type: string - slots: - type: integer - minimum: 10 - healthchecks: - type: object - properties: - threshold: - type: integer - active: - type: object - properties: - concurrency: - type: integer - minimum: 1 - timeout: - type: integer - minimum: 0 - http_path: - type: string - pattern: ^/.*$ - healthy: &healthy - type: object - properties: - http_statuses: - type: array - items: - type: integer - interval: - type: integer - minimum: 0 - successes: - type: integer - minimum: 0 - unhealthy: &unhealthy - type: object - properties: - http_failures: - type: integer - minimum: 0 - http_statuses: - type: array - items: - type: integer - interval: - type: integer - minimum: 0 - tcp_failures: - type: integer - minimum: 0 - timeout: - type: integer - minimum: 0 - passive: - type: object - properties: - healthy: *healthy - unhealthy: *unhealthy - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: tcpingresses.configuration.konghq.com -spec: - group: configuration.konghq.com - version: v1beta1 - scope: Namespaced - names: - kind: TCPIngress - plural: tcpingresses - additionalPrinterColumns: - - name: Address - type: string - description: Address of the load balancer - JSONPath: .status.loadBalancer.ingress[*].ip - - name: Age - type: date - description: Age - JSONPath: .metadata.creationTimestamp - subresources: - status: {} - validation: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - type: object - properties: - tls: - type: array - items: - type: object - properties: - hosts: - type: array - items: - type: string - secretName: - type: string - rules: - type: array - items: - type: object - properties: - host: - type: string - port: - type: integer - format: int32 - backend: - type: object - properties: - serviceName: - type: string - servicePort: - format: int32 - type: integer - status: - type: object - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/packages/helm-charts/kong/kong.conf b/packages/helm-charts/kong/kong.conf deleted file mode 100644 index b4102ed3fb8..00000000000 --- a/packages/helm-charts/kong/kong.conf +++ /dev/null @@ -1,1512 +0,0 @@ -# ----------------------- -# Kong configuration file -# ----------------------- -# -# The commented-out settings shown in this file represent the default values. -# -# This file is read when `kong start` or `kong prepare` are used. Kong -# generates the Nginx configuration with the settings specified in this file. -# -# All environment variables prefixed with `KONG_` and capitalized will override -# the settings specified in this file. -# Example: -# `log_level` setting -> `KONG_LOG_LEVEL` env variable -# -# Boolean values can be specified as `on`/`off` or `true`/`false`. -# Lists must be specified as comma-separated strings. -# -# All comments in this file can be removed safely, including the -# commented-out properties. -# You can verify the integrity of your settings with `kong check `. - -#------------------------------------------------------------------------------ -# GENERAL -#------------------------------------------------------------------------------ - -prefix = /opt/bitnami/kong/server # Working directory. Equivalent to Nginx's - # prefix path, containing temporary files - # and logs. - # Each Kong process must have a separate - # working directory. - -log_level = notice # Log level of the Nginx server. Logs are - # found at `/logs/error.log`. - -# See http://nginx.org/en/docs/ngx_core_module.html#error_log for a list -# of accepted values. - -proxy_access_log = logs/access.log # Path for proxy port request access - # logs. Set this value to `off` to - # disable logging proxy requests. - # If this value is a relative path, - # it will be placed under the - # `prefix` location. - - -proxy_error_log = logs/error.log # Path for proxy port request error - # logs. The granularity of these logs - # is adjusted by the `log_level` - # property. - -proxy_stream_access_log = logs/access.log basic # Path for tcp streams proxy port access - # logs. Set this value to `off` to - # disable logging proxy requests. - # If this value is a relative path, - # it will be placed under the - # `prefix` location. - # `basic` is defined as `'$remote_addr [$time_local] ' - # '$protocol $status $bytes_sent $bytes_received ' - # '$session_time'` - -proxy_stream_error_log = logs/error.log # Path for tcp streams proxy port request error - # logs. The granularity of these logs - # is adjusted by the `log_level` - # property. - -admin_access_log = logs/admin_access.log # Path for Admin API request access - # logs. If Hybrid Mode is enabled - # and the current node is set to be - # the Control Plane, then the - # connection requests from Data Planes - # are also written to this file with - # server name "kong_cluster_listener". - # - # Set this value to `off` to - # disable logging Admin API requests. - # If this value is a relative path, - # it will be placed under the - # `prefix` location. - -admin_error_log = logs/error.log # Path for Admin API request error - # logs. The granularity of these logs - # is adjusted by the `log_level` - # property. - -status_access_log = off # Path for Status API request access - # logs. The default value of `off` - # implies that logging for this API - # is disabled by default. - # If this value is a relative path, - # it will be placed under the - # `prefix` location. - -status_error_log = logs/status_error.log # Path for Status API request error - # logs. The granularity of these logs - # is adjusted by the `log_level` - # property. - -plugins = bundled # Comma-separated list of plugins this node - # should load. By default, only plugins - # bundled in official distributions are - # loaded via the `bundled` keyword. - # - # Loading a plugin does not enable it by - # default, but only instructs Kong to load its - # source code, and allows to configure the - # plugin via the various related Admin API - # endpoints. - # - # The specified name(s) will be substituted as - # such in the Lua namespace: - # `kong.plugins.{name}.*`. - # - # When the `off` keyword is specified as the - # only value, no plugins will be loaded. - # - # `bundled` and plugin names can be mixed - # together, as the following examples suggest: - # - # - `plugins = bundled,custom-auth,custom-log` - # will include the bundled plugins plus two - # custom ones - # - `plugins = custom-auth,custom-log` will - # *only* include the `custom-auth` and - # `custom-log` plugins. - # - `plugins = off` will not include any - # plugins - # - # **Note:** Kong will not start if some - # plugins were previously configured (i.e. - # have rows in the database) and are not - # specified in this list. Before disabling a - # plugin, ensure all instances of it are - # removed before restarting Kong. - # - # **Note:** Limiting the amount of available - # plugins can improve P99 latency when - # experiencing LRU churning in the database - # cache (i.e. when the configured - # `mem_cache_size`) is full. - -#pluginserver_names = # Comma-separated list of names for pluginserver - # processes. The actual names are used for - # log messages and to relate the actual settings. - -#pluginserver_XXX_socket = /.socket # Path to the unix socket - # used by the pluginserver. -#pluginserver_XXX_start_cmd = /usr/local/bin/ # Full command (including - # any needed arguments) to - # start the pluginserver -#pluginserver_XXX_query_cmd = /usr/local/bin/query_ # Full command to "query" the - # pluginserver. Should - # produce a JSON with the - # dump info of all plugins it - # manages - -#port_maps = # With this configuration parameter, you can - # let the Kong to know about the port from - # which the packets are forwarded to it. This - # is fairly common when running Kong in a - # containerized or virtualized environment. - # For example, `port_maps=80:8000, 443:8443` - # instructs Kong that the port 80 is mapped - # to 8000 (and the port 443 to 8443), where - # 8000 and 8443 are the ports that Kong is - # listening to. - # - # This parameter helps Kong set a proper - # forwarded upstream HTTP request header or to - # get the proper forwarded port with the Kong PDK - # (in case other means determining it has - # failed). It changes routing by a destination - # port to route by a port from which packets - # are forwarded to Kong, and similarly it - # changes the default plugin log serializer to - # use the port according to this mapping - # instead of reporting the port Kong is - # listening to. - -anonymous_reports = on # Send anonymous usage data such as error - # stack traces to help improve Kong. - -#------------------------------------------------------------------------------ -# HYBRID MODE -#------------------------------------------------------------------------------ - -role = traditional # Use this setting to enable Hybrid Mode, - # This allows running some Kong nodes in a - # control plane role with a database and - # have them deliver configuration updates - # to other nodes running to DB-less running in - # a Data Plane role. - # - # Valid values to this setting are: - # - # - `traditional`: do not use Hybrid Mode. - # - `control_plane`: this node runs in a - # control plane role. It can use a database - # and will deliver configuration updates - # to data plane nodes. - # - `data_plane`: this is a data plane node. - # It runs DB-less and receives configuration - # updates from a control plane node. - -cluster_mtls = shared # Sets the verification between nodes of the - # cluster. - # - # Valid values to this setting are: - # - # - `shared`: use a shared certificate/key - # pair specified with the `cluster_cert` - # and `cluster_cert_key` settings. - # Note that CP and DP nodes have to present - # the same certificate to establish mTLS - # connections. - # - `pki`: use `cluster_ca_cert`, - # `cluster_server_name` and `cluster_cert` - # for verification. - # These are different certificates for each - # DP node, but issued by a cluster-wide - # common CA certificate: `cluster_ca_cert`. - -#cluster_cert = # Filename of the cluster certificate to use - # when establishing secure communication - # between control and data plane nodes. - # You can use the `kong hybrid` command to - # generate the certificate/key pair. - # Under `shared` mode, it must be the same - # for all nodes. Under `pki` mode it - # should be a different certificate for each - # DP node. - -#cluster_cert_key = # Filename of the cluster certificate key to - # use when establishing secure communication - # between control and data plane nodes. - # You can use the `kong hybrid` command to - # generate the certificate/key pair. - # Under `shared` mode, it must be the same - # for all nodes. Under `pki` mode it - # should be a different certificate for each - # DP node. - -#cluster_ca_cert = # The trusted CA certificate file in PEM - # format used to verify the `cluster_cert`. - # Required if `cluster_mtls` is set to `pki`, - # ignored otherwise. - -#------------------------------------------------------------------------------ -# HYBRID MODE DATA PLANE -#------------------------------------------------------------------------------ - -#cluster_server_name = # The server name used in the SNI of the TLS - # connection from a DP node to a CP node. - # Must match the Common Name (CN) or Subject - # Alternative Name (SAN) found in the CP - # certificate. - # If `cluster_mtls` is set to - # `shared`, this setting is ignored and - # `kong_clustering` is used. - -#cluster_control_plane = # To be used by data plane nodes only: - # address of the control plane node from - # which configuration updates will be fetched, - # in `host:port` format. - -#------------------------------------------------------------------------------ -# HYBRID MODE CONTROL PLANE -#------------------------------------------------------------------------------ - -cluster_listen = 0.0.0.0:8005 - # Comma-separated list of addresses and ports on - # which the cluster control plane server should listen - # for data plane connections. - # The cluster communication port of the control plane - # must be accessible by all the data planes - # within the same cluster. This port is mTLS protected - # to ensure end-to-end security and integrity. - # - # This setting has no effect if `role` is not set to - # `control_plane`. - # - # Connection made to this endpoint are logged - # to the same location as Admin API access logs. - # See `admin_access_log` config description for more - # information. - -cluster_data_plane_purge_delay = 1209600 - # How many seconds must pass from the time a DP node - # becomes offline to the time its entry gets removed - # from the database, as returned by the - # /clustering/data-planes Admin API endpoint. - # - # This is to prevent the cluster data plane table from - # growing indefinitely. The default is set to - # 14 days. That is, if CP haven't heard from a DP for - # 14 days, its entry will be removed. - -cluster_ocsp = off - # Whether to check for revocation status of DP - # certificates using OCSP (Online Certificate Status Protocol). - # If enabled, the DP certificate should contain the - # "Certificate Authority Information Access" extension - # and the OCSP method with URI of which the OCSP responder - # can be reached from CP. - # - # OCSP checks are only performed on CP nodes, it has no - # effect on DP nodes. - # - # Valid values to this setting are: - # - # - `on`: OCSP revocation check is enabled and DP - # must pass the check in order to establish - # connection with CP. - # - `off`: OCSP revocation check is disabled. - # - `optional`: OCSP revocation check will be attempted, - # however, if the required extension is not - # found inside DP provided certificate - # or communication with the OCSP responder - # failed, then DP is still allowed through. -#------------------------------------------------------------------------------ -# NGINX -#------------------------------------------------------------------------------ - -proxy_listen = 0.0.0.0:8000 reuseport backlog=16384, 0.0.0.0:8443 http2 ssl reuseport backlog=16384 - # Comma-separated list of addresses and ports on - # which the proxy server should listen for - # HTTP/HTTPS traffic. - # The proxy server is the public entry point of Kong, - # which proxies traffic from your consumers to your - # backend services. This value accepts IPv4, IPv6, and - # hostnames. - # - # Some suffixes can be specified for each pair: - # - # - `ssl` will require that all connections made - # through a particular address/port be made with TLS - # enabled. - # - `http2` will allow for clients to open HTTP/2 - # connections to Kong's proxy server. - # - `proxy_protocol` will enable usage of the - # PROXY protocol for a given address/port. - # - `deferred` instructs to use a deferred accept on - # Linux (the TCP_DEFER_ACCEPT socket option). - # - `bind` instructs to make a separate bind() call - # for a given address:port pair. - # - `reuseport` instructs to create an individual - # listening socket for each worker process - # allowing the Kernel to better distribute incoming - # connections between worker processes - # - `backlog=N` sets the maximum length for the queue - # of pending TCP connections. This number should - # not be too small in order to prevent clients - # seeing "Connection refused" error connecting to - # a busy Kong instance. - # **Note:** on Linux, this value is limited by the - # setting of `net.core.somaxconn` Kernel parameter. - # In order for the larger `backlog` set here to take - # effect it is necessary to raise - # `net.core.somaxconn` at the same time to match or - # exceed the `backlog` number set. - # - # This value can be set to `off`, thus disabling - # the HTTP/HTTPS proxy port for this node. - # If stream_listen is also set to `off`, this enables - # 'control-plane' mode for this node - # (in which all traffic proxying capabilities are - # disabled). This node can then be used only to - # configure a cluster of Kong - # nodes connected to the same datastore. - # - # Example: - # `proxy_listen = 0.0.0.0:443 ssl, 0.0.0.0:444 http2 ssl` - # - # See http://nginx.org/en/docs/http/ngx_http_core_module.html#listen - # for a description of the accepted formats for this - # and other `*_listen` values. - # - # See https://www.nginx.com/resources/admin-guide/proxy-protocol/ - # for more details about the `proxy_protocol` - # parameter. - # - # Not all `*_listen` values accept all formats - # specified in nginx's documentation. - -stream_listen = off - # Comma-separated list of addresses and ports on - # which the stream mode should listen. - # - # This value accepts IPv4, IPv6, and hostnames. - # Some suffixes can be specified for each pair: - # - `ssl` will require that all connections made - # through a particular address/port be made with TLS - # enabled. - # - `proxy_protocol` will enable usage of the - # PROXY protocol for a given address/port. - # - `bind` instructs to make a separate bind() call - # for a given address:port pair. - # - `reuseport` instructs to create an individual - # listening socket for each worker process - # allowing the Kernel to better distribute incoming - # connections between worker processes - # - `backlog=N` sets the maximum length for the queue - # of pending TCP connections. This number should - # not be too small in order to prevent clients - # seeing "Connection refused" error connecting to - # a busy Kong instance. - # **Note:** on Linux, this value is limited by the - # setting of `net.core.somaxconn` Kernel parameter. - # In order for the larger `backlog` set here to take - # effect it is necessary to raise - # `net.core.somaxconn` at the same time to match or - # exceed the `backlog` number set. - # - # Examples: - # - # ``` - # stream_listen = 127.0.0.1:7000 reuseport backlog=16384 - # stream_listen = 0.0.0.0:989 reuseport backlog=65536, 0.0.0.0:20 - # stream_listen = [::1]:1234 backlog=16384 - # ``` - # - # By default this value is set to `off`, thus - # disabling the stream proxy port for this node. - -# See http://nginx.org/en/docs/stream/ngx_stream_core_module.html#listen -# for a description of the formats that Kong might accept in stream_listen. - -admin_listen = 127.0.0.1:8001 reuseport backlog=16384, 127.0.0.1:8444 http2 ssl reuseport backlog=16384 - # Comma-separated list of addresses and ports on - # which the Admin interface should listen. - # The Admin interface is the API allowing you to - # configure and manage Kong. - # Access to this interface should be *restricted* - # to Kong administrators *only*. This value accepts - # IPv4, IPv6, and hostnames. - # - # Some suffixes can be specified for each pair: - # - # - `ssl` will require that all connections made - # through a particular address/port be made with TLS - # enabled. - # - `http2` will allow for clients to open HTTP/2 - # connections to Kong's proxy server. - # - `proxy_protocol` will enable usage of the - # PROXY protocol for a given address/port. - # - `deferred` instructs to use a deferred accept on - # Linux (the TCP_DEFER_ACCEPT socket option). - # - `bind` instructs to make a separate bind() call - # for a given address:port pair. - # - `reuseport` instructs to create an individual - # listening socket for each worker process - # allowing the Kernel to better distribute incoming - # connections between worker processes - # - `backlog=N` sets the maximum length for the queue - # of pending TCP connections. This number should - # not be too small in order to prevent clients - # seeing "Connection refused" error connecting to - # a busy Kong instance. - # **Note:** on Linux, this value is limited by the - # setting of `net.core.somaxconn` Kernel parameter. - # In order for the larger `backlog` set here to take - # effect it is necessary to raise - # `net.core.somaxconn` at the same time to match or - # exceed the `backlog` number set. - # - # This value can be set to `off`, thus disabling - # the Admin interface for this node, enabling a - # 'data-plane' mode (without configuration - # capabilities) pulling its configuration changes - # from the database. - # - # Example: `admin_listen = 127.0.0.1:8444 http2 ssl` - -status_listen = off # Comma-separated list of addresses and ports on - # which the Status API should listen. - # The Status API is a read-only endpoint - # allowing monitoring tools to retrieve metrics, - # healthiness, and other non-sensitive information - # of the current Kong node. - # - # The following suffix can be specified for each pair: - # - # - `ssl` will require that all connections made - # through a particular address/port be made with TLS - # enabled. - # - # This value can be set to `off`, disabling - # the Status API for this node. - # - # Example: `status_listen = 0.0.0.0:8100` - - -#nginx_user = # Defines user and group credentials used by - # worker processes. If group is omitted, a - # group whose name equals that of user is - # used. - # - # Example: `nginx_user = nginx www` - # - # **Note**: If the `kong` user and the `kong` - # group are not available, the default user - # and group credentials will be - # `nobody nobody`. - -nginx_worker_processes = auto # Determines the number of worker processes - # spawned by Nginx. - # - # See http://nginx.org/en/docs/ngx_core_module.html#worker_processes - # for detailed usage of the equivalent Nginx - # directive and a description of accepted - # values. - -nginx_daemon = off # Determines whether Nginx will run as a daemon - # or as a foreground process. Mainly useful - # for development or when running Kong inside - # a Docker environment. - # - # See http://nginx.org/en/docs/ngx_core_module.html#daemon. - -mem_cache_size = 128m # Size of each of the two in-memory caches - # for database entities. The accepted units are - # `k` and `m`, with a minimum recommended value of - # a few MBs. - # - # **Note**: As this option controls the size of two - # different cache entries, the total memory Kong - # uses to cache entities might be double this value. - -ssl_cipher_suite = intermediate # Defines the TLS ciphers served by Nginx. - # Accepted values are `modern`, - # `intermediate`, `old`, or `custom`. - # - # See https://wiki.mozilla.org/Security/Server_Side_TLS - # for detailed descriptions of each cipher - # suite. - -#ssl_ciphers = # Defines a custom list of TLS ciphers to be - # served by Nginx. This list must conform to - # the pattern defined by `openssl ciphers`. - # This value is ignored if `ssl_cipher_suite` - # is not `custom`. - -ssl_protocols = TLSv1.1 TLSv1.2 TLSv1.3 - # Enables the specified protocols for - # client-side connections. The set of - # supported protocol versions also depends - # on the version of OpenSSL Kong was built - # with. This value is ignored if - # `ssl_cipher_suite` is not `custom`. - # - # See http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_protocols - -ssl_prefer_server_ciphers = on # Specifies that server ciphers should be - # preferred over client ciphers when using - # the SSLv3 and TLS protocols. This value is - # ignored if `ssl_cipher_suite` is not `custom`. - # - # See http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_prefer_server_ciphers - -#ssl_dhparam = # Defines DH parameters for DHE ciphers from the - # predefined groups: `ffdhe2048`, `ffdhe3072`, - # `ffdhe4096`, `ffdhe6144`, `ffdhe8192`, or - # from the absolute path to a parameters file. - # - # This value is ignored if `ssl_cipher_suite` - # is `modern` or `intermediate`. The reason is - # that `modern` has no ciphers that needs this, - # and `intermediate` uses `ffdhe2048`. - # - # See http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_dhparam - -ssl_session_tickets = on # Enables or disables session resumption through - # TLS session tickets. This has no impact when - # used with TLSv1.3. - # - # Kong enables this by default for performance - # reasons, but it has security implications: - # https://github.com/mozilla/server-side-tls/issues/135 - # - # See http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_session_tickets - -ssl_session_timeout = 1d # Specifies a time during which a client may - # reuse the session parameters. See the rationale: - # https://github.com/mozilla/server-side-tls/issues/198 - # - # See http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_session_timeout - -#ssl_cert = # Comma-separated list of the absolute path to the certificates for - # `proxy_listen` values with TLS enabled. - # - # If more than one certificates are specified, it can be used to provide - # alternate type of certificate (for example, ECC certificate) that will be served - # to clients that supports them. Note to properly serve using ECC certificates, - # it is recommended to also set `ssl_cipher_suite` to - # `modern` or `intermediate`. - # - # Unless this option is explicitly set, Kong will auto-generate - # a pair of default certificates (RSA + ECC) first time it starts up and use - # it for serving TLS requests. - -#ssl_cert_key = # Comma-separated list of the absolute path to the keys for - # `proxy_listen` values with TLS enabled. - # - # If more than one certificate was specified for `ssl_cert`, then this - # option should contain the corresponding key for all certificates - # provided in the same order. - # - # Unless this option is explicitly set, Kong will auto-generate - # a pair of default private keys (RSA + ECC) first time it starts up and use - # it for serving TLS requests. - -client_ssl = off # Determines if Nginx should attempt to send client-side - # TLS certificates and perform Mutual TLS Authentication - # with upstream service when proxying requests. - -#client_ssl_cert = # If `client_ssl` is enabled, the absolute - # path to the client certificate for the `proxy_ssl_certificate` directive. - # - # This value can be overwritten dynamically with the `client_certificate` - # attribute of the `Service` object. - -#client_ssl_cert_key = # If `client_ssl` is enabled, the absolute - # path to the client TLS key for the `proxy_ssl_certificate_key` directive. - # - # This value can be overwritten dynamically with the `client_certificate` - # attribute of the `Service` object. - -#admin_ssl_cert = # Comma-separated list of the absolute path to the certificates for - # `admin_listen` values with TLS enabled. - # - # See docs for `ssl_cert` for detailed usage. - -#admin_ssl_cert_key = # Comma-separated list of the absolute path to the keys for - # `admin_listen` values with TLS enabled. - # - # See docs for `ssl_cert_key` for detailed usage. - -#status_ssl_cert = # Comma-separated list of the absolute path to the certificates for - # `status_listen` values with TLS enabled. - # - # See docs for `ssl_cert` for detailed usage. - -#status_ssl_cert_key = # Comma-separated list of the absolute path to the keys for - # `status_listen` values with TLS enabled. - # - # See docs for `ssl_cert_key` for detailed usage. - -headers = server_tokens, latency_tokens - # Comma-separated list of headers Kong should - # inject in client responses. - # - # Accepted values are: - # - `Server`: Injects `Server: kong/x.y.z` - # on Kong-produced response (e.g. Admin - # API, rejected requests from auth plugin). - # - `Via`: Injects `Via: kong/x.y.z` for - # successfully proxied requests. - # - `X-Kong-Proxy-Latency`: Time taken - # (in milliseconds) by Kong to process - # a request and run all plugins before - # proxying the request upstream. - # - `X-Kong-Response-Latency`: time taken - # (in millisecond) by Kong to produce - # a response in case of e.g. plugin - # short-circuiting the request, or in - # in case of an error. - # - `X-Kong-Upstream-Latency`: Time taken - # (in milliseconds) by the upstream - # service to send response headers. - # - `X-Kong-Admin-Latency`: Time taken - # (in milliseconds) by Kong to process - # an Admin API request. - # - `X-Kong-Upstream-Status`: The HTTP status - # code returned by the upstream service. - # This is particularly useful for clients to - # distinguish upstream statuses if the - # response is rewritten by a plugin. - # - `server_tokens`: Same as specifying both - # `Server` and `Via`. - # - `latency_tokens`: Same as specifying - # `X-Kong-Proxy-Latency`, - # `X-Kong-Response-Latency`, - # `X-Kong-Admin-Latency` and - # `X-Kong-Upstream-Latency` - # - # In addition to those, this value can be set - # to `off`, which prevents Kong from injecting - # any of the above headers. Note that this - # does not prevent plugins from injecting - # headers of their own. - # - # Example: `headers = via, latency_tokens` - -#trusted_ips = # Defines trusted IP addresses blocks that are - # known to send correct `X-Forwarded-*` - # headers. - # Requests from trusted IPs make Kong forward - # their `X-Forwarded-*` headers upstream. - # Non-trusted requests make Kong insert its - # own `X-Forwarded-*` headers. - # - # This property also sets the - # `set_real_ip_from` directive(s) in the Nginx - # configuration. It accepts the same type of - # values (CIDR blocks) but as a - # comma-separated list. - # - # To trust *all* /!\ IPs, set this value to - # `0.0.0.0/0,::/0`. - # - # If the special value `unix:` is specified, - # all UNIX-domain sockets will be trusted. - # - # See http://nginx.org/en/docs/http/ngx_http_realip_module.html#set_real_ip_from - # for examples of accepted values. -# GCP ips -trusted_ips = 130.211.0.0/22,35.191.0.0/16,34.120.148.61/32 - -# real_ip_header = X-Real-IP -real_ip_header = X-Forwarded-For # Defines the request header field whose value - # will be used to replace the client address. - # This value sets the `ngx_http_realip_module` - # directive of the same name in the Nginx - # configuration. - # - # If this value receives `proxy_protocol`: - # - # - at least one of the `proxy_listen` entries - # must have the `proxy_protocol` flag - # enabled. - # - the `proxy_protocol` parameter will be - # appended to the `listen` directive of the - # Nginx template. - # - # See http://nginx.org/en/docs/http/ngx_http_realip_module.html#real_ip_header - # for a description of this directive. - -real_ip_recursive = on # This value sets the `ngx_http_realip_module` - # directive of the same name in the Nginx - # configuration. - # - # See http://nginx.org/en/docs/http/ngx_http_realip_module.html#real_ip_recursive - # for a description of this directive. - -error_default_type = text/plain # Default MIME type to use when the request - # `Accept` header is missing and Nginx - # is returning an error for the request. - # Accepted values are `text/plain`, - # `text/html`, `application/json`, and - # `application/xml`. - -upstream_keepalive_pool_size = 60 # Sets the default size of the upstream - # keepalive connection pools. - # Upstream keepalive connection pools - # are segmented by the `dst ip/dst - # port/SNI` attributes of a connection. - # A value of `0` will disable upstream - # keepalive connections by default, forcing - # each upstream request to open a new - # connection. - -upstream_keepalive_max_requests = 100 # Sets the default maximum number of - # requests than can be proxied upstream - # through one keepalive connection. - # After the maximum number of requests - # is reached, the connection will be - # closed. - # A value of `0` will disable this - # behavior, and a keepalive connection - # can be used to proxy an indefinite - # number of requests. - -upstream_keepalive_idle_timeout = 60 # Sets the default timeout (in seconds) - # for which an upstream keepalive - # connection should be kept open. When - # the timeout is reached while the - # connection has not been reused, it - # will be closed. - # A value of `0` will disable this - # behavior, and an idle keepalive - # connection may be kept open - # indefinitely. - -#------------------------------------------------------------------------------ -# NGINX injected directives -#------------------------------------------------------------------------------ - -# Nginx directives can be dynamically injected in the runtime nginx.conf file -# without requiring a custom Nginx configuration template. -# -# All configuration properties respecting the naming scheme -# `nginx__` will result in `` being injected in -# the Nginx configuration block corresponding to the property's ``. -# Example: -# `nginx_proxy_large_client_header_buffers = 8 24k` -# -# Will inject the following directive in Kong's proxy `server {}` block: -# -# `large_client_header_buffers 8 24k;` -# -# The following namespaces are supported: -# -# - `nginx_main_`: Injects `` in Kong's configuration -# `main` context. -# - `nginx_events_`: Injects `` in Kong's `events {}` -# block. -# - `nginx_http_`: Injects `` in Kong's `http {}` block. -# - `nginx_proxy_`: Injects `` in Kong's proxy -# `server {}` block. -# - `nginx_upstream_`: Injects `` in Kong's proxy -# `upstream {}` block. -# - `nginx_admin_`: Injects `` in Kong's Admin API -# `server {}` block. -# - `nginx_status_`: Injects `` in Kong's Status API -# `server {}` block (only effective if `status_listen` is enabled). -# - `nginx_stream_`: Injects `` in Kong's stream module -# `stream {}` block (only effective if `stream_listen` is enabled). -# - `nginx_sproxy_`: Injects `` in Kong's stream module -# `server {}` block (only effective if `stream_listen` is enabled). -# - `nginx_supstream_`: Injects `` in Kong's stream -# module `upstream {}` block. -# -# As with other configuration properties, Nginx directives can be injected via -# environment variables when capitalized and prefixed with `KONG_`. -# Example: -# `KONG_NGINX_HTTP_SSL_PROTOCOLS` -> `nginx_http_ssl_protocols` -# -# Will inject the following directive in Kong's `http {}` block: -# -# `ssl_protocols ;` -# -# If different sets of protocols are desired between the proxy and Admin API -# server, you may specify `nginx_proxy_ssl_protocols` and/or -# `nginx_admin_ssl_protocols`, both of which taking precedence over the -# `http {}` block. - -nginx_main_worker_rlimit_nofile = auto - # Changes the limit on the maximum number of open files - # for worker processes. - # - # The special and default value of `auto` sets this - # value to `ulimit -n` with the upper bound limited to - # 16384 as a measure to protect against excess memory use. - # - # See http://nginx.org/en/docs/ngx_core_module.html#worker_rlimit_nofile - -nginx_events_worker_connections = auto - # Sets the maximum number of simultaneous - # connections that can be opened by a worker process. - # - # The special and default value of `auto` sets this - # value to `ulimit -n` with the upper bound limited to - # 16384 as a measure to protect against excess memory use. - # - # See http://nginx.org/en/docs/ngx_core_module.html#worker_connections - -nginx_http_client_header_buffer_size = 1k # Sets buffer size for reading the - # client request headers. - # See http://nginx.org/en/docs/http/ngx_http_core_module.html#client_header_buffer_size - -nginx_http_large_client_header_buffers = 4 8k # Sets the maximum number and - # size of buffers used for - # reading large clients - # requests headers. - # See http://nginx.org/en/docs/http/ngx_http_core_module.html#large_client_header_buffers - -nginx_http_client_max_body_size = 0 # Defines the maximum request body size - # allowed by requests proxied by Kong, - # specified in the Content-Length request - # header. If a request exceeds this - # limit, Kong will respond with a 413 - # (Request Entity Too Large). Setting - # this value to 0 disables checking the - # request body size. - # See http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size - -nginx_admin_client_max_body_size = 10m # Defines the maximum request body size for - # Admin API. - -nginx_http_client_body_buffer_size = 8k # Defines the buffer size for reading - # the request body. If the client - # request body is larger than this - # value, the body will be buffered to - # disk. Note that when the body is - # buffered to disk, Kong plugins that - # access or manipulate the request - # body may not work, so it is - # advisable to set this value as high - # as possible (e.g., set it as high - # as `client_max_body_size` to force - # request bodies to be kept in - # memory). Do note that - # high-concurrency environments will - # require significant memory - # allocations to process many - # concurrent large request bodies. - # See http://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_buffer_size - -nginx_admin_client_body_buffer_size = 10m # Defines the buffer size for reading - # the request body on Admin API. - -#------------------------------------------------------------------------------ -# DATASTORE -#------------------------------------------------------------------------------ - -# Kong can run with a database to store coordinated data between Kong nodes in -# a cluster, or without a database, where each node stores its information -# independently in memory. -# -# When using a database, Kong will store data for all its entities (such as -# Routes, Services, Consumers, and Plugins) in either Cassandra or PostgreSQL, -# and all Kong nodes belonging to the same cluster must connect themselves -# to the same database. -# -# Kong supports the following database versions: -# - **PostgreSQL**: 9.5 and above. -# - **Cassandra**: 2.2 and above. -# -# When not using a database, Kong is said to be in "DB-less mode": it will keep -# its entities in memory, and each node needs to have this data entered via a -# declarative configuration file, which can be specified through the -# `declarative_config` property, or via the Admin API using the `/config` -# endpoint. -# -# When using Postgres as the backend storage, you can optionally enable Kong -# to serve read queries from a separate database instance. -# When the number of proxies is large, this can greatly reduce the load -# on the main Postgres instance and achieve better scalability. It may also -# reduce the latency jitter if the Kong proxy node's latency to the main -# Postgres instance is high. -# -# The read-only Postgres instance only serves read queries and write -# queries still goes to the main connection. The read-only Postgres instance -# can be eventually consistent while replicating changes from the main -# instance. -# -# At least the `pg_ro_host` config is needed to enable this feature. -# By default, all other database config for the read-only connection are -# inherited from the corresponding main connection config described above but -# may be optionally overwritten explicitly using the `pg_ro_*` config below. - -database = postgres # Determines which of PostgreSQL or Cassandra - # this node will use as its datastore. - # Accepted values are `postgres`, - # `cassandra`, and `off`. - -pg_host = 127.0.0.1 # Host of the Postgres server. -pg_port = 5432 # Port of the Postgres server. -pg_timeout = 5000 # Defines the timeout (in ms), for connecting, - # reading and writing. - -pg_user = kong # Postgres user. -#pg_password = # Postgres user's password. -pg_database = kong # The database name to connect to. - -#pg_schema = # The database schema to use. If unspecified, - # Kong will respect the `search_path` value of - # your PostgreSQL instance. - -pg_ssl = off # Toggles client-server TLS connections - # between Kong and PostgreSQL. - # Because PostgreSQL uses the same port for TLS - # and non-TLS, this is only a hint. If the - # server does not support TLS, the established - # connection will be a plain one. - -pg_ssl_verify = off # Toggles server certificate verification if - # `pg_ssl` is enabled. - # See the `lua_ssl_trusted_certificate` - # setting to specify a certificate authority. - -pg_max_concurrent_queries = 0 # Sets the maximum number of concurrent queries - # that can be executing at any given time. This - # limit is enforced per worker process; the - # total number of concurrent queries for this - # node will be will be: - # `pg_max_concurrent_queries * nginx_worker_processes`. - # - # The default value of 0 removes this - # concurrency limitation. - -pg_semaphore_timeout = 60000 # Defines the timeout (in ms) after which - # PostgreSQL query semaphore resource - # acquisition attempts will fail. Such - # failures will generally result in the - # associated proxy or Admin API request - # failing with an HTTP 500 status code. - # Detailed discussion of this behavior is - # available in the online documentation. - -#pg_ro_host = # Same as `pg_host`, but for the - # read-only connection. - # **Note:** Refer to the documentation - # section above for detailed usage. - -#pg_ro_port = # Same as `pg_port`, but for the - # read-only connection. - -#pg_ro_timeout = # Same as `pg_timeout`, but for the - # read-only connection. - -#pg_ro_user = # Same as `pg_user`, but for the - # read-only connection. - -#pg_ro_password = # Same as `pg_password`, but for the - # read-only connection. - -#pg_ro_database = # Same as `pg_database`, but for the - # read-only connection. - -#pg_ro_schema = # Same as `pg_schema`, but for the - # read-only connection. - -#pg_ro_ssl = # Same as `pg_ssl`, but for the - # read-only connection. - -#pg_ro_ssl_verify = - # Same as `pg_ssl_verify`, but for the - # read-only connection. - -#pg_ro_max_concurrent_queries = - # Same as `pg_max_concurrent_queries`, but for - # the read-only connection. - # Note: read-only concurrency is not shared - # with the main (read-write) connection. - -#pg_ro_semaphore_timeout = - # Same as `pg_semaphore_timeout`, but for the - # read-only connection. - -cassandra_contact_points = 127.0.0.1 # A comma-separated list of contact - # points to your cluster. - # You may specify IP addresses or - # hostnames. Note that the port - # component of SRV records will be - # ignored in favor of `cassandra_port`. - # When connecting to a multi-DC cluster, - # ensure that contact points from the - # local datacenter are specified first - # in this list. - -cassandra_port = 9042 # The port on which your nodes are listening - # on. All your nodes and contact points must - # listen on the same port. Will be created if - # it doesn't exist. - -cassandra_keyspace = kong # The keyspace to use in your cluster. - -cassandra_write_consistency = ONE # Consistency setting to use when - # writing to the Cassandra cluster. - -cassandra_read_consistency = ONE # Consistency setting to use when - # reading from the Cassandra cluster. - -cassandra_timeout = 5000 # Defines the timeout (in ms) for reading - # and writing. - -cassandra_ssl = off # Toggles client-to-node TLS connections - # between Kong and Cassandra. - -cassandra_ssl_verify = off # Toggles server certificate verification if - # `cassandra_ssl` is enabled. - # See the `lua_ssl_trusted_certificate` - # setting to specify a certificate authority. - -cassandra_username = kong # Username when using the - # `PasswordAuthenticator` scheme. - -#cassandra_password = # Password when using the - # `PasswordAuthenticator` scheme. - -cassandra_lb_policy = RequestRoundRobin # Load balancing policy to use when - # distributing queries across your - # Cassandra cluster. - # Accepted values are: - # `RoundRobin`, `RequestRoundRobin`, - # `DCAwareRoundRobin`, and - # `RequestDCAwareRoundRobin`. - # Policies prefixed with "Request" - # make efficient use of established - # connections throughout the same - # request. - # Prefer "DCAware" policies if and - # only if you are using a - # multi-datacenter cluster. - -#cassandra_local_datacenter = # When using the `DCAwareRoundRobin` - # or `RequestDCAwareRoundRobin` load - # balancing policy, you must specify the name - # of the local (closest) datacenter for this - # Kong node. - -cassandra_refresh_frequency = 60 # Frequency (in seconds) at which - # the cluster topology will be - # checked for new or decommissioned - # nodes. - # A value of `0` will disable this - # check, and the cluster topology - # will never be refreshed. - -cassandra_repl_strategy = SimpleStrategy # When migrating for the first time, - # Kong will use this setting to - # create your keyspace. - # Accepted values are - # `SimpleStrategy` and - # `NetworkTopologyStrategy`. - -cassandra_repl_factor = 1 # When migrating for the first time, Kong - # will create the keyspace with this - # replication factor when using the - # `SimpleStrategy`. - -cassandra_data_centers = dc1:2,dc2:3 # When migrating for the first time, - # will use this setting when using the - # `NetworkTopologyStrategy`. - # The format is a comma-separated list - # made of `:`. - -cassandra_schema_consensus_timeout = 10000 # Defines the timeout (in ms) for - # the waiting period to reach a - # schema consensus between your - # Cassandra nodes. - # This value is only used during - # migrations. - -#declarative_config = # The path to the declarative configuration - # file which holds the specification of all - # entities (Routes, Services, Consumers, etc.) - # to be used when the `database` is set to - # `off`. - # - # Entities are stored in Kong's in-memory cache, - # so you must ensure that enough memory is - # allocated to it via the `mem_cache_size` - # property. You must also ensure that items - # in the cache never expire, which means that - # `db_cache_ttl` should preserve its default - # value of 0. - # - # If the Hybrid mode `role` is set to `data_plane` - # and there's no configuration cache file, - # this configuration is used before connecting - # to the Control Plane node as a user-controlled - # fallback. - -#------------------------------------------------------------------------------ -# DATASTORE CACHE -#------------------------------------------------------------------------------ - -# In order to avoid unnecessary communication with the datastore, Kong caches -# entities (such as APIs, Consumers, Credentials...) for a configurable period -# of time. It also handles invalidations if such an entity is updated. -# -# This section allows for configuring the behavior of Kong regarding the -# caching of such configuration entities. - -db_update_frequency = 5 # Frequency (in seconds) at which to check for - # updated entities with the datastore. - # - # When a node creates, updates, or deletes an - # entity via the Admin API, other nodes need - # to wait for the next poll (configured by - # this value) to eventually purge the old - # cached entity and start using the new one. - -db_update_propagation = 0 # Time (in seconds) taken for an entity in the - # datastore to be propagated to replica nodes - # of another datacenter. - # - # When in a distributed environment such as - # a multi-datacenter Cassandra cluster, this - # value should be the maximum number of - # seconds taken by Cassandra to propagate a - # row to other datacenters. - # - # When set, this property will increase the - # time taken by Kong to propagate the change - # of an entity. - # - # Single-datacenter setups or PostgreSQL - # servers should suffer no such delays, and - # this value can be safely set to 0. - -db_cache_ttl = 0 # Time-to-live (in seconds) of an entity from - # the datastore when cached by this node. - # - # Database misses (no entity) are also cached - # according to this setting if you do not - # configure `db_cache_neg_ttl`. - # - # If set to 0 (default), such cached entities - # or misses never expire. - -#db_cache_neg_ttl = # Time-to-live (in seconds) of a datastore - # miss (no entity). - # - # If not specified (default), `db_cache_ttl` - # value will be used instead. - # - # If set to 0, misses will never expire. - -db_resurrect_ttl = 30 # Time (in seconds) for which stale entities - # from the datastore should be resurrected for - # when they cannot be refreshed (e.g., the - # datastore is unreachable). When this TTL - # expires, a new attempt to refresh the stale - # entities will be made. - -db_cache_warmup_entities = services - # Entities to be pre-loaded from the datastore - # into the in-memory cache at Kong start-up. - # This speeds up the first access of endpoints - # that use the given entities. - # - # When the `services` entity is configured - # for warmup, the DNS entries for values in - # its `host` attribute are pre-resolved - # asynchronously as well. - # - # Cache size set in `mem_cache_size` should - # be set to a value large enough to hold all - # instances of the specified entities. - # If the size is insufficient, Kong will log - # a warning. - -#------------------------------------------------------------------------------ -# DNS RESOLVER -#------------------------------------------------------------------------------ - -# By default, the DNS resolver will use the standard configuration files -# `/etc/hosts` and `/etc/resolv.conf`. The settings in the latter file will be -# overridden by the environment variables `LOCALDOMAIN` and `RES_OPTIONS` if -# they have been set. -# -# Kong will resolve hostnames as either `SRV` or `A` records (in that order, and -# `CNAME` records will be dereferenced in the process). -# In case a name was resolved as an `SRV` record it will also override any given -# port number by the `port` field contents received from the DNS server. -# -# The DNS options `SEARCH` and `NDOTS` (from the `/etc/resolv.conf` file) will -# be used to expand short names to fully qualified ones. So it will first try -# the entire `SEARCH` list for the `SRV` type, if that fails it will try the -# `SEARCH` list for `A`, etc. -# -# For the duration of the `ttl`, the internal DNS resolver will loadbalance each -# request it gets over the entries in the DNS record. For `SRV` records the -# `weight` fields will be honored, but it will only use the lowest `priority` -# field entries in the record. - -#dns_resolver = # Comma separated list of nameservers, each - # entry in `ip[:port]` format to be used by - # Kong. If not specified the nameservers in - # the local `resolv.conf` file will be used. - # Port defaults to 53 if omitted. Accepts - # both IPv4 and IPv6 addresses. - -dns_hostsfile = /etc/hosts # The hosts file to use. This file is read - # once and its content is static in memory. - # To read the file again after modifying it, - # Kong must be reloaded. - -dns_order = LAST,SRV,A,CNAME # The order in which to resolve different - # record types. The `LAST` type means the - # type of the last successful lookup (for the - # specified name). The format is a (case - # insensitive) comma separated list. - -#dns_valid_ttl = # By default, DNS records are cached using - # the TTL value of a response. If this - # property receives a value (in seconds), it - # will override the TTL for all records. - -dns_stale_ttl = 4 # Defines, in seconds, how long a record will - # remain in cache past its TTL. This value - # will be used while the new DNS record is - # fetched in the background. - # Stale data will be used from expiry of a - # record until either the refresh query - # completes, or the `dns_stale_ttl` number of - # seconds have passed. - -dns_not_found_ttl = 30 # TTL in seconds for empty DNS responses and - # "(3) name error" responses. - -dns_error_ttl = 1 # TTL in seconds for error responses. - -dns_no_sync = off # If enabled, then upon a cache-miss every - # request will trigger its own dns query. - # When disabled multiple requests for the - # same name/type will be synchronised to a - # single query. - -#------------------------------------------------------------------------------ -# TUNING & BEHAVIOR -#------------------------------------------------------------------------------ - -worker_consistency = strict - # Defines whether this node should rebuild its - # state synchronously or asynchronously (the - # balancers and the router are rebuilt on - # updates that affects them, e.g., updates to - # Routes, Services or Upstreams, via the Admin - # API or loading a declarative configuration - # file). - # - # Accepted values are: - # - # - `strict`: the router will be rebuilt - # synchronously, causing incoming requests to - # be delayed until the rebuild is finished. - # - `eventual`: the router will be rebuilt - # asynchronously via a recurring background - # job running every second inside of each - # worker. - # - # Note that `strict` ensures that all workers - # of a given node will always proxy requests - # with an identical router, but that increased - # long tail latency can be observed if - # frequent Routes and Services updates are - # expected. - # Using `eventual` will help preventing long - # tail latency issues in such cases, but may - # cause workers to route requests differently - # for a short period of time after Routes and - # Services updates. - -worker_state_update_frequency = 5 - # Defines how often the worker state changes are - # checked with a background job. When a change - # is detected, a new router or balancer will be - # built, as needed. Raising this value will - # decrease the load on database servers and - # result in less jitter in proxy latency, but - # it might take more time to propagate changes - # to each individual worker. - -#------------------------------------------------------------------------------ -# MISCELLANEOUS -#------------------------------------------------------------------------------ - -# Additional settings inherited from lua-nginx-module allowing for more -# flexibility and advanced usage. -# -# See the lua-nginx-module documentation for more information: -# https://github.com/openresty/lua-nginx-module - - -#lua_ssl_trusted_certificate = # Comma-separated list of paths to certificate - # authority files for Lua cosockets in PEM format. - # - # The special value `system` attempts to search for the - # "usual default" provided by each distro, according - # to an arbitrary heuristic. In the current implementation, - # The following pathnames will be tested in order, - # and the first one found will be used: - # - # - /etc/ssl/certs/ca-certificates.crt (Debian/Ubuntu/Gentoo) - # - /etc/pki/tls/certs/ca-bundle.crt (Fedora/RHEL 6) - # - /etc/ssl/ca-bundle.pem (OpenSUSE) - # - /etc/pki/tls/cacert.pem (OpenELEC) - # - /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem (CentOS/RHEL 7) - # - /etc/ssl/cert.pem (OpenBSD, Alpine) - # - # If no file is found on any of these paths, an error will - # be raised. - # - # `system` can be used by itself or in conjunction with other - # CA filepaths. - # - # When `pg_ssl_verify` or `cassandra_ssl_verify` - # are enabled, these certificate authority files will be - # used for verifying Kong's database connections. - # - # See https://github.com/openresty/lua-nginx-module#lua_ssl_trusted_certificate - -lua_ssl_verify_depth = 1 # Sets the verification depth in the server - # certificates chain used by Lua cosockets, - # set by `lua_ssl_trusted_certificate`. - # This includes the certificates configured - # for Kong's database connections. - # If the maximum depth is reached before - # reaching the end of the chain, verification - # will fail. This helps mitigate certificate - # based DoS attacks. - # - # See https://github.com/openresty/lua-nginx-module#lua_ssl_verify_depth - -lua_ssl_protocols = TLSv1.1 TLSv1.2 TLSv1.3 # Defines the TLS versions supported - # when handshaking with OpenResty's - # TCP cosocket APIs. - # - # This affects connections made by Lua - # code, such as connections to the - # database Kong uses, or when sending logs - # using a logging plugin. It does *not* - # affect connections made to the upstream - # Service or from downstream clients. - -#lua_package_path = # Sets the Lua module search path - # (LUA_PATH). Useful when developing - # or using custom plugins not stored - # in the default search path. - # - # See https://github.com/openresty/lua-nginx-module#lua_package_path - -#lua_package_cpath = # Sets the Lua C module search path - # (LUA_CPATH). - # - # See https://github.com/openresty/lua-nginx-module#lua_package_cpath - -lua_socket_pool_size = 30 # Specifies the size limit for every cosocket - # connection pool associated with every remote - # server. - # - # See https://github.com/openresty/lua-nginx-module#lua_socket_pool_size - -untrusted_lua = sandbox - # Controls loading of Lua functions from admin-supplied - # sources such as the Admin API. LuaJIT bytecode - # loading is always disabled. - # - # **Warning:** LuaJIT is not designed as a secure - # runtime for running malicious code, therefore - # you should properly protect your Admin API endpoint - # even with sandboxing enabled. The sandbox only - # provides protection against trivial attackers or - # unintentional modification of the Kong global - # environment. - # - # Accepted values are: `off`, `sandbox`, or - # `on`: - # - # * `off`: Disallow loading of any arbitrary - # Lua functions. The `off` option - # disables any functionality that runs - # arbitrary Lua code, including the - # Serverless Functions plugins and any - # transformation plugin that allows - # custom Lua functions. - # - # * `sandbox`: Allow loading of Lua functions, - # but use a sandbox when executing - # them. The sandboxed function has - # restricted access to the global - # environment and only has access - # to standard Lua functions that - # will generally not cause harm to - # the Kong Gateway node. - # - # * `on`: Functions have unrestricted - # access to the global environment and - # can load any Lua modules. This is - # similar to the behavior in - # Kong Gateway prior to 2.3.0. - # - # The default `sandbox` environment does not - # allow importing other modules or libraries, - # or executing anything at the OS level (for - # example, file read/write). The global - # environment is also not accessible. - # - # Examples of `untrusted_lua = sandbox` - # behavior: - # - # * You can't access or change global values - # such as `kong.configuration.pg_password` - # * You can run harmless lua: - # `local foo = 1 + 1`. However, OS level - # functions are not allowed, like: - # `os.execute('rm -rf /*')`. - # - # For a full allowed/disallowed list, see: - # https://github.com/kikito/sandbox.lua/blob/master/sandbox.lua - # - # To customize the sandbox environment, use - # the `untrusted_lua_sandbox_requires` and - # `untrusted_lua_sandbox_environment` - # parameters below. - -#untrusted_lua_sandbox_requires = # Comma-separated list of modules allowed to - # be loaded with `require` inside the - # sandboxed environment. Ignored - # if `untrusted_lua` is not `sandbox`. - # - # For example, say you have configured the - # Serverless pre-function plugin and it - # contains the following `requires`: - # - # ``` - # local template = require "resty.template" - # local split = require "kong.tools.utils".split - # ``` - # - # To run the plugin, add the modules to the - # allowed list: - # ``` - # untrusted_lua_sandbox_requires = resty.template, kong.tools.utils - # ``` - # - # **Warning:** Allowing certain modules may - # create opportunities to escape the - # sandbox. For example, allowing `os` or - # `luaposix` may be unsafe. - -#untrusted_lua_sandbox_environment = # Comma-separated list of global Lua - # variables that should be made available - # inside the sandboxed environment. Ignored - # if `untrusted_lua` is not `sandbox`. - # - # **Warning**: Certain variables, when made - # available, may create opportunities to - # escape the sandbox. diff --git a/packages/helm-charts/kong/templates/NOTES.txt b/packages/helm-charts/kong/templates/NOTES.txt deleted file mode 100644 index 9542a8b9540..00000000000 --- a/packages/helm-charts/kong/templates/NOTES.txt +++ /dev/null @@ -1,95 +0,0 @@ -** Please be patient while the chart is being deployed ** - -{{- if .Values.ingress.enabled }} - Kong URL(s): -{{- if .Values.ingress.hostname }} - - http://{{ .Values.ingress.hostname }} -{{- end }} -{{- range $host := .Values.ingress.hosts }} - {{- range .paths }} - - http://{{ $host.name }}{{ . }} - {{- end }} -{{- end }} -{{- else if contains "NodePort" .Values.service.type }} - - Get the Kubernetes node IP by using the following command - export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") - - Access the Kong proxy by using the following commands - - export PROXY_NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "common.names.fullname" . }}) - echo http://$NODE_IP:$PROXY_NODE_PORT - - {{- if .Values.service.exposeAdmin }} - - Access the Kong admin by using the following commands - - export ADMIN_NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[2].nodePort}" services {{ include "common.names.fullname" . }}) - echo http://$NODE_IP:$ADMIN_NODE_PORT - - {{- end }} -{{- else if contains "LoadBalancer" .Values.service.type }} - - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "common.names.fullname" . }}' - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "common.names.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') - echo http://$SERVICE_IP:{{ .Values.service.proxyHttpPort }} -{{- else if contains "ClusterIP" .Values.service.type }} - - Access the Kong proxy by using the following commands - - echo "Browse to http://127.0.0.1:8000" - kubectl port-forward svc/{{ include "common.names.fullname" . }} 8080:{{ .Values.service.proxyHttpPort }} & - - Access the Kong admin by using the following commands - - echo "Browse to http://127.0.0.1:8001" - {{- if .Values.service.exposeAdmin }} - kubectl port-forward svc/{{ include "common.names.fullname" . }} 8001:{{ .Values.service.adminHttpPort }} & - {{- else }} - export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name:{{ include "common.names.name" . }},app.kubernetes.io/instance:{{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") - - kubectl port-forward pod/$POD_NAME 8001:8001 & - {{- end }} -{{- end }} - -{{- if .Values.ingressController.enabled }} - - The Kong Ingress Controller was deployed as part of the Kong pods. The following objects are available in the Kubernetes API: - - kubectl get kongconsumers - kubectl get kongcredentials - kubectl get kongingresses - kubectl get kongplugins - -{{- end }} - -{{- include "common.warnings.rollingTag" .Values.image }} -{{- if .Values.ingressController.enabled }} -{{- include "common.warnings.rollingTag" .Values.ingressController.image }} -{{- end }} - -{{- $passwordValidationErrors := list }} - -If you want to upgrade the installation you will need to re-set the database credentials. Execute the following command -{{- if eq .Values.database "postgresql" }} -{{- $dbSecretName := include "kong.postgresql.secretName" . -}} -{{- $dbPasswordValidationErrors := include "common.validations.values.postgresql.passwords" (dict "secret" $dbSecretName "subchart" true "context" $) -}} -{{- $passwordValidationErrors = append $passwordValidationErrors $dbPasswordValidationErrors -}} - - kubectl get secret --namespace {{ .Release.Namespace }} {{ include "kong.postgresql.secretName" . }} -o jsonpath="{.data.postgresql-password}" | base64 --decode -{{- else }} - {{- $dbSecretName := include "kong.cassandra.secretName" . -}} - {{- $dbPasswordValidationErrors := include "common.validations.values.cassandra.passwords" (dict "secret" $dbSecretName "subchart" true "context" $) -}} - {{- $passwordValidationErrors = append $passwordValidationErrors $dbPasswordValidationErrors -}} - - kubectl get secret --namespace {{ .Release.Namespace }} {{ include "kong.cassandra.secretName" . }} -o jsonpath="{.data.cassandra-password}" | base64 --decode -{{- end }} - -{{- if .Values.service.exposeAdmin }} - -WARNING: You made the Kong admin {{ if contains "ClusterIP" .Values.service.type }}accessible from other pods in the cluster{{ else }}externally accessible{{- end }}. We do not recommend this configuration in production. For accessing the admin, using pod port-forwarding or using the Kong Ingress Controller is preferred. -{{- end }} - -{{ include "kong.validateValues" . }} -{{- include "common.errors.upgrade.passwords.empty" (dict "validationErrors" $passwordValidationErrors "context" $) -}} diff --git a/packages/helm-charts/kong/templates/_helpers.tpl b/packages/helm-charts/kong/templates/_helpers.tpl deleted file mode 100644 index 67b5ea3a982..00000000000 --- a/packages/helm-charts/kong/templates/_helpers.tpl +++ /dev/null @@ -1,217 +0,0 @@ -{{/* -Return the proper kong image name -*/}} -{{- define "kong.image" -}} -{{ include "common.images.image" (dict "imageRoot" .Values.image "global" .Values.global) }} -{{- end -}} - -{{/* -Return the proper kong image name -*/}} -{{- define "kong.ingress-controller.image" -}} -{{ include "common.images.image" (dict "imageRoot" .Values.ingressController.image "global" .Values.global) }} -{{- end -}} - -{{/* -Return the proper kong migration image name -*/}} -{{- define "kong.migration.image" -}} -{{- if .Values.migration.image -}} -{{ include "common.images.image" (dict "imageRoot" .Values.migration.image "global" .Values.global) }} -{{- else -}} -{{- template "kong.image" . -}} -{{- end -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -*/}} -{{- define "kong.postgresql.fullname" -}} -{{- printf "%s-%s" .Release.Name "postgresql" | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -*/}} -{{- define "kong.cassandra.fullname" -}} -{{- printf "%s-%s" .Release.Name "cassandra" | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Get Cassandra port -*/}} -{{- define "kong.cassandra.port" -}} -{{- if .Values.cassandra.enabled -}} -{{- .Values.cassandra.service.port -}} -{{- else -}} -{{- .Values.cassandra.external.port -}} -{{- end -}} -{{- end -}} - -{{/* -Get Cassandra contact points -*/}} -{{- define "kong.cassandra.contactPoints" -}} -{{- $global := . -}} -{{- if .Values.cassandra.enabled -}} - {{- $replicas := int .Values.cassandra.cluster.replicaCount -}} - {{- $domain := .Values.clusterDomain -}} - {{- range $i, $e := until $replicas }} - {{- include "kong.cassandra.fullname" $global }}-{{ $i }}.{{ include "kong.cassandra.fullname" $global }}-headless.{{ $global.Release.Namespace }}.svc.{{ $domain }} - {{- if (lt ( add1 $i ) $replicas ) -}} - , - {{- end -}} - {{- end -}} -{{- else -}} - {{- $replicas := len .Values.cassandra.external.hosts -}} - {{- range $i, $e := until $replicas }} - {{- index $global.Values.cassandra.external.hosts $i -}} - {{- if (lt ( add1 $i ) $replicas ) -}} - , - {{- end -}} - {{- end -}} -{{- end -}} -{{- end -}} - -{{/* -Get PostgreSQL host -*/}} -{{- define "kong.postgresql.host" -}} -{{- if .Values.postgresql.enabled -}} - {{- template "kong.postgresql.fullname" . -}} -{{- else -}} - {{ .Values.postgresql.external.host }} -{{- end -}} -{{- end -}} - -{{/* -Get PostgreSQL user -*/}} -{{- define "kong.postgresql.user" -}} -{{- if .Values.postgresql.enabled -}} - {{- .Values.postgresql.postgresqlUsername -}} -{{- else -}} - {{ .Values.postgresql.external.user }} -{{- end -}} -{{- end -}} - -{{/* -Get Cassandra user -*/}} -{{- define "kong.cassandra.user" -}} -{{- if .Values.postgresql.enabled -}} - {{- .Values.cassandra.dbUser.user -}} -{{- else -}} - {{ .Values.cassandra.external.user }} -{{- end -}} -{{- end -}} - -{{/* -Get Cassandra secret -*/}} -{{- define "kong.cassandra.secretName" -}} -{{- if .Values.cassandra.existingSecret -}} - {{- .Values.cassandra.existingSecret -}} -{{- else if .Values.cassandra.enabled }} - {{- template "kong.cassandra.fullname" . -}} -{{- else -}} - {{- printf "%s-external-secret" ( include "common.names.fullname" . ) -}} -{{- end -}} -{{- end -}} - -{{/* -Get PostgreSQL secret -*/}} -{{- define "kong.postgresql.secretName" -}} -{{- if .Values.postgresql.existingSecret -}} - {{- .Values.postgresql.existingSecret -}} -{{- else if .Values.postgresql.enabled }} - {{- template "kong.postgresql.fullname" . -}} -{{- else -}} - {{- printf "%s-external-secret" ( include "common.names.fullname" . ) -}} -{{- end -}} -{{- end -}} - -{{/* -Return the proper Docker Image Registry Secret Names -*/}} -{{- define "kong.imagePullSecrets" -}} -{{ include "common.images.pullSecrets" (dict "images" (list .Values.image .Values.ingressController.image) "global" .Values.global) }} -{{- end -}} - -{{/* -Return true if a secret for a external database should be created -*/}} -{{- define "kong.createExternalDBSecret" -}} -{{- if and (not .Values.postgresql.enabled) (not .Values.cassandra.enabled) (not .Values.cassandra.existingSecret) (not .Values.postgresql.existingSecret) -}} - {{- true -}} -{{- end -}} -{{- end -}} - -{{/* -Get proper service account -*/}} -{{- define "kong.serviceAccount" -}} -{{- if .Values.ingressController.rbac.existingServiceAccount -}} -{{ .Values.ingressController.rbac.existingServiceAccount }} -{{- else -}} -{{- include "common.names.fullname" . -}} -{{- end -}} -{{- end -}} - -{{/* -Validate values for kong. -*/}} -{{- define "kong.validateValues" -}} -{{- $messages := list -}} -{{- $messages := append $messages (include "kong.validateValues.database" .) -}} -{{- $messages := append $messages (include "kong.validateValues.rbac" .) -}} -{{- $messages := without $messages "" -}} -{{- $message := join "\n" $messages -}} - -{{- if $message -}} -{{- printf "\nVALUES VALIDATION:\n%s" $message -}} -{{- end -}} -{{- end -}} - -{{/* -Function to validate the RBAC -*/}} -{{- define "kong.validateValues.rbac" -}} -{{- if and .Values.ingressController.enabled (not .Values.ingressController.rbac.existingServiceAccount) (not .Values.ingressController.rbac.create) -}} -INVALID RBAC: You enabled the Kong Ingress Controller sidecar without creating RBAC objects and not -specifying an existing Service Account. Specify an existing Service Account in ingressController.rbac.existingServiceAccount -or allow the chart to create the proper RBAC objects with ingressController.rbac.create -{{- end -}} -{{- end -}} -{{/* -Function to validate the external database -*/}} -{{- define "kong.validateValues.database" -}} - -{{- if and (not (eq .Values.database "postgresql")) (not (eq .Values.database "cassandra")) -}} -INVALID DATABASE: The value "{{ .Values.database }}" is not allowed for the "database" value. It -must be one either "postgresql" or "cassandra". -{{- end }} - -{{- if and (eq .Values.database "postgresql") (not .Values.postgresql.enabled) (not .Values.postgresql.external.host) -}} -NO DATABASE: You disabled the Cassandra sub-chart but did not specify external Cassandra hosts. Either deploy the PostgreSQL sub-chart by setting cassandra.enabled=true or set a value for cassandra.external.hosts. -{{- end }} - -{{- if and (eq .Values.database "postgresql") (not .Values.postgresql.enabled) (not .Values.postgresql.external.host) -}} -NO DATABASE: You disabled the PostgreSQL sub-chart but did not specify an external PostgreSQL host. Either deploy the PostgreSQL sub-chart by setting postgresql.enabled=true or set a value for postgresql.external.host. -{{- end }} - - -{{- if and (eq .Values.database "postgresql") .Values.postgresql.enabled .Values.postgresql.external.host -}} -CONFLICT: You specified to deploy the PostgreSQL sub-chart and also specified an external -PostgreSQL instance. Only one of postgresql.enabled (deploy sub-chart) and postgresql.external.host can be set -{{- end }} - -{{- if and (eq .Values.database "cassandra") .Values.cassandra.enabled .Values.cassandra.external.hosts -}} -CONFLICT: You specified to deploy the Cassandra sub-chart and also specified external -Cassandra hosts. Only one of cassandra.enabled (deploy sub-chart) and cassandra.external.hosts can be set -{{- end }} -{{- end -}} diff --git a/packages/helm-charts/kong/templates/dep-ds.yaml b/packages/helm-charts/kong/templates/dep-ds.yaml deleted file mode 100644 index c24a7385769..00000000000 --- a/packages/helm-charts/kong/templates/dep-ds.yaml +++ /dev/null @@ -1,359 +0,0 @@ -apiVersion: {{ include "common.capabilities.deployment.apiVersion" . }} -{{- if .Values.useDaemonset }} -kind: DaemonSet -{{- else }} -kind: Deployment -{{- end }} -metadata: - name: {{ include "common.names.fullname" . }} - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: server - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -spec: - {{- if not .Values.useDaemonset }} - replicas: {{ .Values.replicaCount }} - {{- end }} - selector: - matchLabels: {{- include "common.labels.matchLabels" . | nindent 6 }} - app.kubernetes.io/component: server - {{- if .Values.updateStrategy }} - {{- if .Values.useDaemonset }} - updateStrategy: {{- toYaml .Values.updateStrategy | nindent 4 }} - {{- else }} - strategy: {{- toYaml .Values.updateStrategy | nindent 4 }} - {{- end }} - {{- end }} - template: - metadata: - labels: {{- include "common.labels.standard" . | nindent 8 }} - app.kubernetes.io/component: server - {{- if .Values.podLabels }} - {{- include "common.tplvalues.render" (dict "value" .Values.podLabels "context" $) | nindent 8 }} - {{- end }} - annotations: - {{- if (include "kong.createExternalDBSecret" .) }} - checksum/secret: {{ include (print $.Template.BasePath "/external-database-secret.yaml") . | sha256sum }} - {{- end }} - checksum/configmap-kong: {{ include (print $.Template.BasePath "/kong-script-configmap.yaml") . | sha256sum }} - {{- if .Values.metrics.enabled }} - checksum/configmap-metrics-plugin: {{ include (print $.Template.BasePath "/metrics-script-configmap.yaml") . | sha256sum }} - {{- end }} - {{- if .Values.podAnnotations }} - {{- include "common.tplvalues.render" (dict "value" .Values.podAnnotations "context" $) | nindent 8 }} - {{- end }} - spec: - {{- if .Values.hostAliases }} - hostAliases: {{- include "common.tplvalues.render" (dict "value" .Values.hostAliases "context" $) | nindent 8 }} - {{- end }} - {{- if .Values.ingressController.enabled }} - serviceAccountName: {{ include "kong.serviceAccount" . }} - {{- end }} - {{- if .Values.podSecurityContext }} - securityContext: {{- include "common.tplvalues.render" (dict "value" .Values.podSecurityContext "context" $) | nindent 8 }} - {{- end }} - {{- include "kong.imagePullSecrets" . | nindent 6 }} - {{- if .Values.schedulerName }} - schedulerName: {{ .Values.schedulerName | quote }} - {{- end }} - {{- if .Values.affinity }} - affinity: {{- include "common.tplvalues.render" (dict "value" .Values.affinity "context" $) | nindent 8 }} - {{- else }} - affinity: - podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.podAffinityPreset "component" "server" "context" $) | nindent 10 }} - podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.podAntiAffinityPreset "component" "server" "context" $) | nindent 10 }} - nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.nodeAffinityPreset.type "key" .Values.nodeAffinityPreset.key "values" .Values.nodeAffinityPreset.values) | nindent 10 }} - {{- end }} - {{- if .Values.nodeSelector }} - nodeSelector: {{- include "common.tplvalues.render" (dict "value" .Values.nodeSelector "context" $) | nindent 8 }} - {{- end }} - {{- if .Values.tolerations }} - tolerations: {{- include "common.tplvalues.render" (dict "value" .Values.tolerations "context" $) | nindent 8 }} - {{- end }} - {{- if .Values.initContainers }} - initContainers: {{- include "common.tplvalues.render" (dict "value" .Values.initContainers "context" $) | nindent 8 }} - {{- end }} - containers: - - name: kong - image: {{ template "kong.image" . }} - imagePullPolicy: {{ .Values.image.pullPolicy }} - {{- if .Values.containerSecurityContext }} - securityContext: {{- include "common.tplvalues.render" (dict "value" .Values.containerSecurityContext "context" $) | nindent 12 }} - {{- end }} - {{- if .Values.kong.command }} - command: {{- include "common.tplvalues.render" (dict "value" .Values.kong.command "context" $) | nindent 12 }} - {{- end }} - {{- if .Values.kong.args }} - args: {{- include "common.tplvalues.render" (dict "value" .Values.kong.args "context" $) | nindent 12 }} - {{- end }} - {{- if not .Values.kong.lifecycleHooks }} - lifecycle: - preStop: - exec: - command: - - /bin/sh - - -c - - kong quit - {{ else }} - lifecycle: {{- include "common.tplvalues.render" (dict "value" .Values.kong.lifecycleHooks "context" $) | nindent 12 }} - {{- end }} - env: - {{- if .Values.service.exposeAdmin }} - - name: KONG_ADMIN_LISTEN_ADDRESS - value: "0.0.0.0" - {{- end }} - {{- if (eq .Values.database "postgresql") }} - - name: KONG_DATABASE - value: "postgres" - {{- if .Values.postgresql.usePasswordFile }} - - name: KONG_POSTGRESQL_PASSWORD_FILE - value: "/bitnami/kong/secrets/postgresql-password" - {{- else }} - - name: KONG_PG_PASSWORD - valueFrom: - secretKeyRef: - name: {{ include "kong.postgresql.secretName" . }} - key: postgresql-password - {{- end }} - - name: KONG_PG_HOST - value: {{ include "kong.postgresql.host" . }} - - name: KONG_PG_USER - value: {{ include "kong.postgresql.user" . }} - {{- end }} - {{- if (eq .Values.database "cassandra") }} - - name: KONG_DATABASE - value: "cassandra" - {{- if .Values.cassandra.usePasswordFile }} - - name: KONG_CASSANDRA_PASSWORD_FILE - value: "/bitnami/kong/secrets/cassandra-password" - {{- else }} - - name: KONG_CASSANDRA_PASSWORD - valueFrom: - secretKeyRef: - name: {{ include "kong.cassandra.secretName" . }} - key: cassandra-password - {{- end }} - - name: KONG_CASSANDRA_CONTACT_POINTS - value: {{ include "kong.cassandra.contactPoints" . }} - - name: KONG_CASSANDRA_PORT - value: {{ include "kong.cassandra.port" . | quote }} - - name: KONG_CASSANDRA_USER - value: {{ include "kong.cassandra.user" . | quote }} - {{- end }} - {{- if .Values.metrics.enabled }} - - name: KONG_NGINX_HTTP_INCLUDE - value: "/bitnami/kong/metrics-exporter/exporter.conf" - {{- end }} - {{- if .Values.kong.extraEnvVars }} - {{- include "common.tplvalues.render" (dict "value" .Values.kong.extraEnvVars "context" $) | nindent 12 }} - {{- end }} - {{- if or .Values.kong.extraEnvVarsCM .Values.kong.extraEnvVarsSecret }} - envFrom: - {{- if .Values.kong.extraEnvVarsCM }} - - configMapRef: - name: {{ .Values.kong.extraEnvVarsCM }} - {{- end }} - {{- if .Values.kong.extraEnvVarsSecret }} - - secretRef: - name: {{ .Values.kong.extraEnvVarsSecret }} - {{- end }} - {{- end }} - ports: - - name: http-proxy - containerPort: 8000 - protocol: TCP - - name: https-proxy - containerPort: 8443 - protocol: TCP - - name: http-admin - containerPort: 8001 - protocol: TCP - - name: https-admin - containerPort: 8444 - protocol: TCP - {{- if .Values.metrics.enabled }} - - name: http-metrics - containerPort: {{ .Values.metrics.service.port }} - protocol: TCP - {{- end }} - {{- if .Values.kong.livenessProbe.enabled }} - livenessProbe: - exec: - command: - - /bin/bash - - -ec - - /health/kong-container-health.sh - initialDelaySeconds: {{ .Values.kong.livenessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.kong.livenessProbe.periodSeconds }} - timeoutSeconds: {{ .Values.kong.livenessProbe.timeoutSeconds }} - failureThreshold: {{ .Values.kong.livenessProbe.failureThreshold }} - successThreshold: {{ .Values.kong.livenessProbe.successThreshold }} - {{- else if .Values.kong.customLivenessProbe }} - livenessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.kong.customLivenessProbe "context" $) | nindent 12 }} - {{- end }} - {{- if .Values.kong.readinessProbe.enabled }} - readinessProbe: - exec: - command: - - /bin/bash - - -ec - - /health/kong-container-health.sh - initialDelaySeconds: {{ .Values.kong.readinessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.kong.readinessProbe.periodSeconds }} - timeoutSeconds: {{ .Values.kong.readinessProbe.timeoutSeconds }} - failureThreshold: {{ .Values.kong.readinessProbe.failureThreshold }} - successThreshold: {{ .Values.kong.readinessProbe.successThreshold }} - {{- else if .Values.kong.customReadinessProbe }} - readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.kong.customReadinessProbe "context" $) | nindent 12 }} - {{- end }} - {{- if .Values.kong.resources }} - resources: {{- toYaml .Values.kong.resources | nindent 12 }} - {{- end }} - volumeMounts: - - name: health - mountPath: /health - {{- if .Values.metrics.enabled }} - - name: metrics-init-scripts - mountPath: /docker-entrypoint-initdb.d/metrics-init - - name: metrics-server-block - mountPath: "/bitnami/kong/metrics-exporter" - {{ end }} - {{- if .Values.kong.initScriptsCM }} - - name: custom-init-scripts-cm - mountPath: /docker-entrypoint-initdb.d/cm - {{- end }} - {{- if .Values.kong.initScriptsSecret }} - - name: custom-init-scripts-secret - mountPath: /docker-entrypoint-initdb.d/secret - {{- end }} - {{- if .Values.kong.extraVolumeMounts }} - {{- include "common.tplvalues.render" (dict "value" .Values.kong.extraVolumeMounts "context" $) | nindent 12 }} - {{- end }} - {{- if .Values.ingressController.enabled }} - - name: kong-ingress-controller - image: {{ template "kong.ingress-controller.image" . }} - imagePullPolicy: {{ .Values.ingressController.image.pullPolicy }} - {{- if .Values.containerSecurityContext }} - securityContext: {{- include "common.tplvalues.render" (dict "value" .Values.containerSecurityContext "context" $) | nindent 12 }} - {{- end }} - {{- if .Values.ingressController.args }} - command: {{- include "common.tplvalues.render" (dict "value" .Values.ingressController.command "context" $) | nindent 12 }} - {{- else }} - command: - - bash - - -ec - - /health/ingress-container-start.sh - {{- end }} - {{- if .Values.ingressController.args }} - args: {{- include "common.tplvalues.render" (dict "value" .Values.ingressController.args "context" $) | nindent 12 }} - {{- end }} - env: - - name: CONTROLLER_KONG_ADMIN_URL - value: http://127.0.0.1:8001 - - name: CONTROLLER_PUBLISH_SERVICE - value: {{ printf "%s/%s" .Release.Namespace (include "common.names.fullname" .) | quote }} - - name: CONTROLLER_INGRESS_CLASS - value: {{ .Values.ingressController.ingressClass }} - - name: CONTROLLER_ELECTION_ID - value: {{ printf "kong-ingress-controller-leader-%s" .Values.ingressController.ingressClass }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - {{- if .Values.ingressController.extraEnvVars }} - {{- include "common.tplvalues.render" (dict "value" .Values.ingressController.extraEnvVars "context" $) | nindent 12 }} - {{- end }} - {{- if or .Values.ingressController.extraEnvVarsCM .Values.ingressController.extraEnvVarsSecret }} - envFrom: - {{- if .Values.ingressController.extraEnvVarsCM }} - - configMapRef: - name: {{ .Values.ingressController.extraEnvVarsCM }} - {{- end }} - {{- if .Values.ingressController.extraEnvVarsSecret }} - - secretRef: - name: {{ .Values.ingressController.extraEnvVarsSecret }} - {{- end }} - {{- end }} - ports: - - name: http-health - containerPort: 10254 - protocol: TCP - {{- if .Values.ingressController.livenessProbe.enabled }} - livenessProbe: - httpGet: - path: "/healthz" - port: http-health - scheme: HTTP - initialDelaySeconds: {{ .Values.ingressController.livenessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.ingressController.livenessProbe.periodSeconds }} - timeoutSeconds: {{ .Values.ingressController.livenessProbe.timeoutSeconds }} - failureThreshold: {{ .Values.ingressController.livenessProbe.failureThreshold }} - successThreshold: {{ .Values.ingressController.livenessProbe.successThreshold }} - {{- else if .Values.ingressController.customLivenessProbe }} - livenessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.ingressController.customLivenessProbe "context" $) | nindent 12 }} - {{- end }} - {{- if .Values.ingressController.readinessProbe.enabled }} - readinessProbe: - httpGet: - path: "/healthz" - port: http-health - scheme: HTTP - initialDelaySeconds: {{ .Values.ingressController.readinessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.ingressController.readinessProbe.periodSeconds }} - timeoutSeconds: {{ .Values.ingressController.readinessProbe.timeoutSeconds }} - failureThreshold: {{ .Values.ingressController.readinessProbe.failureThreshold }} - successThreshold: {{ .Values.ingressController.readinessProbe.successThreshold }} - {{- else if .Values.ingressController.customReadinessProbe }} - readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.ingressController.customReadinessProbe "context" $) | nindent 12 }} - {{- end }} - {{- if .Values.ingressController.resources }} - resources: {{- toYaml .Values.ingressController.resources | nindent 12 }} - {{- end }} - volumeMounts: - - name: health - mountPath: /health - {{- if .Values.ingressController.extraVolumeMounts }} - {{- include "common.tplvalues.render" (dict "value" .Values.ingressController.extraVolumeMounts "context" $) | nindent 12 }} - {{- end }} - {{- end }} - {{- if .Values.sidecars }} - {{- include "common.tplvalues.render" (dict "value" .Values.sidecars "context" $) | nindent 8 }} - {{- end }} - volumes: - - name: health - configMap: - name: {{ template "common.names.fullname" . }}-scripts - defaultMode: 0755 - {{- if .Values.metrics.enabled }} - - name: metrics-init-scripts - configMap: - name: {{ template "common.names.fullname" . }}-metrics-scripts - defaultMode: 0755 - - name: metrics-server-block - configMap: - name: {{ template "common.names.fullname" . }}-metrics-exporter - {{- end }} - {{- if .Values.kong.initScriptsCM }} - - name: custom-init-scripts-cm - configMap: - name: {{ .Values.kong.initScriptsCM }} - defaultMode: 0755 - {{- end }} - {{- if .Values.kong.initScriptsSecret }} - - name: custom-init-scripts-secret - secret: - secretName: {{ .Values.kong.initScriptsSecret }} - defaultMode: 0755 - {{- end }} - {{- if .Values.extraVolumes }} - {{- include "common.tplvalues.render" (dict "value" .Values.extraVolumes "context" $) | nindent 8 }} - {{- end }} diff --git a/packages/helm-charts/kong/templates/external-database-secret.yaml b/packages/helm-charts/kong/templates/external-database-secret.yaml deleted file mode 100644 index 35755dc716c..00000000000 --- a/packages/helm-charts/kong/templates/external-database-secret.yaml +++ /dev/null @@ -1,23 +0,0 @@ -{{- if (include "kong.createExternalDBSecret" .) }} -apiVersion: v1 -kind: Secret -metadata: - name: {{ include "common.names.fullname" . }}-external-secret - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: server - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -type: Opaque -data: - {{- if .Values.cassandra.external.password }} - cassandra-password: {{ .Values.cassandra.external.password | b64enc | quote }} - {{- end }} - {{- if .Values.postgresql.external.password }} - postgresql-password: {{ .Values.postgresql.external.password | b64enc | quote }} - {{- end }} -{{- end }} diff --git a/packages/helm-charts/kong/templates/extra-list.yaml b/packages/helm-charts/kong/templates/extra-list.yaml deleted file mode 100644 index 9ac65f9e16f..00000000000 --- a/packages/helm-charts/kong/templates/extra-list.yaml +++ /dev/null @@ -1,4 +0,0 @@ -{{- range .Values.extraDeploy }} ---- -{{ include "common.tplvalues.render" (dict "value" . "context" $) }} -{{- end }} diff --git a/packages/helm-charts/kong/templates/hpa.yaml b/packages/helm-charts/kong/templates/hpa.yaml deleted file mode 100644 index 9b3abb6e0aa..00000000000 --- a/packages/helm-charts/kong/templates/hpa.yaml +++ /dev/null @@ -1,24 +0,0 @@ -{{- if .Values.autoscaling.enabled }} -apiVersion: {{ .Values.autoscaling.apiVersion }} -kind: HorizontalPodAutoscaler -metadata: - name: {{ include "common.names.fullname" . }} - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: server - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -spec: - scaleTargetRef: - apiVersion: {{ include "common.capabilities.deployment.apiVersion" . }} - kind: Deployment - name: {{ include "common.names.fullname" . }} - minReplicas: {{ .Values.autoscaling.minReplicas }} - maxReplicas: {{ .Values.autoscaling.maxReplicas }} - metrics: - {{- include "common.tplvalues.render" (dict "value" .Values.autoscaling.metrics "context" $) | nindent 4 }} -{{- end }} diff --git a/packages/helm-charts/kong/templates/ingress-controller-rbac.yaml b/packages/helm-charts/kong/templates/ingress-controller-rbac.yaml deleted file mode 100644 index 0ecac2238fe..00000000000 --- a/packages/helm-charts/kong/templates/ingress-controller-rbac.yaml +++ /dev/null @@ -1,187 +0,0 @@ -{{- if and .Values.ingressController.rbac.create .Values.ingressController.enabled }} -apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} -kind: Role -metadata: - name: {{ include "common.names.fullname" . }} - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: server - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -rules: - - apiGroups: - - "" - resources: - - configmaps - - pods - - secrets - - namespaces - verbs: - - get - - apiGroups: - - "" - resources: - - configmaps - resourceNames: - - "kong-ingress-controller-leader-{{ .Values.ingressController.ingressClass }}-{{ .Values.ingressController.ingressClass }}" - verbs: - - get - - update - - apiGroups: - - "" - resources: - - configmaps - verbs: - - create - - apiGroups: - - "" - resources: - - endpoints - verbs: - - get ---- -apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} -kind: RoleBinding -metadata: - name: {{ template "common.names.fullname" . }} - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: server - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: {{ template "common.names.fullname" . }} -subjects: - - kind: ServiceAccount - name: {{ template "kong.serviceAccount" . }} - namespace: {{ .Release.Namespace }} ---- -apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} -kind: ClusterRole -metadata: - labels: {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: server - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} - name: {{ template "common.names.fullname" . }} -rules: - - apiGroups: - - "" - resources: - - endpoints - - nodes - - pods - - secrets - verbs: - - list - - watch - - apiGroups: - - "" - resources: - - nodes - verbs: - - get - - apiGroups: - - "" - resources: - - services - verbs: - - get - - list - - watch - - apiGroups: - - "extensions" - - "networking.k8s.io" - - "networking.internal.knative.dev" - resources: - - ingresses - verbs: - - get - - list - - watch - - apiGroups: - - "" - resources: - - events - verbs: - - create - - patch - - apiGroups: - - "extensions" - - "networking.k8s.io" - - "networking.internal.knative.dev" - resources: - - ingresses/status - verbs: - - update - - apiGroups: - - "configuration.konghq.com" - resources: - - tcpingresses/status - verbs: - - update - - apiGroups: - - "configuration.konghq.com" - resources: - - kongplugins - - kongclusterplugins - - kongcredentials - - kongconsumers - - kongingresses - - tcpingresses - verbs: - - get - - list - - watch ---- -apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} -kind: ClusterRoleBinding -metadata: - name: {{ template "common.names.fullname" . }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: server - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: {{ template "common.names.fullname" . }} -subjects: - - kind: ServiceAccount - name: {{ template "kong.serviceAccount" . }} - namespace: {{ .Release.Namespace }} ---- -{{- if not .Values.ingressController.rbac.existingServiceAccount }} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ template "common.names.fullname" . }} - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: server - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -{{- end }} -{{- end }} diff --git a/packages/helm-charts/kong/templates/ingress.yaml b/packages/helm-charts/kong/templates/ingress.yaml deleted file mode 100644 index 0210d8edad2..00000000000 --- a/packages/helm-charts/kong/templates/ingress.yaml +++ /dev/null @@ -1,58 +0,0 @@ -{{- if .Values.ingress.enabled -}} -apiVersion: {{ template "common.capabilities.ingress.apiVersion" . }} -kind: Ingress -metadata: - name: {{ include "common.names.fullname" . }} - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: server - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - annotations: - {{- if .Values.ingress.certManager }} - kubernetes.io/tls-acme: "true" - {{- end }} - {{- if .Values.ingress.annotations }} - {{- include "common.tplvalues.render" (dict "value" .Values.ingress.annotations "context" $) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -spec: - rules: - {{- if .Values.ingress.hostname }} - - host: {{ .Values.ingress.hostname }} - http: - paths: - {{- if .Values.ingress.extraPaths }} - {{- toYaml .Values.ingress.extraPaths | nindent 10 }} - {{- end }} - - path: {{ .Values.ingress.path }} - {{- if eq "true" (include "common.ingress.supportsPathType" .) }} - pathType: {{ .Values.ingress.pathType }} - {{- end }} - backend: {{- include "common.ingress.backend" (dict "serviceName" (include "common.names.fullname" .) "servicePort" "http-proxy" "context" $) | nindent 14 }} - {{- end }} - {{- range .Values.ingress.extraHosts }} - - host: {{ .name | quote }} - http: - paths: - - path: {{ default "/" .path }} - {{- if eq "true" (include "common.ingress.supportsPathType" $) }} - pathType: {{ default "ImplementationSpecific" .pathType }} - {{- end }} - backend: {{- include "common.ingress.backend" (dict "serviceName" (include "common.names.fullname" $) "servicePort" "http-proxy" "context" $) | nindent 14 }} - {{- end }} - {{- if or .Values.ingress.tls .Values.ingress.extraTls }} - tls: - {{- if .Values.ingress.tls }} - - hosts: - - {{ .Values.ingress.hostname }} - secretName: {{ printf "%s-tls" .Values.ingress.hostname }} - {{- end }} - {{- if .Values.ingress.extraTls }} - {{- include "common.tplvalues.render" ( dict "value" .Values.ingress.extraTls "context" $ ) | nindent 4 }} - {{- end }} - {{- end }} -{{- end }} diff --git a/packages/helm-charts/kong/templates/kong-prometheus-role.yaml b/packages/helm-charts/kong/templates/kong-prometheus-role.yaml deleted file mode 100644 index 36e85ef1226..00000000000 --- a/packages/helm-charts/kong/templates/kong-prometheus-role.yaml +++ /dev/null @@ -1,11 +0,0 @@ -{{- if and .Values.metrics.enabled .Values.metrics.serviceMonitor.enabled .Values.metrics.serviceMonitor.rbac.create }} -apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} -kind: Role -metadata: - name: {{ template "common.names.fullname" . }}-prometheus - namespace: {{ .Release.Namespace }} -rules: -- apiGroups: [""] - resources: ["endpoints", "services", "pods"] - verbs: ["get", "list", "watch"] -{{- end }} diff --git a/packages/helm-charts/kong/templates/kong-prometheus-rolebinding.yaml b/packages/helm-charts/kong/templates/kong-prometheus-rolebinding.yaml deleted file mode 100644 index ce4cb8b909c..00000000000 --- a/packages/helm-charts/kong/templates/kong-prometheus-rolebinding.yaml +++ /dev/null @@ -1,19 +0,0 @@ -{{- if and .Values.metrics.enabled .Values.metrics.serviceMonitor.enabled .Values.metrics.serviceMonitor.rbac.create }} -apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} -kind: RoleBinding -metadata: - name: {{ template "common.names.fullname" . }}-prometheus - namespace: {{ .Release.Namespace }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: {{ template "common.names.fullname" . }}-prometheus -subjects: - {{- if .Values.metrics.serviceMonitor.namespace }} - - namespace: {{ .Values.metrics.serviceMonitor.namespace }} - {{- else }} - - namespace: {{ .Release.Namespace }} - {{- end }} - kind: ServiceAccount - name: {{ required "A valid .Values.metrics.serviceMonitor.serviceAccount entry required!" .Values.metrics.serviceMonitor.serviceAccount }} -{{- end }} diff --git a/packages/helm-charts/kong/templates/kong-script-configmap.yaml b/packages/helm-charts/kong/templates/kong-script-configmap.yaml deleted file mode 100644 index d20d873ae33..00000000000 --- a/packages/helm-charts/kong/templates/kong-script-configmap.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "common.names.fullname" . }}-scripts - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: server - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -data: - kong-container-health.sh: |- - #!/bin/bash - - set -o errexit - set -o nounset - set -o pipefail - - # Load libraries - . /opt/bitnami/scripts/libos.sh - . /opt/bitnami/scripts/libkong.sh - - # Load Kong environment variables - eval "$(kong_env)" - - is_kong_running - - ingress-container-wait-for-kong.sh: |- - #!/bin/bash - - echo "Waiting for the Kong container to be ready" - if wait-for-port --timeout={{ .Values.ingressController.proxyReadyTimeout }} --host=127.0.0.1 --state=inuse 8000; then - echo "Kong container ready" - else - echo "Kong not ready after {{ .Values.ingressController.proxyReadyTimeout }} seconds" - exit 1 - fi - - ingress-container-start.sh: |- - #!/bin/bash - - . /health/ingress-container-wait-for-kong.sh - - kong-ingress-controller diff --git a/packages/helm-charts/kong/templates/metrics-exporter-configmap.yaml b/packages/helm-charts/kong/templates/metrics-exporter-configmap.yaml deleted file mode 100644 index fd99cebad10..00000000000 --- a/packages/helm-charts/kong/templates/metrics-exporter-configmap.yaml +++ /dev/null @@ -1,30 +0,0 @@ -{{- if .Values.metrics.enabled -}} -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "common.names.fullname" . }}-metrics-exporter - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: server - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -data: - exporter.conf: |- - # Prometheus metrics - server { - server_name kong_prometheus_exporter; - listen 0.0.0.0:{{ .Values.metrics.service.port }}; - access_log off; - location /metrics { - default_type text/plain; - content_by_lua_block { - local prometheus = require "kong.plugins.prometheus.exporter" - prometheus:collect() - } - } - } -{{- end }} diff --git a/packages/helm-charts/kong/templates/metrics-script-configmap.yaml b/packages/helm-charts/kong/templates/metrics-script-configmap.yaml deleted file mode 100644 index e38816c9928..00000000000 --- a/packages/helm-charts/kong/templates/metrics-script-configmap.yaml +++ /dev/null @@ -1,36 +0,0 @@ -{{- if .Values.metrics.enabled -}} -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "common.names.fullname" . }}-metrics-scripts - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: server - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -data: - enable-metrics-plugin.sh: |- - #!/bin/bash - . /opt/bitnami/scripts/libos.sh - . /opt/bitnami/scripts/libkong.sh - - info "Enabling prometheus plugin" - - if curl --silent http://localhost:8001/ | grep -Eo '"prometheus":false' > /dev/null; then - if ! curl --silent http://localhost:8001/plugins -d name=prometheus; then - info "Issue enabling prometheus plugin, this could be due to a race condition with another kong node. Checking status" - fi - if curl http://localhost:8001/ | grep -Eo '"prometheus":true' > /dev/null; then - info "Prometheus metrics plugin enabled" - else - error "Error enabling Prometheus plugin" - exit 1 - fi - else - info "Prometheus plugin already enabled" - fi -{{- end }} diff --git a/packages/helm-charts/kong/templates/metrics-service.yaml b/packages/helm-charts/kong/templates/metrics-service.yaml deleted file mode 100644 index ea30093a77c..00000000000 --- a/packages/helm-charts/kong/templates/metrics-service.yaml +++ /dev/null @@ -1,36 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ include "common.names.fullname" . }}-metrics - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: server - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if or .Values.metrics.service.annotations .Values.commonAnnotations }} - annotations: - {{- if .Values.commonAnnotations }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.metrics.service.annotations }} - {{- include "common.tplvalues.render" (dict "value" .Values.metrics.service.annotations "context" $) | nindent 4 }} - {{- end }} - {{- end }} -spec: - type: {{ .Values.metrics.service.type }} - {{- if and (eq .Values.metrics.service.type "LoadBalancer") (not (empty .Values.metrics.service.loadBalancerIP)) }} - loadBalancerIP: {{ .Values.metrics.service.loadBalancerIP }} - {{- end }} - ports: - - port: {{ .Values.metrics.service.port }} - targetPort: http-metrics - protocol: TCP - name: http-metrics - {{- if and (or (eq .Values.metrics.service.type "NodePort") (eq .Values.metrics.service.type "LoadBalancer")) (not (empty .Values.metrics.service.nodePort)) }} - nodePort: {{ .Values.metrics.service.nodePort }} - {{- else if eq .Values.metrics.service.type "ClusterIP" }} - nodePort: null - {{- end }} - selector: {{- include "common.labels.matchLabels" . | nindent 4 }} - app.kubernetes.io/component: server diff --git a/packages/helm-charts/kong/templates/migrate-job.yaml b/packages/helm-charts/kong/templates/migrate-job.yaml deleted file mode 100644 index 3fdf2ee5e82..00000000000 --- a/packages/helm-charts/kong/templates/migrate-job.yaml +++ /dev/null @@ -1,113 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - name: {{ include "common.names.fullname" . }}-migrate - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: server - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.migration.annotations "context" $ ) | nindent 4 }} - {{- if .Values.commonAnnotations }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -spec: - template: - metadata: - labels: {{- include "common.labels.standard" . | nindent 8 }} - app.kubernetes.io/component: migration - annotations: - {{- if .Values.migration.podAnnotations }} - {{- include "common.tplvalues.render" (dict "value" .Values.migration.podAnnotations "context" $) | nindent 8 }} - {{- end }} - spec: - {{- include "kong.imagePullSecrets" . | nindent 6 }} - restartPolicy: OnFailure - {{- if .Values.podSecurityContext }} - securityContext: {{- include "common.tplvalues.render" (dict "value" .Values.podSecurityContext "context" $) | nindent 8 }} - {{- end }} - {{- if .Values.migration.hostAliases }} - hostAliases: {{- include "common.tplvalues.render" (dict "value" .Values.migration.hostAliases "context" $) | nindent 8 }} - {{- end }} - containers: - - name: kong-migrate - image: {{ template "kong.migration.image" . }} - imagePullPolicy: {{ .Values.image.pullPolicy }} - {{- if .Values.migration.command }} - command: {{- include "common.tplvalues.render" (dict "value" .Values.migration.command "context" $) | nindent 12 }} - {{- end }} - {{- if .Values.migration.args }} - args: {{- include "common.tplvalues.render" (dict "value" .Values.migration.args "context" $) | nindent 12 }} - {{- end }} - {{- if .Values.containerSecurityContext }} - securityContext: {{- include "common.tplvalues.render" (dict "value" .Values.containerSecurityContext "context" $) | nindent 12 }} - {{- end }} - env: - - name: KONG_MIGRATE - value: "yes" - - name: KONG_EXIT_AFTER_MIGRATE - value: "yes" - {{- if (eq .Values.database "postgresql") }} - - name: KONG_DATABASE - value: "postgres" - {{- if .Values.postgresql.usePasswordFile }} - - name: KONG_POSTGRESQL_PASSWORD_FILE - value: "/bitnami/kong/secrets/postgresql-password" - {{- else }} - - name: KONG_PG_PASSWORD - valueFrom: - secretKeyRef: - name: {{ include "kong.postgresql.secretName" . }} - key: postgresql-password - {{- end }} - - name: KONG_PG_HOST - value: {{ include "kong.postgresql.host" . }} - - name: KONG_PG_USER - value: {{ include "kong.postgresql.user" . }} - {{- end }} - {{- if (eq .Values.database "cassandra") }} - - name: KONG_DATABASE - value: "cassandra" - {{- if .Values.cassandra.usePasswordFile }} - - name: KONG_CASSANDRA_PASSWORD_FILE - value: "/bitnami/kong/secrets/cassandra-password" - {{- else }} - - name: KONG_CASSANDRA_PASSWORD - valueFrom: - secretKeyRef: - name: {{ include "kong.cassandra.secretName" . }} - key: cassandra-password - {{- end }} - - name: KONG_CASSANDRA_CONTACT_POINTS - value: {{ include "kong.cassandra.contactPoints" . }} - - name: KONG_CASSANDRA_PORT - value: {{ include "kong.cassandra.port" . | quote }} - - name: KONG_CASSANDRA_USER - value: {{ include "kong.cassandra.user" . | quote }} - {{- end }} - {{- if .Values.migration.extraEnvVars }} - {{- include "common.tplvalues.render" (dict "value" .Values.migration.extraEnvVars "context" $) | nindent 12 }} - {{- end }} - {{- if or .Values.migration.extraEnvVarsCM .Values.migration.extraEnvVarsSecret }} - envFrom: - {{- if .Values.migration.extraEnvVarsCM }} - - configMapRef: - name: {{ .Values.migration.extraEnvVarsCM }} - {{- end }} - {{- if .Values.migration.extraEnvVarsSecret }} - - secretRef: - name: {{ .Values.migration.extraEnvVarsSecret }} - {{- end }} - {{- end }} - {{- if .Values.migration.extraVolumeMounts }} - volumeMounts: - {{- include "common.tplvalues.render" (dict "value" .Values.migration.extraVolumeMounts "context" $) | nindent 12 }} - {{- end }} - {{- if .Values.migration.resources }} - resources: {{- toYaml .Values.migration.resources | nindent 12 }} - {{- end }} - {{- if .Values.extraVolumes }} - volumes: - {{- include "common.tplvalues.render" (dict "value" .Values.extraVolumes "context" $) | nindent 6 }} - {{- end }} diff --git a/packages/helm-charts/kong/templates/pdb.yaml b/packages/helm-charts/kong/templates/pdb.yaml deleted file mode 100644 index 61a43a0bcc4..00000000000 --- a/packages/helm-charts/kong/templates/pdb.yaml +++ /dev/null @@ -1,25 +0,0 @@ -{{- if .Values.pdb.enabled }} -apiVersion: policy/v1beta1 -kind: PodDisruptionBudget -metadata: - name: {{ include "common.names.fullname" . }} - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: server - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -spec: - {{- if .Values.pdb.minAvailable }} - minAvailable: {{ .Values.pdb.minAvailable }} - {{- end }} - {{- if .Values.pdb.maxUnavailable }} - maxUnavailable: {{ .Values.pdb.maxUnavailable }} - {{- end }} - selector: - matchLabels: - {{- include "common.labels.matchLabels" . | nindent 6 }} -{{- end }} diff --git a/packages/helm-charts/kong/templates/service.yaml b/packages/helm-charts/kong/templates/service.yaml deleted file mode 100644 index a5af0c99ec8..00000000000 --- a/packages/helm-charts/kong/templates/service.yaml +++ /dev/null @@ -1,74 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ include "common.names.fullname" . }} - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: server - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if or .Values.service.annotations .Values.commonAnnotations }} - annotations: - {{- if .Values.service.annotations }} - {{- include "common.tplvalues.render" ( dict "value" .Values.service.annotations "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} - {{- end }} -spec: - type: {{ .Values.service.type }} - {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.externalTrafficPolicy)) }} - externalTrafficPolicy: {{ .Values.service.externalTrafficPolicy }} - {{- end }} - {{- if not (empty .Values.service.clusterIP) }} - clusterIP: {{ .Values.service.clusterIP }} - {{- end }} - {{- if and (eq .Values.service.type "LoadBalancer") (not (empty .Values.service.loadBalancerIP)) }} - loadBalancerIP: {{ .Values.service.loadBalancerIP }} - {{- end }} - ports: - - port: {{ .Values.service.proxyHttpPort }} - targetPort: http-proxy - protocol: TCP - name: http-proxy - {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.proxyHttpNodePort)) }} - nodePort: {{ .Values.service.proxyHttpNodePort }} - {{- else if eq .Values.service.type "ClusterIP" }} - nodePort: null - {{- end }} - - port: {{ .Values.service.proxyHttpsPort }} - targetPort: https-proxy - protocol: TCP - name: https-proxy - {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.proxyHttpsNodePort)) }} - nodePort: {{ .Values.service.proxyHttpsNodePort }} - {{- else if eq .Values.service.type "ClusterIP" }} - nodePort: null - {{- end }} - {{- if .Values.service.exposeAdmin }} - - port: {{ .Values.service.adminHttpPort }} - targetPort: http-admin - protocol: TCP - name: http-admin - {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.adminHttpNodePort)) }} - nodePort: {{ .Values.service.adminHttpNodePort }} - {{- else if eq .Values.service.type "ClusterIP" }} - nodePort: null - {{- end }} - - port: {{ .Values.service.adminHttpsPort }} - targetPort: https-admin - protocol: TCP - name: https-admin - {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.adminHttpsNodePort)) }} - nodePort: {{ .Values.service.adminHttpsNodePort }} - {{- else if eq .Values.service.type "ClusterIP" }} - nodePort: null - {{- end }} - {{- end }} - {{- if .Values.service.extraPorts }} - {{- include "common.tplvalues.render" (dict "value" .Values.service.extraPorts "context" $) | nindent 4 }} - {{- end }} - selector: {{- include "common.labels.matchLabels" . | nindent 4 }} - app.kubernetes.io/component: server diff --git a/packages/helm-charts/kong/templates/servicemonitor.yaml b/packages/helm-charts/kong/templates/servicemonitor.yaml deleted file mode 100644 index 66baec8ae81..00000000000 --- a/packages/helm-charts/kong/templates/servicemonitor.yaml +++ /dev/null @@ -1,37 +0,0 @@ -{{- if and .Values.metrics.enabled .Values.metrics.serviceMonitor.enabled }} -apiVersion: monitoring.coreos.com/v1 -kind: ServiceMonitor -metadata: - name: {{ include "common.names.fullname" . }} - {{- if .Values.metrics.serviceMonitor.namespace }} - namespace: {{ .Values.metrics.serviceMonitor.namespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: server - {{- if .Values.metrics.serviceMonitor.selector }} - {{- include "common.tplvalues.render" (dict "value" .Values.metrics.serviceMonitor.selector "context" $) | nindent 4 }} - {{- end }} - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -spec: - selector: - matchLabels: {{ include "common.labels.matchLabels" . | nindent 6 }} - endpoints: - - port: metrics - path: "/metrics" - {{- if .Values.metrics.serviceMonitor.interval }} - interval: {{ .Values.metrics.serviceMonitor.interval }} - {{- end }} - {{- if .Values.metrics.serviceMonitor.scrapeTimeout }} - scrapeTimeout: {{ .Values.metrics.serviceMonitor.scrapeTimeout }} - {{- end }} - namespaceSelector: - matchNames: - - {{ .Release.Namespace }} -{{- end }} diff --git a/packages/helm-charts/kong/templates/tls-secrets.yaml b/packages/helm-charts/kong/templates/tls-secrets.yaml deleted file mode 100644 index 3c187eae12d..00000000000 --- a/packages/helm-charts/kong/templates/tls-secrets.yaml +++ /dev/null @@ -1,43 +0,0 @@ -{{- if .Values.ingress.enabled }} -{{- if .Values.ingress.secrets }} -{{- range .Values.ingress.secrets }} -apiVersion: v1 -kind: Secret -metadata: - name: {{ .name }} - namespace: {{ $.Release.Namespace }} - labels: {{- include "common.labels.standard" $ | nindent 4 }} - {{- if $.Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" $.Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if $.Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" $.Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -type: kubernetes.io/tls -data: - tls.crt: {{ .certificate | b64enc }} - tls.key: {{ .key | b64enc }} ---- -{{- end }} -{{- else if and .Values.ingress.tls (not .Values.ingress.certManager) }} -{{- $ca := genCA "kong-ca" 365 }} -{{- $cert := genSignedCert .Values.ingress.hostname nil (list .Values.ingress.hostname) 365 $ca }} -apiVersion: v1 -kind: Secret -metadata: - name: {{ printf "%s-tls" .Values.ingress.hostname }} - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -type: kubernetes.io/tls -data: - tls.crt: {{ $cert.Cert | b64enc | quote }} - tls.key: {{ $cert.Key | b64enc | quote }} - ca.crt: {{ $ca.Cert | b64enc | quote }} -{{- end }} -{{- end }} diff --git a/packages/helm-charts/kong/values-clabs.yaml b/packages/helm-charts/kong/values-clabs.yaml deleted file mode 100644 index 4f2cb190fc8..00000000000 --- a/packages/helm-charts/kong/values-clabs.yaml +++ /dev/null @@ -1,39 +0,0 @@ -ingress: - enabled: true - hostname: kong.local - -ingressController: - ingressClass: kong - -postgresql: - postgresqlPassword: kong - -service: - exposeAdmin: true - annotations: - cloud.google.com/neg: '{"exposed_ports": {"80":{}}}' - -extraVolumes: -- name: kong-config - configMap: - name: kong-config - -kong: - extraVolumeMounts: - - name: kong-config - mountPath: /opt/bitnami/kong/conf/kong.conf.backup - subPath: kong.conf - - extraEnvVars: - - name: KONG_TRUSTED_IPS - value: 130.211.0.0/22,35.191.0.0/16,34.120.148.61/32 - - name: KONG_REAL_IP_HEADER - value: X-Forwarded-For - - name: KONG_REAL_IP_RECURSIVE - value: "on" - - name: KONG_PROXY_ACCESS_LOG - value: /dev/stdout geth - - name: KONG_ADMIN_ACCESS_LOG - value: /dev/stdout geth - - name: KONG_NGINX_HTTP_LOG_FORMAT - value: geth '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for" "$remote_addr"' diff --git a/packages/helm-charts/kong/values.yaml b/packages/helm-charts/kong/values.yaml deleted file mode 100644 index 215c0f8c00f..00000000000 --- a/packages/helm-charts/kong/values.yaml +++ /dev/null @@ -1,694 +0,0 @@ -## Global Docker image parameters -## Please, note that this will override the image parameters, including dependencies, configured to use the global value -## Current available global Docker image parameters: imageRegistry, imagePullSecrets and storageClass -## -# global: -# imageRegistry: myRegistryName -# imagePullSecrets: -# - myRegistryKeySecretName -# storageClass: myStorageClass - -## Bitnami kong image version -## ref: https://hub.docker.com/r/bitnami/kong/tags/ -## -image: - registry: docker.io - repository: bitnami/kong - tag: 2.4.1-debian-10-r9 - ## Specify a imagePullPolicy - ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' - ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images - ## - pullPolicy: IfNotPresent - ## Optionally specify an array of imagePullSecrets. - ## Secrets must be manually created in the namespace. - ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ - ## - # pullSecrets: - # - myRegistryKeySecretName - -## Force target Kubernetes version (using Helm capabilites if not set) -## -kubeVersion: - -## Select database: can be either postgresql or cassandra -## -database: postgresql - -## String to partially override kong.fullname template (will maintain the release name) -## -nameOverride: - -## String to fully override kong.fullname template -## -fullnameOverride: - -## Number of kong Pod replicas -## -replicaCount: 2 - -## Deployment pod host aliases -## https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/ -## -hostAliases: [] - -## Set up update strategy for kong installation. Set to Recreate if you use persistent volume that cannot be mounted by more than one pods to makesure the pods is destroyed first. -## ref: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy -## Example: -# updateStrategy: -# type: RollingUpdate -# rollingUpdate: -# maxSurge: 25% -# maxUnavailable: 25% -updateStrategy: - type: RollingUpdate - -## Use an alternate scheduler, e.g. "stork". -## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ -## -schedulerName: - -## Common annotations to add to all Kong resources (sub-charts are not considered). Evaluated as a template -## -commonAnnotations: {} - -## Common labels to add to all Kong resources (sub-charts are not considered). Evaluated as a template -## -commonLabels: {} - -## Use a daemonset instead of a deployment -## -useDaemonset: false - -kong: - ## Command and args for running the container (set to default if not set). Use array form - ## - command: - args: - ## Configmap with init scripts to execute - ## - initScriptsCM: - - ## Secret with init scripts to execute (for sensitive data) - ## - initScriptsSecret: - - ## An array to add extra env vars - ## For example: - ## extraEnvVars: - ## - name: GF_DEFAULT_INSTANCE_NAME - ## value: my-instance - ## - extraEnvVars: [] - - ## ConfigMap with extra env vars: - ## - extraEnvVarsCM: - - ## Secret with extra env vars: - ## - extraEnvVarsSecret: - ## Array to add extra mounts (normally used with extraVolumes) - ## - extraVolumeMounts: [] - - ## Custom Liveness probe - ## - customLivenessProbe: {} - - ## Custom Rediness probe - ## - customReadinessProbe: {} - - ## - ## Liveness and readiness probes - ## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes - ## - livenessProbe: - enabled: true - initialDelaySeconds: 120 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 6 - successThreshold: 1 - - readinessProbe: - enabled: true - initialDelaySeconds: 30 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 6 - successThreshold: 1 - ## - ## Container lifecycle hooks - ## ref: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/ - ## - lifecycleHooks: {} - - ## Container resource requests and limits - ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ - ## - resources: - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - limits: {} - # cpu: 500m - # memory: 1Gi - requests: {} - # cpu: 250m - # memory: 256Mi - -ingressController: - enabled: true - customResourceDeletePolicy: {} - image: - registry: docker.io - repository: bitnami/kong-ingress-controller - tag: 1.2.0-debian-10-r53 - ## Specify a imagePullPolicy - ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' - ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images - ## - pullPolicy: IfNotPresent - ## Optionally specify an array of imagePullSecrets. - ## Secrets must be manually created in the namespace. - ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ - ## - # pullSecrets: - # - myRegistryKeySecretName - proxyReadyTimeout: 300 - rbac: - create: true - existingServiceAccount: - ingressClass: kong - ## Command and args for running the container (set to default if not set). Use array form - ## - command: - args: - ## An array to add extra env vars - ## For example: - ## extraEnvVars: - ## - name: GF_DEFAULT_INSTANCE_NAME - ## value: my-instance - ## - extraEnvVars: [] - - ## ConfigMap with extra env vars: - ## - extraEnvVarsCM: - - ## Secret with extra env vars: - ## - extraEnvVarsSecret: - ## Array to add extra mounts (normally used with extraVolumes) - ## - extraVolumeMounts: [] - - ## Custom Liveness probe - ## - customLivenessProbe: {} - - ## Custom Rediness probe - ## - customReadinessProbe: {} - - ## - ## Liveness and readiness probes - ## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes - ## - livenessProbe: - enabled: true - initialDelaySeconds: 120 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 6 - successThreshold: 1 - readinessProbe: - enabled: true - initialDelaySeconds: 30 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 6 - successThreshold: 1 - ## Container resource requests and limits - ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ - ## - resources: - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - limits: {} - # cpu: 500m - # memory: 1Gi - requests: {} - # cpu: 250m - # memory: 256Mi - -migration: - ## In case you want to use a custom image for Kong migration, set this value - ## - # image: - # registry: - # repository: - # tag: - ## Command and args for running the container (set to default if not set). Use array form - ## - command: - args: - - ## Deployment pod host aliases - ## https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/ - ## - hostAliases: [] - - ## Job annotation. By defeault set to post-install and post-upgrade - ## - annotations: - helm.sh/hook: post-install, post-upgrade - helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded - - ## An array to add extra env vars - ## For example: - ## extraEnvVars: - ## - name: GF_DEFAULT_INSTANCE_NAME - ## value: my-instance - ## - extraEnvVars: [] - - ## ConfigMap with extra env vars: - ## - extraEnvVarsCM: - - ## Secret with extra env vars: - ## - extraEnvVarsSecret: - ## Array to add extra mounts (normally used with extraVolumes) - ## - extraVolumeMounts: - ## Liveness and readiness probes - ## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes - ## - ## Container resource requests and limits - ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ - ## - resources: - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - limits: {} - # cpu: 500m - # memory: 1Gi - requests: {} - # cpu: 250m - # memory: 256Mi - -## Array to add extra volumes -## -extraVolumes: [] - -## Add init containers to the pod -## -initContainers: -## e.g. -# - name: your-image-name -# image: your-image -# imagePullPolicy: Always -# ports: -# - name: portname -# containerPort: 1234 - -## Add sidecar containers to the pod -## -sidecars: -## e.g. -# - name: your-image-name -# image: your-image -# imagePullPolicy: Always -# ports: -# - name: portname -# containerPort: 1234 - -## Service parameters -## -service: - ## K8s service type - ## - type: ClusterIP - - ## clusterIP for the service (optional) - ## This is the internal IP address of the service and is usually assigned randomly. - ## ref: https://kubernetes.io/docs/reference/kubernetes-api/service-resources/service-v1/#ServiceSpec - ## - clusterIP: - - ## externalTrafficPolicy for the service - ## default to "Cluster" - ## set to "Local" in order to preserve the client source IP (only on service of type LoadBalancer or NodePort) - ## ref: https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/ - ## - externalTrafficPolicy: - - ## kong proxy HTTP service port - ## - proxyHttpPort: 80 - ## kong proxy HTTPS service port - ## - proxyHttpsPort: 443 - - ## Include the admin ports in the service - ## - exposeAdmin: false - ## kong proxy HTTP service port - ## - adminHttpPort: 8001 - ## kong proxy HTTPS service port - ## - adminHttpsPort: 8444 - - ## Specify the nodePort value for the LoadBalancer and NodePort service types. - ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport - ## - # proxyHttpNodePort: - # proxyHttpsNodePort: - # adminHttpNodePort: - # adminHttpsNodePort: - - ## loadBalancerIP for the Kong Service (optional, cloud specific) - ## ref: http://kubernetes.io/docs/user-guide/services/#type-loadbalancer - ## - loadBalancerIP: - - ## Provide any additional annotations which may be required. This can be used to - ## set the LoadBalancer service type to internal only. - ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer - ## - annotations: {} - ## Extra ports to expose (normally used with the `sidecar` value) - # extraPorts: - -## Kong cluster domain -## -clusterDomain: cluster.local - -## Configure the ingress resource that allows you to access the -## Kong installation. Set up the URL -## ref: http://kubernetes.io/docs/user-guide/ingress/ -## -ingress: - ## Set to true to enable ingress record generation - ## - enabled: false - - ## Set this to true in order to add the corresponding annotations for cert-manager - ## - certManager: false - - ## Ingress Path type - ## - pathType: ImplementationSpecific - - ## Override API Version (automatically detected if not set) - ## - apiVersion: - - ## When the ingress is enabled, a host pointing to this will be created - ## - hostname: kong.local - - ## The Path to Kong. You may need to set this to '/*' in order to use this - ## with ALB ingress controllers. - ## - path: / - - ## Ingress annotations done as key:value pairs - ## For a full list of possible ingress annotations, please see - ## ref: https://github.com/kubernetes/ingress-nginx/blob/master/docs/user-guide/nginx-configuration/annotations.md - ## - ## If certManager is set to true, annotation kubernetes.io/tls-acme: "true" will automatically be set - ## - annotations: {} - - ## Enable TLS configuration for the hostname defined at ingress.hostname parameter - ## TLS certificates will be retrieved from a TLS secret with name: {{- printf "%s-tls" .Values.ingress.hostname }} - ## You can use the ingress.secrets parameter to create this TLS secret or relay on cert-manager to create it - ## - tls: false - - ## The list of additional hostnames to be covered with this ingress record. - ## Most likely the hostname above will be enough, but in the event more hosts are needed, this is an array - ## extraHosts: - ## - name: kong.local - ## path: / - ## - - ## Any additional arbitrary paths that may need to be added to the ingress under the main host. - ## For example: The ALB ingress controller requires a special rule for handling SSL redirection. - ## extraPaths: - ## - path: /* - ## backend: - ## serviceName: ssl-redirect - ## servicePort: use-annotation - ## - - ## The tls configuration for additional hostnames to be covered with this ingress record. - ## see: https://kubernetes.io/docs/concepts/services-networking/ingress/#tls - ## extraTls: - ## - hosts: - ## - kong.local - ## secretName: kong.local-tls - ## - - ## If you're providing your own certificates, please use this to add the certificates as secrets - ## key and certificate should start with -----BEGIN CERTIFICATE----- or - ## -----BEGIN RSA PRIVATE KEY----- - ## - ## name should line up with a tlsSecret set further up - ## If you're using cert-manager, this is unneeded, as it will create the secret for you if it is not set - ## - ## It is also possible to create and manage the certificates outside of this helm chart - ## Please see README.md for more information - ## - secrets: [] - ## - name: kong.local-tls - ## key: - ## certificate: - ## - -## SecurityContext configuration -## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ -## -containerSecurityContext: - runAsUser: 1001 - runAsNonRoot: true - -podSecurityContext: {} - -## Node labels for pod assignment -## Ref: https://kubernetes.io/docs/user-guide/node-selection/ -## -nodeSelector: {} - -## Tolerations for pod assignment -## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ -## -tolerations: [] - -## Pod affinity preset -## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity -## Allowed values: soft, hard -## -podAffinityPreset: "" - -## Pod anti-affinity preset -## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity -## Allowed values: soft, hard -## -podAntiAffinityPreset: soft - -## Node affinity preset -## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity -## Allowed values: soft, hard -## -nodeAffinityPreset: - ## Node affinity type - ## Allowed values: soft, hard - ## - type: "" - ## Node label key to match - ## E.g. - ## key: "kubernetes.io/e2e-az-name" - ## - key: "" - ## Node label values to match - ## E.g. - ## values: - ## - e2e-az1 - ## - e2e-az2 - ## - values: [] - -## Affinity for pod assignment -## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity -## Note: podAffinityPreset, podAntiAffinityPreset, and nodeAffinityPreset will be ignored when it's set -## -affinity: {} - -## Pod annotations -## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ -## -podAnnotations: {} - -## Pod labels -## Ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ -## -podLabels: {} - -## Prometheus metrics -## -metrics: - enabled: false - - ## Kong metrics service configuration - ## - service: - annotations: - prometheus.io/scrape: "true" - prometheus.io/port: "{{ .Values.metrics.service.port }}" - prometheus.io/path: "/metrics" - type: ClusterIP - port: 9119 - - ## Kong ServiceMonitor configuration - ## - serviceMonitor: - enabled: false - ## Namespace in which Prometheus is running - ## - # namespace: monitoring - - ## Service Account used by Prometheus - ## - # serviceAccount: prometheus - - ## Interval at which metrics should be scraped. - ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#endpoint - ## - # interval: 10s - - ## Timeout after which the scrape is ended - ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#endpoint - ## - # scrapeTimeout: 10s - - ## ServiceMonitor selector labels - ## ref: https://github.com/bitnami/charts/tree/master/bitnami/prometheus-operator#prometheus-configuration - ## - # selector: - # prometheus: my-prometheus - - ## If RBAC enabled on the cluster, Additional resources will be required - ## so Prometheus can reach kong's namespace - ## - rbac: - enabled: true - -## Add an horizontal pod autoscaler -## ref: https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ -## -autoscaling: - enabled: false - apiVersion: autoscaling/v2beta1 - minReplicas: 2 - maxReplicas: 5 - metrics: - - type: Resource - resource: - name: cpu - targetAverageUtilization: 80 - -## Kong Pod Disruption Budget -## ref: https://kubernetes.io/docs/concepts/workloads/pods/disruptions/ -## -pdb: - enabled: false - maxUnavailable: "50%" - -## Extra objects to deploy (value evaluated as a template) -## -extraDeploy: [] - -## PostgreSQL properties -## -postgresql: - ## Deploy the postgresql sub-chart - ## - enabled: true - ## Mounts secrets as a file - ## - usePasswordFile: false - ## Properties for using an existing PostgreSQL installation - ## - external: - ## Host of the external PostgreSQL installation - ## - host: - ## Username of the external PostgreSQL installation - ## - user: - ## Password of the external PostgreSQL installation - ## - password: - - ## Use an existing secret with the PostgreSQL password - ## - existingSecret: - - ## Name of the Database for Kong to access - ## - postgresqlDatabase: kong - - ## Create a username in the bundled PostgreSQL installation - ## - postgresqlUsername: kong - -## Cassandra properties -## -cassandra: - ## Deploy the cassandra sub-chart - ## - enabled: false - ## Database user to create - ## - dbUser: - user: kong - ## Mount secrets as files - ## - usePasswordFile: false - ## Properties for using an existing Cassandra installation - ## - external: - ## Array with the contact points - ## - hosts: [] - # - host1 - # - host2 - - ## Port for accessing the external cassandra installation - ## - port: 9042 - - ## Username of the external Cassandra installation - ## - user: - - ## Password of the external Cassandra installation - ## - password: - - ## Use an existing secret with the Cassandra password - ## - existingSecret: diff --git a/packages/helm-charts/konga/.helmignore b/packages/helm-charts/konga/.helmignore deleted file mode 100644 index 50af0317254..00000000000 --- a/packages/helm-charts/konga/.helmignore +++ /dev/null @@ -1,22 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ diff --git a/packages/helm-charts/konga/Chart.yaml b/packages/helm-charts/konga/Chart.yaml deleted file mode 100644 index e9e2fe0ba48..00000000000 --- a/packages/helm-charts/konga/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -appVersion: "1.0" -description: A Helm chart for Kubernetes -name: konga -version: 1.0.0 diff --git a/packages/helm-charts/konga/README.md b/packages/helm-charts/konga/README.md deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/packages/helm-charts/konga/httpie-jq.Dockerfile b/packages/helm-charts/konga/httpie-jq.Dockerfile deleted file mode 100644 index 7274d839734..00000000000 --- a/packages/helm-charts/konga/httpie-jq.Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM alpine/httpie:latest - -RUN apk add --no-cache jq diff --git a/packages/helm-charts/konga/templates/NOTES.txt b/packages/helm-charts/konga/templates/NOTES.txt deleted file mode 100644 index 16772ddd69a..00000000000 --- a/packages/helm-charts/konga/templates/NOTES.txt +++ /dev/null @@ -1,21 +0,0 @@ -1. Get the application URL by running these commands: -{{- if .Values.ingress.enabled }} -{{- range $host := .Values.ingress.hosts }} - {{- range .paths }} - http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ . }} - {{- end }} -{{- end }} -{{- else if contains "NodePort" .Values.service.type }} - export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "konga.fullname" . }}) - export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") - echo http://$NODE_IP:$NODE_PORT -{{- else if contains "LoadBalancer" .Values.service.type }} - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "konga.fullname" . }}' - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "konga.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') - echo http://$SERVICE_IP:{{ .Values.service.port }} -{{- else if contains "ClusterIP" .Values.service.type }} - export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "konga.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") - echo "Visit http://127.0.0.1:8080 to use your application" - kubectl port-forward $POD_NAME 8080:80 -{{- end }} diff --git a/packages/helm-charts/konga/templates/_helpers.tpl b/packages/helm-charts/konga/templates/_helpers.tpl deleted file mode 100644 index 19a1d008fd7..00000000000 --- a/packages/helm-charts/konga/templates/_helpers.tpl +++ /dev/null @@ -1,43 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Expand the name of the chart. -*/}} -{{- define "konga.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "konga.fullname" -}} -{{- if .Values.fullnameOverride -}} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} -{{- end -}} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "konga.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end -}} - - -{{/* -Valdiate an env value -{{ include "konga.validate_config_var" ( dict "name" "" "value" .Values.path.to.value.to.check) }} -*/}} -{{- define "konga.validate_config_var" -}} -{{- if .value -}} -{{ .name }}: {{ .value }} -{{- end -}} -{{- end -}} diff --git a/packages/helm-charts/konga/templates/configmap-snapshot.yaml b/packages/helm-charts/konga/templates/configmap-snapshot.yaml deleted file mode 100644 index 5e2c2956897..00000000000 --- a/packages/helm-charts/konga/templates/configmap-snapshot.yaml +++ /dev/null @@ -1,449 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "konga.fullname" . }}-snapshot - labels: - app.kubernetes.io/name: {{ include "konga.name" . }} - helm.sh/chart: {{ include "konga.chart" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -data: - kong_node.data: | - module.exports = [ - { - "name": "Kong", - "type": "default", - "kong_admin_url": "http://{{ .Values.kong.service }}:{{ .Values.kong.admin_port }}", - "health_checks": false, - "active" : true, - } - ] - snapshot.json: | - { - "createdUser": null, - "updatedUser": null, - "id": 5, - "name": "snapshot_May2022", - "kong_node_name": "Kong", - "kong_node_url": "http://kong:8001", - "kong_version": "0-10-x", - "data": { - "services": [ - { - "retries": 5, - "id": "90a37cb9-2c01-472b-8fff-715835e56156", - "name": "status", - "port": 8000, - "client_certificate": null, - "updated_at": 1635786900, - "read_timeout": 60000, - "tags": [], - "ca_certificates": null, - "connect_timeout": 60000, - "write_timeout": 60000, - "protocol": "http", - "created_at": 1623859719, - "host": "localhost", - "path": null, - "tls_verify_depth": null, - "tls_verify": null, - "extras": {} - }, - { - "retries": 5, - "id": "d98c9e59-dc44-4bad-81ca-d399724462b0", - "name": "Forno", - "port": 8545, - "client_certificate": null, - "updated_at": 1635786900, - "read_timeout": 60000, - "tags": [], - "ca_certificates": null, - "connect_timeout": 60000, - "write_timeout": 60000, - "protocol": "http", - "created_at": 1623232145, - "host": "rc1-fullnodes-rpc.rc1", - "path": null, - "tls_verify_depth": null, - "tls_verify": null, - "extras": {} - } - ], - "routes": [ - { - "request_buffering": true, - "response_buffering": true, - "id": "37f4e0db-fbc9-4aa3-9f47-5c957c54efdc", - "name": "health-check", - "preserve_host": false, - "created_at": 1623859737, - "updated_at": 1635786900, - "protocols": [ - "http", - "https" - ], - "tags": null, - "hosts": null, - "headers": null, - "service": { - "id": "90a37cb9-2c01-472b-8fff-715835e56156" - }, - "paths": [ - "/status", - "/status*", - "/kong/status", - "/kong/status*" - ], - "methods": [ - "GET" - ], - "sources": null, - "destinations": null, - "path_handling": "v0", - "strip_path": true, - "https_redirect_status_code": 426, - "snis": null, - "regex_priority": 100 - }, - { - "request_buffering": true, - "response_buffering": true, - "id": "536b91df-0542-4c78-8ef4-de35c237050c", - "name": "forno", - "preserve_host": false, - "created_at": 1634898059, - "updated_at": 1635764767, - "protocols": [ - "http", - "https" - ], - "tags": null, - "hosts": null, - "headers": null, - "service": { - "id": "d98c9e59-dc44-4bad-81ca-d399724462b0" - }, - "paths": [ - "/", - "/*" - ], - "methods": null, - "sources": null, - "destinations": null, - "path_handling": "v0", - "strip_path": true, - "https_redirect_status_code": 426, - "snis": null, - "regex_priority": 0 - }, - { - "request_buffering": true, - "response_buffering": true, - "id": "798f63dd-60bc-4bf9-a640-e8177b747d69", - "name": "kong-path", - "preserve_host": false, - "created_at": 1635430090, - "updated_at": 1635786900, - "protocols": [ - "http", - "https" - ], - "tags": null, - "hosts": null, - "headers": null, - "service": { - "id": "d98c9e59-dc44-4bad-81ca-d399724462b0" - }, - "paths": [ - "/kong" - ], - "methods": null, - "sources": null, - "destinations": null, - "path_handling": "v1", - "strip_path": true, - "https_redirect_status_code": 426, - "snis": null, - "regex_priority": 11 - } - ], - "consumers": [ - { - "custom_id": null, - "created_at": 1623404756, - "id": "1532538d-cb47-48fa-8a8e-b21952644a6e", - "tags": [], - "username": "anonymous", - "credentials": { - "key-auths": [] - } - }, - { - "custom_id": null, - "created_at": 1623404316, - "id": "bdfbe991-2e69-4d50-bf79-4681be7f5949", - "tags": [], - "username": "komenci", - "credentials": { - "key-auths": [ - { - "key": "komenci", - "created_at": 1636497435, - "ttl": null, - "id": "5c085269-23d8-4dda-9dfe-2661507c2c27", - "tags": null, - "consumer": { - "id": "bdfbe991-2e69-4d50-bf79-4681be7f5949" - } - }, - { - "key": "odisMainnetAPIKey-9029667", - "created_at": 1651575802, - "ttl": null, - "id": "b2d3bc17-185d-415a-a76e-941586bc66ef", - "tags": null, - "consumer": { - "id": "bdfbe991-2e69-4d50-bf79-4681be7f5949" - } - } - ] - } - } - ], - "plugins": [ - { - "config": { - "path": "/tmp/kong.log", - "custom_fields_by_lua": null, - "reopen": false - }, - "enabled": false, - "service": null, - "id": "0e6fe9fe-c218-4b72-a9b5-4d09359f7906", - "route": null, - "created_at": 1635780419, - "protocols": [ - "grpc", - "grpcs", - "http", - "https" - ], - "consumer": { - "id": "1532538d-cb47-48fa-8a8e-b21952644a6e" - }, - "tags": null, - "name": "file-log" - }, - { - "config": { - "hide_client_headers": false, - "minute": 1000000000, - "hour": null, - "day": null, - "month": null, - "header_name": "CF-Connecting-IP", - "limit_by": "header", - "policy": "local", - "second": 10000000, - "redis_timeout": 2000, - "redis_database": 0, - "redis_host": null, - "redis_port": 6379, - "path": null, - "year": null, - "redis_password": null, - "fault_tolerant": true - }, - "enabled": true, - "service": { - "id": "d98c9e59-dc44-4bad-81ca-d399724462b0" - }, - "id": "175852a3-9891-4b26-b0f3-f2832bceadbf", - "route": null, - "created_at": 1624889602, - "protocols": [ - "grpc", - "grpcs", - "http", - "https" - ], - "consumer": { - "id": "bdfbe991-2e69-4d50-bf79-4681be7f5949" - }, - "tags": null, - "name": "rate-limiting" - }, - { - "config": { - "status_code": 200, - "message": "ok", - "body": null, - "content_type": null - }, - "enabled": true, - "service": { - "id": "90a37cb9-2c01-472b-8fff-715835e56156" - }, - "id": "18ccb4e4-b09c-4251-af1d-d3367f7ca667", - "route": null, - "created_at": 1623859751, - "protocols": [ - "grpc", - "grpcs", - "http", - "https" - ], - "consumer": null, - "tags": null, - "name": "request-termination" - }, - { - "config": { - "path": "/tmp/kong.log", - "custom_fields_by_lua": null, - "reopen": false - }, - "enabled": false, - "service": null, - "id": "5ae582e9-db79-4fd2-b973-1ba1ae7ab622", - "route": null, - "created_at": 1635767630, - "protocols": [ - "grpc", - "grpcs", - "http", - "https" - ], - "consumer": null, - "tags": null, - "name": "file-log" - }, - { - "config": { - "hide_client_headers": false, - "minute": 20000, - "hour": 500000, - "day": null, - "month": null, - "header_name": "CF-Connecting-IP", - "limit_by": "header", - "policy": "local", - "second": null, - "redis_timeout": 2000, - "redis_database": 0, - "redis_host": null, - "redis_port": 6379, - "path": null, - "year": null, - "redis_password": null, - "fault_tolerant": true - }, - "enabled": true, - "service": { - "id": "d98c9e59-dc44-4bad-81ca-d399724462b0" - }, - "id": "6060fd61-cf04-4bb2-a426-0aea585ac159", - "route": null, - "created_at": 1624889534, - "protocols": [ - "grpc", - "grpcs", - "http", - "https" - ], - "consumer": { - "id": "1532538d-cb47-48fa-8a8e-b21952644a6e" - }, - "tags": null, - "name": "rate-limiting" - }, - { - "config": { - "key_names": [ - "apikey" - ], - "anonymous": "1532538d-cb47-48fa-8a8e-b21952644a6e", - "hide_credentials": false, - "key_in_header": true, - "key_in_query": true, - "key_in_body": false, - "run_on_preflight": true - }, - "enabled": true, - "service": { - "id": "d98c9e59-dc44-4bad-81ca-d399724462b0" - }, - "id": "96259f8e-0484-4c98-9f4d-d4406c354407", - "route": null, - "created_at": 1624889633, - "protocols": [ - "grpc", - "grpcs", - "http", - "https" - ], - "consumer": null, - "tags": null, - "name": "key-auth" - }, - { - "config": { - "allow": null, - "deny": [ - "186.189.238.210/32" - ] - }, - "enabled": true, - "service": null, - "id": "a0b3939d-2976-4369-8c8a-f00e9e39d171", - "route": null, - "created_at": 1635965325, - "protocols": [ - "grpc", - "grpcs", - "http", - "https" - ], - "consumer": null, - "tags": null, - "name": "ip-restriction" - }, - { - "config": { - "per_consumer": true - }, - "enabled": true, - "service": null, - "id": "cf0c9419-69b5-4143-b863-2fa86f094841", - "route": null, - "created_at": 1626078855, - "protocols": [ - "grpc", - "grpcs", - "http", - "https" - ], - "consumer": null, - "tags": null, - "name": "prometheus" - } - ], - "acls": [ - { - "group": "noratelimit", - "created_at": 1623404350, - "id": "57187a97-d48b-4128-b007-281c0ed33659", - "tags": null, - "consumer": { - "id": "bdfbe991-2e69-4d50-bf79-4681be7f5949" - } - } - ], - "upstreams": [], - "certificates": [], - "snis": [] - }, - "createdAt": "2022-05-27T07:14:08.000Z", - "updatedAt": "2022-05-27T07:14:08.000Z" - } diff --git a/packages/helm-charts/konga/templates/configmap.yaml b/packages/helm-charts/konga/templates/configmap.yaml deleted file mode 100644 index 5f92cad0c8c..00000000000 --- a/packages/helm-charts/konga/templates/configmap.yaml +++ /dev/null @@ -1,52 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "konga.fullname" . }}-config - labels: - app.kubernetes.io/name: {{ include "konga.name" . }} - helm.sh/chart: {{ include "konga.chart" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -data: - {{- if .Values.config }} - PORT: "{{ default 1337 .Values.config.port }}" - NODE_ENV: {{ default "development" .Values.config.node_env }} - {{ include "konga.validate_config_var" ( dict "name" "SSL_KEY_PATH" "value" .Values.config.ssl_key_path ) }} - {{ include "konga.validate_config_var" ( dict "name" "SSL_CRT_PATH" "value" .Values.config.ssl_crt_path ) }} - KONGA_HOOK_TIMEOUT: "{{ default 60000 .Values.config.konga_hook_timeout }}" - DB_ADAPTER: {{ default "postgres" .Values.config.db_adapter }} - {{ include "konga.validate_config_var" ( dict "name" "DB_URI" "value" .Values.config.db_uri ) }} - DB_HOST: {{ default "localhost" .Values.config.db_host }} - DB_PORT: "{{ default 5432 .Values.config.db_port }}" - {{ include "konga.validate_config_var" ( dict "name" "DB_USER" "value" .Values.config.db_user ) }} - {{ include "konga.validate_config_var" ( dict "name" "DB_PASSWORD" "value" .Values.config.db_password ) }} - DB_DATABASE: {{ default "konga_database" .Values.config.db_database }} - DB_PG_SCHEMA: {{ default "public" .Values.config.db_pg_schema }} - {{- if eq .Values.config.node_env "development" }} - KONGA_LOG_LEVEL: {{ default "debug" .Values.config.log_level }} - {{ else if eq .Values.config.node_env "production" }} - KONGA_LOG_LEVEL: {{ default "warn" .Values.config.log_level }} - {{- end }} - {{ include "konga.validate_config_var" ( dict "name" "TOKEN_SECRET" "value" .Values.config.token_secret ) }} - {{ include "konga.validate_config_var" ( dict "name" "KONGA_SEED_KONG_NODE_DATA_SOURCE_FILE" "value" .Values.config.konga_node_data ) }} - {{ include "konga.validate_config_var" ( dict "name" "KONGA_SEED_USER_DATA_SOURCE_FILE" "value" .Values.config.konga_user_data ) }} - NO_AUTH: "true" - {{- end }} - - {{- if .Values.ldap }} - KONGA_AUTH_PROVIDER: {{ default "local" .Values.ldap.auth_provider }} - KONGA_LDAP_HOST: {{ default "ldap://localhost:389" .Values.ldap.host }} - KONGA_LDAP_BIND_DN: {{ .Values.ldap.bind_dn }} - KONGA_LDAP_BIND_PASSWORD: {{ .Values.ldap.bind_pass }} - KONGA_LDAP_USER_SEARCH_BASE: {{ default "ou=users,dc=com" .Values.ldap.user_search_base }} - KONGA_LDAP_USER_SEARCH_FILTER: {{ default "(|(uid={{username}})(sAMAccountName={{username}}))" .Values.ldap.user_search_filter }} - KONGA_LDAP_USER_ATTRS: {{ default "uid,uidNumber,givenName,sn,mail" .Values.ldap.user_attrs }} - KONGA_LDAP_GROUP_SEARCH_BASE: {{ default "ou=groups,dc=com" .Values.ldap.group_search_base }} - KONGA_LDAP_GROUP_SEARCH_FILTER: {{ default "(|(memberUid={{uid}})(memberUid={{uidNumber}})(sAMAccountName={{uid}}))" .Values.ldap.group_search_filter }} - KONGA_LDAP_GROUP_ATTRS: {{ default "cn" .Values.ldap.group_attrs }} - KONGA_ADMIN_GROUP_REG: {{ default "^(admin|konga)$" .Values.ldap.group_reg }} - KONGA_LDAP_ATTR_USERNAME: {{ default "uid" .Values.ldap.attr_username }} - KONGA_LDAP_ATTR_FIRSTNAME: {{ default "givenName" .Values.ldap.attr_firstname }} - KONGA_LDAP_ATTR_LASTNAME: {{ default "sn" .Values.ldap.attr_lastname }} - KONGA_LDAP_ATTR_EMAIL: {{ default "mail" .Values.ldap.attr_email }} - {{- end }} diff --git a/packages/helm-charts/konga/templates/deployment.yaml b/packages/helm-charts/konga/templates/deployment.yaml deleted file mode 100644 index 14ae1eee31e..00000000000 --- a/packages/helm-charts/konga/templates/deployment.yaml +++ /dev/null @@ -1,104 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "konga.fullname" . }} - labels: - app.kubernetes.io/name: {{ include "konga.name" . }} - helm.sh/chart: {{ include "konga.chart" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -spec: - replicas: {{ .Values.replicaCount }} - selector: - matchLabels: - app.kubernetes.io/name: {{ include "konga.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - template: - metadata: - labels: - app.kubernetes.io/name: {{ include "konga.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - spec: - volumes: - - configMap: - defaultMode: 420 - name: {{ include "konga.fullname" . }}-snapshot - name: {{ include "konga.fullname" . }}-snapshot -{{- if .Values.extraVolumes }} -{{ toYaml .Values.extraVolumes | indent 8 }} -{{- end }} - containers: - - name: konga - image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - ports: - - name: http - containerPort: 1337 - protocol: TCP - livenessProbe: - httpGet: - path: / - port: http - readinessProbe: - httpGet: - path: / - port: http - envFrom: - - configMapRef: - name: {{ include "konga.fullname" . }}-config - resources: - {{- toYaml .Values.resources | nindent 12 }} - volumeMounts: - - mountPath: /opt/clabs/files/ - name: {{ include "konga.fullname" . }}-snapshot -{{- if .Values.extraVolumeMounts }} -{{ toYaml .Values.extraVolumeMounts | nindent 12 }} -{{- end }} - - name: restore-snapshot - image: jcortejoso/httpie-jq:latest - imagePullPolicy: IfNotPresent - stdin: true - tty: true - command: - - /bin/sh - - -c - args: - - | - while :; do - # Check if konga api is ready - if http HEAD localhost:1337 --follow --check-status >/dev/null 2>&1; then - break - fi - done - # Check if snapshot already exists - snapshot_name="$(cat /opt/clabs/files/snapshot.json | jq -r '.name')" - if http GET localhost:1337/api/snapshot | jq -e -r ".[] | select (.name==\"$snapshot_name\")" >/dev/null 2>&1; then - echo "Snapshot already present" - else - # Upload snapshot - http POST localhost:1337/api/snapshot Authorization:'Bearer noauthtoken' 'Content-Type: application/json' < /opt/clabs/files/snapshot.json - # Restore snapshot - ## Connection id field can be non-unique. It should be 1 - connection_id=$(http GET localhost:1337/api/kongnode | jq -r '.[] | select (.name=="Kong") | .id' | head -n1) - snapshot_id=$(http GET localhost:1337/api/snapshot | jq -e -r ".[] | select (.name==\"$snapshot_name\") | .id") - sleep 90 - http POST localhost:1337/api/snapshots/$snapshot_id/restore Authorization:'Bearer noauthtoken' "Connection-Id:$connection_id" imports\\:='["services","routes","consumers","plugins","acls","upstreams","certificates","snis"]' token=noauthtoken - sleep 90 - http POST localhost:1337/api/snapshots/$snapshot_id/restore Authorization:'Bearer noauthtoken' "Connection-Id:$connection_id" imports\\:='["services","routes","consumers","plugins","acls","upstreams","certificates","snis"]' token=noauthtoken - fi - tail -f /dev/null - volumeMounts: - - mountPath: /opt/clabs/files/ - name: {{ include "konga.fullname" . }}-snapshot - {{- with .Values.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} diff --git a/packages/helm-charts/konga/templates/ingress.yaml b/packages/helm-charts/konga/templates/ingress.yaml deleted file mode 100644 index 9b51db33688..00000000000 --- a/packages/helm-charts/konga/templates/ingress.yaml +++ /dev/null @@ -1,39 +0,0 @@ -{{- if .Values.ingress.enabled -}} -{{- $fullName := include "konga.fullname" . -}} -apiVersion: extensions/v1beta1 -kind: Ingress -metadata: - name: {{ $fullName }} - labels: - app.kubernetes.io/name: {{ include "konga.name" . }} - helm.sh/chart: {{ include "konga.chart" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - {{- with .Values.ingress.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -spec: -{{- if .Values.ingress.tls }} - tls: - {{- range .Values.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} -{{- end }} - rules: - {{- range .Values.ingress.hosts }} - - host: {{ .host | quote }} - http: - paths: - {{- range .paths }} - - path: {{ . }} - backend: - serviceName: {{ $fullName }} - servicePort: http - {{- end }} - {{- end }} -{{- end }} diff --git a/packages/helm-charts/konga/templates/service.yaml b/packages/helm-charts/konga/templates/service.yaml deleted file mode 100644 index aa8cf7ba1ec..00000000000 --- a/packages/helm-charts/konga/templates/service.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ include "konga.fullname" . }} - labels: - app.kubernetes.io/name: {{ include "konga.name" . }} - helm.sh/chart: {{ include "konga.chart" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.service.port }} - targetPort: http - protocol: TCP - name: http - selector: - app.kubernetes.io/name: {{ include "konga.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} diff --git a/packages/helm-charts/konga/templates/tests/test-connection.yaml b/packages/helm-charts/konga/templates/tests/test-connection.yaml deleted file mode 100644 index 61bca6a257e..00000000000 --- a/packages/helm-charts/konga/templates/tests/test-connection.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: "{{ include "konga.fullname" . }}-test-connection" - labels: - app.kubernetes.io/name: {{ include "konga.name" . }} - helm.sh/chart: {{ include "konga.chart" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - annotations: - "helm.sh/hook": test-success -spec: - containers: - - name: wget - image: busybox - command: ['wget'] - args: ['{{ include "konga.fullname" . }}:{{ .Values.service.port }}'] - restartPolicy: Never diff --git a/packages/helm-charts/konga/values.yaml b/packages/helm-charts/konga/values.yaml deleted file mode 100644 index 1d4fbc33c14..00000000000 --- a/packages/helm-charts/konga/values.yaml +++ /dev/null @@ -1,93 +0,0 @@ -# Default values for konga. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -replicaCount: 1 - -image: - repository: pantsel/konga - tag: next - pullPolicy: IfNotPresent - -nameOverride: "" -fullnameOverride: "" - -service: - type: ClusterIP - port: 80 - -# Konga default configuration -config: -# port: 1337 - node_env: development -# ssl_key_path: -# ssl_crt_path: -# konga_hook_timeout: 60000 - db_adapter: postgres - # db_uri: - db_host: kong-postgresql - db_port: 5432 - db_user: kong - db_password: kong - db_database: kong -# db_pg_schema: public -# log_level: debug - token_secret: kong - konga_node_data: /opt/clabs/files/kong_node.data - # konga_user_data: - -# LDAP configuration for Konga -ldap: {} -# ldap: -# auth_provider: -# host: -# bind_dn: -# bind_pass: -# user_search_base: -# user_search_filter: -# user_attrs: -# group_search_base: -# group_search_filter: -# group_attrs: -# group_reg: -# attr_username: -# attr_firstname: -# attr_lastname: -# attr_email: - -# Ingress Configuration for Konga -ingress: - enabled: false - annotations: {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - hosts: - - host: chart-example.local - paths: [] - - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local - -resources: {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - -nodeSelector: {} - -tolerations: [] - -affinity: {} - -kong: - service: kong - admin_port: 8001 diff --git a/packages/helm-charts/odis/templates/signer-deployment.yaml b/packages/helm-charts/odis/templates/signer-deployment.yaml index a49fb358279..b6eb4dde19c 100644 --- a/packages/helm-charts/odis/templates/signer-deployment.yaml +++ b/packages/helm-charts/odis/templates/signer-deployment.yaml @@ -52,7 +52,6 @@ spec: {{ include "common.env-var" (dict "name" "DOMAINS_LATEST_KEY_VERSION" "dict" .Values.keystore "value_name" "domainsKeyLatestVersion") | indent 12 }} {{ include "common.env-var" (dict "name" "DOMAINS_API_ENABLED" "dict" .Values.api "value_name" "domainsAPIEnabled") | indent 12 }} {{ include "common.env-var" (dict "name" "PHONE_NUMBER_PRIVACY_API_ENABLED" "dict" .Values.api "value_name" "pnpAPIEnabled") | indent 12 }} -{{ include "common.env-var" (dict "name" "LEGACY_PHONE_NUMBER_PRIVACY_API_ENABLED" "dict" .Values.api "value_name" "legacyPnpAPIEnabled") | indent 12 }} - name: DB_PASSWORD valueFrom: secretKeyRef: diff --git a/packages/helm-charts/oracle-rbac/Chart.yaml b/packages/helm-charts/oracle-rbac/Chart.yaml index 9e47eb76691..4f10fc0d1a3 100644 --- a/packages/helm-charts/oracle-rbac/Chart.yaml +++ b/packages/helm-charts/oracle-rbac/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v1 -appVersion: "1.0" +appVersion: '1.0' description: A Helm chart to get the RBAC token needed by the oracle to reach the K8s API server name: oracle-rbac -version: 0.2.0 +version: 0.3.0 diff --git a/packages/helm-charts/oracle-rbac/templates/_helper.tpl b/packages/helm-charts/oracle-rbac/templates/_helper.tpl index d8c548e1fc4..75e25d060b7 100644 --- a/packages/helm-charts/oracle-rbac/templates/_helper.tpl +++ b/packages/helm-charts/oracle-rbac/templates/_helper.tpl @@ -2,6 +2,10 @@ {{- .Values.environment.name -}}-{{- .Values.environment.currencyPair | lower -}}-oracle-rbac-{{- .index -}} {{- end -}} +{{- define "secret-name" -}} +{{- .Values.environment.name -}}-{{- .Values.environment.currencyPair | lower -}}-oracle-rbac-secret-{{- .index -}} +{{- end -}} + {{- define "oracle-pod-name" -}} {{- .Values.environment.name -}}-{{- .Values.environment.currencyPair | lower -}}-oracle-{{- .index -}} {{- end -}} diff --git a/packages/helm-charts/oracle-rbac/templates/secret.yaml b/packages/helm-charts/oracle-rbac/templates/secret.yaml new file mode 100644 index 00000000000..b2bbda2cbad --- /dev/null +++ b/packages/helm-charts/oracle-rbac/templates/secret.yaml @@ -0,0 +1,11 @@ +{{ range $index, $e := until (.Values.oracle.replicas | int) }} +{{- $index_counter := (dict "Values" $.Values "index" $index) -}} +apiVersion: v1 +kind: Secret +type: kubernetes.io/service-account-token +metadata: + name: {{ template "secret-name" $index_counter }} + annotations: + kubernetes.io/service-account.name: {{ template "name" $index_counter }} +--- +{{ end }} diff --git a/packages/helm-charts/oracle/CELOXOF.yaml b/packages/helm-charts/oracle/CELOXOF.yaml new file mode 100644 index 00000000000..122fb468961 --- /dev/null +++ b/packages/helm-charts/oracle/CELOXOF.yaml @@ -0,0 +1,83 @@ +oracle: + currencyPair: CELOXOF + overrideOracleCount: 12 # At 5s block time, every client reports once per minute + aggregation: + mid: + maxExchangeVolumeShare: 1 + maxPercentageDeviation: 0.025 + maxPercentageBidAskSpread: 0.015 + metrics: + enabled: true + prometheusPort: 9090 + apiRequestTimeoutMs: 5000 + circuitBreakerPriceChangeThreshold: 0.25 + gasPriceMultiplier: 1.5 + priceSources: "[ + [ + {exchange: 'BINANCE', symbol: 'CELOUSDT', toInvert: false}, + {exchange: 'BINANCE', symbol: 'EURUSDT', toInvert: true}, + {exchange: 'ALPHAVANTAGE', symbol: 'EURXOF', toInvert: false, ignoreVolume: true} + ], + [ + {exchange: 'BINANCE', symbol: 'CELOUSDT', toInvert: false}, + {exchange: 'BINANCE', symbol: 'EURUSDT', toInvert: true}, + {exchange: 'CURRENCYAPI', symbol: 'EURXOF', toInvert: false, ignoreVolume: true} + ], + + [ + {exchange: 'COINBASE', symbol: 'CELOUSD', toInvert: false}, + {exchange: 'COINBASE', symbol: 'USDTUSD', toInvert: true}, + {exchange: 'COINBASE', symbol: 'USDTEUR', toInvert: false}, + {exchange: 'ALPHAVANTAGE', symbol: 'EURXOF', toInvert: false, ignoreVolume: true} + ], + [ + {exchange: 'COINBASE', symbol: 'CELOUSD', toInvert: false}, + {exchange: 'COINBASE', symbol: 'USDTUSD', toInvert: true}, + {exchange: 'COINBASE', symbol: 'USDTEUR', toInvert: false}, + {exchange: 'CURRENCYAPI', symbol: 'EURXOF', toInvert: false, ignoreVolume: true} + ], + + [ + {exchange: 'OKX', symbol: 'CELOUSDT', toInvert: false}, + {exchange: 'BITSTAMP', symbol: 'USDTEUR', toInvert: false}, + {exchange: 'ALPHAVANTAGE', symbol: 'EURXOF', toInvert: false, ignoreVolume: true} + ], + [ + {exchange: 'OKX', symbol: 'CELOUSDT', toInvert: false}, + {exchange: 'BITSTAMP', symbol: 'USDTEUR', toInvert: false}, + {exchange: 'CURRENCYAPI', symbol: 'EURXOF', toInvert: false, ignoreVolume: true} + ], + + [ + {exchange: 'KUCOIN', symbol: 'CELOUSDT', toInvert: false}, + {exchange: 'KRAKEN', symbol: 'USDTEUR', toInvert: false}, + {exchange: 'ALPHAVANTAGE', symbol: 'EURXOF', toInvert: false, ignoreVolume: true} + ], + [ + {exchange: 'KUCOIN', symbol: 'CELOUSDT', toInvert: false}, + {exchange: 'KRAKEN', symbol: 'USDTEUR', toInvert: false}, + {exchange: 'CURRENCYAPI', symbol: 'EURXOF', toInvert: false, ignoreVolume: true} + ], + + [ + {exchange: 'BITGET', symbol: 'CELOUSDT', toInvert: false}, + {exchange: 'BITGET', symbol: 'USDTEUR', toInvert: false}, + {exchange: 'ALPHAVANTAGE', symbol: 'EURXOF', toInvert: false, ignoreVolume: true} + ], + [ + {exchange: 'BITGET', symbol: 'CELOUSDT', toInvert: false}, + {exchange: 'BITGET', symbol: 'USDTEUR', toInvert: false}, + {exchange: 'CURRENCYAPI', symbol: 'EURXOF', toInvert: false, ignoreVolume: true} + ] + ]" + # Additional sources missing adapters [ + # {exchange: 'UPBIT', symbol: 'CELOKRW', toInvert: false}, + # {exchange: 'UPBIT', symbol: 'BTCKRW', toInvert: true}, + # {exchange: 'KRAKEN', symbol: 'BTCEUR', toInvert: false}, + # {exchange: 'ALPHAVANTAGE', symbol: 'EURXOF', toInvert: false, ignoreVolume: true} + # ] + minPriceSourceCount: 6 + reportStrategy: BLOCK_BASED + reporter: + blockBased: + minReportPriceChangeThreshold: 0.005 diff --git a/packages/helm-charts/oracle/Chart.yaml b/packages/helm-charts/oracle/Chart.yaml index f8011e8d942..01ca063f2e0 100644 --- a/packages/helm-charts/oracle/Chart.yaml +++ b/packages/helm-charts/oracle/Chart.yaml @@ -1,9 +1,9 @@ apiVersion: v1 -appVersion: "1.0" +appVersion: '1.0' description: A Helm chart for the oracle client name: oracle -version: 0.2.0 +version: 0.2.1 dependencies: - name: common repository: oci://us-west1-docker.pkg.dev/devopsre/clabs-public-oci - version: 0.2.0 \ No newline at end of file + version: 0.2.0 diff --git a/packages/helm-charts/oracle/EUROCEUR.yaml b/packages/helm-charts/oracle/EUROCEUR.yaml new file mode 100644 index 00000000000..ee365067a3b --- /dev/null +++ b/packages/helm-charts/oracle/EUROCEUR.yaml @@ -0,0 +1,31 @@ +oracle: + currencyPair: EUROCEUR + aggregation: + mid: + maxExchangeVolumeShare: 1 + maxPercentageDeviation: 0.01 + maxPercentageBidAskSpread: 0.005 + metrics: + enabled: true + prometheusPort: 9090 + apiRequestTimeoutMs: 5000 + circuitBreakerPriceChangeThreshold: 0.25 + gasPriceMultiplier: 1.5 + priceSources: "[ + [ + {exchange: 'COINBASE', symbol: 'EUROCEUR', toInvert: false} + ], + [ + {exchange: 'COINBASE', symbol: 'EUROCUSD', toInvert: false}, + {exchange: 'COINBASE', symbol: 'USDTUSD', toInvert: true}, + {exchange: 'COINBASE', symbol: 'USDTEUR', toInvert: false} + ], + [ + {exchange: 'BITSTAMP', symbol: 'EUROCEUR', toInvert: false}, + ] + ]" + minPriceSourceCount: 2 + reportStrategy: BLOCK_BASED + reporter: + blockBased: + minReportPriceChangeThreshold: 0.0005 # 0.05% diff --git a/packages/helm-charts/oracle/EUROCXOF.yaml b/packages/helm-charts/oracle/EUROCXOF.yaml new file mode 100644 index 00000000000..b09447b9a5d --- /dev/null +++ b/packages/helm-charts/oracle/EUROCXOF.yaml @@ -0,0 +1,51 @@ +oracle: + currencyPair: EUROCXOF + overrideOracleCount: 12 # At 5s block time, every client reports once per minute + aggregation: + mid: + maxExchangeVolumeShare: 1 + maxPercentageDeviation: 0.01 + maxPercentageBidAskSpread: 0.005 + metrics: + enabled: true + prometheusPort: 9090 + apiRequestTimeoutMs: 5000 + circuitBreakerPriceChangeThreshold: 0.25 + gasPriceMultiplier: 1.5 + priceSources: "[ + [ + {exchange: 'COINBASE', symbol: 'EUROCEUR', toInvert: false}, + {exchange: 'ALPHAVANTAGE', symbol: 'EURXOF', toInvert: false, ignoreVolume: true} + ], + [ + {exchange: 'COINBASE', symbol: 'EUROCEUR', toInvert: false}, + {exchange: 'CURRENCYAPI', symbol: 'EURXOF', toInvert: false, ignoreVolume: true} + ], + + [ + {exchange: 'COINBASE', symbol: 'EUROCUSD', toInvert: false}, + {exchange: 'COINBASE', symbol: 'USDTUSD', toInvert: true}, + {exchange: 'COINBASE', symbol: 'USDTEUR', toInvert: false}, + {exchange: 'ALPHAVANTAGE', symbol: 'EURXOF', toInvert: false, ignoreVolume: true} + ], + [ + {exchange: 'COINBASE', symbol: 'EUROCUSD', toInvert: false}, + {exchange: 'COINBASE', symbol: 'USDTUSD', toInvert: true}, + {exchange: 'COINBASE', symbol: 'USDTEUR', toInvert: false}, + {exchange: 'CURRENCYAPI', symbol: 'EURXOF', toInvert: false, ignoreVolume: true} + ], + + [ + {exchange: 'BITSTAMP', symbol: 'EUROCEUR', toInvert: false}, + {exchange: 'ALPHAVANTAGE', symbol: 'EURXOF', toInvert: false, ignoreVolume: true} + ], + [ + {exchange: 'BITSTAMP', symbol: 'EUROCEUR', toInvert: false}, + {exchange: 'CURRENCYAPI', symbol: 'EURXOF', toInvert: false, ignoreVolume: true} + ] + ]" + minPriceSourceCount: 4 + reportStrategy: BLOCK_BASED + reporter: + blockBased: + minReportPriceChangeThreshold: 0.0005 # 0.05% diff --git a/packages/helm-charts/oracle/EURXOF.yaml b/packages/helm-charts/oracle/EURXOF.yaml new file mode 100644 index 00000000000..68cc0ab170b --- /dev/null +++ b/packages/helm-charts/oracle/EURXOF.yaml @@ -0,0 +1,27 @@ +oracle: + currencyPair: EURXOF + overrideOracleCount: 12 # At 5s block time, every client reports once per minute + aggregation: + mid: + maxExchangeVolumeShare: 1 + maxPercentageDeviation: 0.01 + maxPercentageBidAskSpread: 0.005 + metrics: + enabled: true + prometheusPort: 9090 + apiRequestTimeoutMs: 5000 + circuitBreakerPriceChangeThreshold: 0.25 + gasPriceMultiplier: 1.5 + priceSources: "[ + [ + {exchange: 'ALPHAVANTAGE', symbol: 'EURXOF', toInvert: false} + ], + [ + {exchange: 'CURRENCYAPI', symbol: 'EURXOF', toInvert: false} + ] + ]" + minPriceSourceCount: 2 + reportStrategy: BLOCK_BASED + reporter: + blockBased: + minReportPriceChangeThreshold: 0.0005 # 0.05% diff --git a/packages/helm-charts/oracle/USDCBRL.yaml b/packages/helm-charts/oracle/USDCBRL.yaml index d605cf0d68d..94080e764f5 100644 --- a/packages/helm-charts/oracle/USDCBRL.yaml +++ b/packages/helm-charts/oracle/USDCBRL.yaml @@ -19,10 +19,6 @@ oracle: {exchange: 'KRAKEN', symbol: 'USDCUSD', toInvert: false}, {exchange: 'BITSO', symbol: 'USDBRL', toInvert: false } ], - [ - {exchange: 'BINANCEUS', symbol: 'USDCUSD', toInvert: false}, - {exchange: 'BITSO', symbol: 'USDBRL', toInvert: false } - ], [ {exchange: 'COINBASE', symbol: 'USDTUSDC', toInvert: true}, {exchange: 'BINANCE', symbol: 'USDTBRL', toInvert: false } diff --git a/packages/helm-charts/oracle/USDCEUR.yaml b/packages/helm-charts/oracle/USDCEUR.yaml index 431971a2418..112008c9edb 100644 --- a/packages/helm-charts/oracle/USDCEUR.yaml +++ b/packages/helm-charts/oracle/USDCEUR.yaml @@ -27,10 +27,6 @@ oracle: {exchange: 'KRAKEN', symbol: 'USDCUSD', toInvert: false}, {exchange: 'KRAKEN', symbol: 'EURUSD', toInvert: true} ], - [ - {exchange: 'BINANCEUS', symbol: 'USDCUSD', toInvert: false}, - {exchange: 'KRAKEN', symbol: 'EURUSD', toInvert: true} - ], ]" minPriceSourceCount: 2 reportStrategy: BLOCK_BASED diff --git a/packages/helm-charts/oracle/USDCUSD.yaml b/packages/helm-charts/oracle/USDCUSD.yaml index 5a747b4b684..bdf4cdfa3ba 100644 --- a/packages/helm-charts/oracle/USDCUSD.yaml +++ b/packages/helm-charts/oracle/USDCUSD.yaml @@ -3,7 +3,7 @@ oracle: aggregation: mid: maxExchangeVolumeShare: 1 - maxPercentageDeviation: 0.01 + maxPercentageDeviation: 0.005 maxPercentageBidAskSpread: 0.005 metrics: enabled: true @@ -11,23 +11,40 @@ oracle: apiRequestTimeoutMs: 5000 circuitBreakerPriceChangeThreshold: 0.25 gasPriceMultiplier: 1.5 - priceSources: "[ + priceSources: "[ + [ + {exchange: 'BINANCE', symbol: 'USDCUSDT', toInvert: false}, + {exchange: 'KRAKEN', symbol: 'USDTUSD', toInvert: false } + ], [ {exchange: 'KRAKEN', symbol: 'USDCUSD', toInvert: false} ], [ - {exchange: 'BINANCEUS', symbol: 'USDCUSD', toInvert: false} + {exchange: 'BITSTAMP', symbol: 'USDCUSD', toInvert: false} ], [ {exchange: 'COINBASE', symbol: 'USDTUSDC', toInvert: true}, {exchange: 'COINBASE', symbol: 'USDTUSD', toInvert: false} ], [ - {exchange: 'WHITEBIT', symbol: 'USDCUSDT', toInvert: false}, + {exchange: 'OKX', symbol: 'USDCUSDT', toInvert: false}, + {exchange: 'BITSTAMP', symbol: 'USDTUSD', toInvert: false} + ], + [ + {exchange: 'BITGET', symbol: 'USDTUSDC', toInvert: true}, + {exchange: 'KRAKEN', symbol: 'USDTUSD', toInvert: false} + ], + [ + {exchange: 'KUCOIN', symbol: 'USDCUSDT', toInvert: false}, {exchange: 'COINBASE', symbol: 'USDTUSD', toInvert: false} ] ]" - minPriceSourceCount: 2 + # Additional sources missing adapters + # [ + # {exchange: 'Bybit', symbol: 'USDTUSDC', toInvert: true}, + # {exchange: 'Kraken', symbol: 'USDTUSD', toInvert: false} + # ], + minPriceSourceCount: 5 reportStrategy: BLOCK_BASED reporter: blockBased: diff --git a/packages/helm-charts/oracle/templates/_helper.tpl b/packages/helm-charts/oracle/templates/_helper.tpl index d2ef8a2b9be..ece6db7dbb1 100644 --- a/packages/helm-charts/oracle/templates/_helper.tpl +++ b/packages/helm-charts/oracle/templates/_helper.tpl @@ -49,4 +49,7 @@ The name of the pkey secret */}} {{- define "pkey-secret-name" -}} pkey-secret-{{- .Values.oracle.currencyPair | lower -}} -{{- end -}} \ No newline at end of file +{{- end -}} +{{- define "api-keys-secret-name" -}} +api-keys-{{- .Values.oracle.currencyPair | lower -}} +{{- end -}} diff --git a/packages/helm-charts/oracle/templates/api-keys-secret.yaml b/packages/helm-charts/oracle/templates/api-keys-secret.yaml new file mode 100644 index 00000000000..db2d40d782a --- /dev/null +++ b/packages/helm-charts/oracle/templates/api-keys-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "api-keys-secret-name" . }} + labels: +{{ include "labels" . | indent 4 }} +type: Opaque +data: + api_keys: {{ .Values.oracle.api_keys | b64enc }} diff --git a/packages/helm-charts/oracle/templates/statefulset.yaml b/packages/helm-charts/oracle/templates/statefulset.yaml index de6c0790911..423881c0e0b 100644 --- a/packages/helm-charts/oracle/templates/statefulset.yaml +++ b/packages/helm-charts/oracle/templates/statefulset.yaml @@ -115,6 +115,11 @@ spec: valueFrom: fieldRef: fieldPath: metadata.name + - name: API_KEYS + valueFrom: + secretKeyRef: + key: api_keys + name: {{ template "api-keys-secret-name" . }} {{ include "common.env-var" (dict "name" "API_REQUEST_TIMEOUT" "dict" .Values.oracle "value_name" "apiRequestTimeoutMs" "optional" true) | indent 8 }} {{ include "common.env-var" (dict "name" "AZURE_HSM_INIT_TRY_COUNT" "dict" .Values.oracle.azureHsm "value_name" "initTryCount") | indent 8 }} {{ include "common.env-var" (dict "name" "AZURE_HSM_INIT_MAX_RETRY_BACKOFF_MS" "dict" .Values.oracle.azureHsm "value_name" "initMaxRetryBackoffMs") | indent 8 }} diff --git a/packages/helm-charts/prometheus-stackdriver/values.yaml b/packages/helm-charts/prometheus-stackdriver/values.yaml index 225833bcea1..201a465f94b 100644 --- a/packages/helm-charts/prometheus-stackdriver/values.yaml +++ b/packages/helm-charts/prometheus-stackdriver/values.yaml @@ -136,7 +136,6 @@ remote_write: |coredns.*\ |cortex.*\ |csi_operations.*\ - |eksportisto_step_duration_bucket\ |erlang_.+\ |etcd_.+\ |go_gc_.*\ diff --git a/packages/helm-charts/testnet/templates/_helpers.tpl b/packages/helm-charts/testnet/templates/_helpers.tpl index 350721e6152..2764475eebd 100644 --- a/packages/helm-charts/testnet/templates/_helpers.tpl +++ b/packages/helm-charts/testnet/templates/_helpers.tpl @@ -227,18 +227,20 @@ spec: echo "Generating proxy enode url pair for proxy $PROXY_INDEX" PROXY_INTERNAL_IP_ENV_VAR={{ $.Release.Namespace | upper }}_VALIDATORS_${RID}_PROXY_INTERNAL_${PROXY_INDEX}_SERVICE_HOST echo "PROXY_INTERNAL_IP_ENV_VAR=$PROXY_INTERNAL_IP_ENV_VAR" -PROXY_INTERNAL_IP=$(eval "echo \\${${PROXY_INTERNAL_IP_ENV_VAR}}") +PROXY_INTERNAL_IP=`eval "echo \\${${PROXY_INTERNAL_IP_ENV_VAR}}"` # If $PROXY_IPS is not empty, then we use the IPs from there. Otherwise, # we use the IP address of the proxy internal service if [ ! -z $PROXY_IPS ]; then echo "Proxy external IP from PROXY_IPS=$PROXY_IPS: " - PROXY_EXTERNAL_IP=$(echo -n $PROXY_IPS | cut -d '/' -f $((PROXY_INDEX + 1))) + PROXY_EXTERNAL_IP=`echo -n $PROXY_IPS | cut -d '/' -f $((PROXY_INDEX + 1))` else PROXY_EXTERNAL_IP=$PROXY_INTERNAL_IP fi +echo "Proxy internal IP: $PROXY_INTERNAL_IP" +echo "Proxy external IP: $PROXY_EXTERNAL_IP" # Proxy key index to allow for a high number of proxies per validator without overlap PROXY_KEY_INDEX=$(( ($RID * 10000) + $PROXY_INDEX )) -PROXY_ENODE_ADDRESS=$(celotooljs.sh generate public-key --mnemonic "$MNEMONIC" --accountType proxy --index $PROXY_KEY_INDEX) +PROXY_ENODE_ADDRESS=`celotooljs.sh generate public-key --mnemonic "$MNEMONIC" --accountType proxy --index $PROXY_KEY_INDEX` PROXY_INTERNAL_ENODE=enode://${PROXY_ENODE_ADDRESS}@${PROXY_INTERNAL_IP}:30503 PROXY_EXTERNAL_ENODE=enode://${PROXY_ENODE_ADDRESS}@${PROXY_EXTERNAL_IP}:30303 echo "Proxy internal enode: $PROXY_INTERNAL_ENODE" diff --git a/packages/metadata-crawler/CHANGELOG.md b/packages/metadata-crawler/CHANGELOG.md new file mode 100644 index 00000000000..e62008db222 --- /dev/null +++ b/packages/metadata-crawler/CHANGELOG.md @@ -0,0 +1,29 @@ +# @celo/metadata-crawler + +## 0.0.2 + +### Patch Changes + +- Updated dependencies [d48c68afc] +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [d48c68afc] + - @celo/contractkit@5.1.0 + - @celo/connect@5.1.0 + - @celo/utils@5.0.5 + +## 0.0.2-beta.0 + +### Patch Changes + +- Updated dependencies [d48c68afc] +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [d48c68afc] + - @celo/contractkit@5.1.0-beta.0 + - @celo/connect@5.1.0-beta.0 + - @celo/utils@5.0.5-beta.0 diff --git a/packages/metadata-crawler/package.json b/packages/metadata-crawler/package.json index abb633d682d..e129b76df4b 100644 --- a/packages/metadata-crawler/package.json +++ b/packages/metadata-crawler/package.json @@ -1,6 +1,6 @@ { "name": "@celo/metadata-crawler", - "version": "0.0.1", + "version": "0.0.2", "description": "Celo metadata crawler", "main": "index.js", "types": "./lib/index.d.ts", @@ -9,9 +9,9 @@ "homepage": "https://github.com/celo-org/celo-monorepo/tree/master/packages/metadata-crawler", "repository": "https://github.com/celo-org/celo-monorepo/tree/master/packages/metadata-crawler", "dependencies": { - "@celo/connect": "4.1.1-dev", - "@celo/contractkit": "4.1.1-dev", - "@celo/utils": "4.1.1-dev", + "@celo/connect": "^5.1.0", + "@celo/contractkit": "^5.1.0", + "@celo/utils": "^5.0.5", "@types/pg": "^7.14.3", "bunyan": "1.8.12", "bunyan-gke-stackdriver": "0.1.2", @@ -19,7 +19,7 @@ "dotenv": "^8.2.0", "googleapis": "^39.2.0", "pg": "^7.18.0", - "ts-node": "^8.5.4", + "ts-node": "^10.9.1", "web3": "1.10.0" }, "devDependencies": { diff --git a/packages/phone-number-privacy/README.md b/packages/phone-number-privacy/README.md deleted file mode 100644 index 1cef4c0a079..00000000000 --- a/packages/phone-number-privacy/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Contents - -Home for the Oblivious Decentralized Identifier Service (ODIS), formerly PGPNP (Pretty Good Phone Number Privacy). diff --git a/packages/phone-number-privacy/combiner/.env b/packages/phone-number-privacy/combiner/.env deleted file mode 100644 index 14bedd678cf..00000000000 --- a/packages/phone-number-privacy/combiner/.env +++ /dev/null @@ -1,8 +0,0 @@ -# Options: json, human (default), stackdriver -LOG_FORMAT=stackdriver -# Options: fatal, error, warn, info (default), debug, trace -LOG_LEVEL=trace -SERVICE_NAME='odis-combiner' -# For e2e Tests -ODIS_BLOCKCHAIN_PROVIDER=https://alfajores-forno.celo-testnet.org -CONTEXT_NAME='alfajores' diff --git a/packages/phone-number-privacy/combiner/.firebaserc b/packages/phone-number-privacy/combiner/.firebaserc deleted file mode 100644 index b8893af884b..00000000000 --- a/packages/phone-number-privacy/combiner/.firebaserc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "projects": { - "default": "celo-phone-number-privacy" - } -} diff --git a/packages/phone-number-privacy/combiner/.gitignore b/packages/phone-number-privacy/combiner/.gitignore deleted file mode 100644 index f929bc6f851..00000000000 --- a/packages/phone-number-privacy/combiner/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -dist/ - -# Firebase cache -.firebase/ - diff --git a/packages/phone-number-privacy/combiner/README.md b/packages/phone-number-privacy/combiner/README.md deleted file mode 100644 index 5c9909368f4..00000000000 --- a/packages/phone-number-privacy/combiner/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# ODIS Combiner - -A firebase function that orchestrates distributed BLS threshold signing with the set of ODIS signers. - -## DB Migrations - -### Add migration file - -To update the combiner DB schema, first run `yarn db:migrate:make ` to create a new migrations file. Then, fill in the new migration file as needed using the previous migration files as references. - -### Whitelist your IP address - -Go to [https://console.cloud.google.com/sql/instances?authuser=1&folder=&organizationId=&project=celo-phone-number-privacy] and navigate to the desired workspace and db instance. Then, add your IP address under `Connections -> Authorized networks`. - -Remember to remove your IP address from the whitelist when finished. - -### Add db credentials to config.ts - -Run the command `yarn config:get:` to fetch the necessary db credentials and add them to `src/config.ts` under the `DEV_MODE` section. DO NOT COMMIT THESE CREDENTIALS TO GITHUB. - -Note: When you fill in the `host` field you may need to use the database's public IP, which can be found in the `Overview` section under the link above. - -### Run migrations - -Always run migrations in staging first and ensure all e2e tests pass before migrating in alfajores and mainnet. - -Run `yarn db:migrate:` diff --git a/packages/phone-number-privacy/combiner/firebase.json b/packages/phone-number-privacy/combiner/firebase.json deleted file mode 100644 index 028b3af2579..00000000000 --- a/packages/phone-number-privacy/combiner/firebase.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "functions": { - "source": ".", - "predeploy": ["yarn run lint", "yarn run build"] - } -} diff --git a/packages/phone-number-privacy/combiner/index.d.ts b/packages/phone-number-privacy/combiner/index.d.ts deleted file mode 100644 index 102dc17cf17..00000000000 --- a/packages/phone-number-privacy/combiner/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare module 'bunyan-debug-stream' diff --git a/packages/phone-number-privacy/combiner/jest.config.js b/packages/phone-number-privacy/combiner/jest.config.js deleted file mode 100644 index 6c79c8222c0..00000000000 --- a/packages/phone-number-privacy/combiner/jest.config.js +++ /dev/null @@ -1,15 +0,0 @@ -const { nodeFlakeTracking } = require('@celo/flake-tracker/src/jest/config.js') - -module.exports = { - preset: 'ts-jest', - ...nodeFlakeTracking, - setupFilesAfterEnv: [...nodeFlakeTracking.setupFilesAfterEnv], - // setupFilesAfterEnv: ['/jest_setup.ts', ...nodeFlakeTracking.setupFilesAfterEnv], - coverageReporters: [['lcov', { projectRoot: '../../../' }], 'text'], - collectCoverageFrom: ['./src/**'], - coverageThreshold: { - global: { - lines: 80, - }, - }, -} diff --git a/packages/phone-number-privacy/combiner/jest_setup.ts b/packages/phone-number-privacy/combiner/jest_setup.ts deleted file mode 100644 index d9933443acc..00000000000 --- a/packages/phone-number-privacy/combiner/jest_setup.ts +++ /dev/null @@ -1,6 +0,0 @@ -if (typeof window !== 'object') { - // @ts-ignore - global.window = global - // @ts-ignore - global.window.navigator = {} -} diff --git a/packages/phone-number-privacy/combiner/package.json b/packages/phone-number-privacy/combiner/package.json deleted file mode 100644 index c1517359c8b..00000000000 --- a/packages/phone-number-privacy/combiner/package.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "name": "@celo/phone-number-privacy-combiner", - "version": "3.0.0-dev", - "description": "Orchestrates and combines threshold signatures for use in ODIS", - "author": "Celo", - "license": "Apache-2.0", - "main": "dist/index.js", - "scripts": { - "dev": "yarn build && firebase serve --only functions", - "deploy": "yarn build && firebase deploy --only functions:combiner", - "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", - "config:get:staging": "firebase functions:config:get --project celo-phone-number-privacy-stg", - "config:get:alfajores": "firebase functions:config:get --project celo-phone-number-privacy", - "config:get:mainnet": "firebase functions:config:get --project celo-pgpnp-mainnet", - "config:set:staging": "firebase functions:config:set --project celo-phone-number-privacy-stg", - "config:set:alfajores": "firebase functions:config:set --project celo-phone-number-privacy", - "config:set:mainnet": "firebase functions:config:set --project celo-pgpnp-mainnet", - "clean": "tsc -b . --clean", - "build": "tsc -b .", - "lint": "tslint --project .", - "test": "jest --runInBand --testPathIgnorePatterns test/end-to-end", - "test:coverage": "yarn test --coverage", - "test:integration": "jest --runInBand test/integration", - "test:e2e": "FLAKEY=true jest test/end-to-end --verbose", - "test:e2e:staging": "FLAKEY=true CONTEXT_NAME=staging yarn test:e2e", - "test:e2e:alfajores": "FLAKEY=true CONTEXT_NAME=alfajores yarn test:e2e" - }, - "dependencies": { - "@celo/contractkit": "^4.1.1-dev", - "@celo/phone-number-privacy-common": "^3.0.0-dev", - "@celo/identity": "^4.1.1-dev", - "@celo/encrypted-backup": "^4.1.1-dev", - "@celo/identity-prev": "npm:@celo/identity@1.2.0", - "@celo/poprf": "^0.1.9", - "@types/bunyan": "^1.8.8", - "blind-threshold-bls": "https://github.com/celo-org/blind-threshold-bls-wasm#e1e2f8a", - "dotenv": "^8.2.0", - "express": "^4.17.1", - "firebase-admin": "^9.12.0", - "firebase-functions": "^3.15.7", - "knex": "^2.1.0", - "node-fetch": "^2.6.9", - "pg": "^8.2.1", - "uuid": "^7.0.3" - }, - "devDependencies": { - "@celo/utils": "^4.1.1-dev", - "@celo/phone-utils": "^4.1.1-dev", - "@types/express": "^4.17.6", - "@types/supertest": "^2.0.12", - "@types/uuid": "^7.0.3", - "firebase-functions-test": "^0.3.3", - "firebase-tools": "9.20.0" - }, - "peerDependencies": { - "@celo/phone-number-privacy-signer": "^2.0.2", - "@celo/flake-tracker": "0.0.1-dev" - }, - "engines": { - "node": ">=14" - } -} diff --git a/packages/phone-number-privacy/combiner/src/common/action.ts b/packages/phone-number-privacy/combiner/src/common/action.ts deleted file mode 100644 index 2bf519cb861..00000000000 --- a/packages/phone-number-privacy/combiner/src/common/action.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { OdisRequest } from '@celo/phone-number-privacy-common' -import { IO } from './io' -import { Session } from './session' - -export interface Action { - readonly io: IO - perform(session: Session): Promise -} diff --git a/packages/phone-number-privacy/combiner/src/common/combine.ts b/packages/phone-number-privacy/combiner/src/common/combine.ts deleted file mode 100644 index d2fd2d8754f..00000000000 --- a/packages/phone-number-privacy/combiner/src/common/combine.ts +++ /dev/null @@ -1,175 +0,0 @@ -import { - ErrorMessage, - OdisRequest, - OdisResponse, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import { Response as FetchResponse } from 'node-fetch' -import { PerformanceObserver } from 'perf_hooks' -import { OdisConfig } from '../config' -import { Action } from './action' -import { IO } from './io' -import { Session } from './session' - -export interface Signer { - url: string - fallbackUrl?: string -} - -export abstract class CombineAction implements Action { - readonly signers: Signer[] - public constructor(readonly config: OdisConfig, readonly io: IO) { - this.signers = JSON.parse(config.odisServices.signers) - } - - abstract combine(session: Session): void - - async perform(session: Session) { - await this.distribute(session) - this.combine(session) - } - - async distribute(session: Session): Promise { - 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) => { - session.logger.info( - { latency: entry, signer: entry.name }, - 'Signer response latency measured' - ) - }) - }) - obs.observe({ entryTypes: ['measure'], buffered: false }) - - const timeout = setTimeout(() => { - session.timedOut = true - session.abort.abort() - }, this.config.odisServices.timeoutMilliSeconds) - - // Forward request to signers - // An unexpected error in handling the result for one signer should not - // block a threshold of correct responses, but should be logged. - await Promise.all( - this.signers.map(async (signer) => { - try { - await this.forwardToSigner(signer, session) - } catch (err) { - session.logger.error({ - signer: signer.url, - message: 'Unexpected error caught while distributing request to signer', - err, - }) - } - }) - ) - // TODO Resolve race condition where a session can both receive a successful - // response in time and be aborted - - clearTimeout(timeout) - // DO NOT call performance.clearMarks() as this also deletes marks used to - // measure e2e combiner latency. - obs.disconnect() - } - - protected async forwardToSigner(signer: Signer, session: Session): Promise { - let signerFetchResult: FetchResponse | undefined - try { - signerFetchResult = await this.io.fetchSignerResponseWithFallback(signer, session) - session.logger.info({ - message: 'Received signerFetchResult', - signer: signer.url, - status: signerFetchResult.status, - }) - } catch (err) { - session.logger.debug({ err, signer: signer.url, message: 'signer request failure' }) - if (err instanceof Error && err.name === 'AbortError' && session.abort.signal.aborted) { - if (session.timedOut) { - session.logger.error({ signer }, ErrorMessage.TIMEOUT_FROM_SIGNER) - } else { - session.logger.info({ signer }, WarningMessage.CANCELLED_REQUEST_TO_SIGNER) - } - } else { - // Logging the err & message simultaneously fails to log the message in some cases - session.logger.error({ signer }, ErrorMessage.SIGNER_REQUEST_ERROR) - session.logger.error({ signer, err }) - } - } - return this.handleFetchResult(signer, session, signerFetchResult) - } - - protected async handleFetchResult( - signer: Signer, - session: Session, - signerFetchResult?: FetchResponse - ): Promise { - if (signerFetchResult?.ok) { - try { - // Throws if response is not actually successful - await this.receiveSuccess(signerFetchResult, signer.url, session) - return - } catch (err) { - session.logger.error(err) - } - } - if (signerFetchResult) { - session.logger.info({ - message: 'Received signerFetchResult on unsuccessful signer response', - res: await signerFetchResult.text(), - status: signerFetchResult.status, - signer: signer.url, - }) - } - return this.addFailureToSession(signer, signerFetchResult?.status, session) - } - - protected async receiveSuccess( - signerFetchResult: FetchResponse, - url: string, - session: Session - ): Promise> { - if (!signerFetchResult.ok) { - throw new Error(`Implementation Error: receiveSuccess should only receive 'OK' responses`) - } - const { status } = signerFetchResult - const data: string = await signerFetchResult.text() - session.logger.info({ signer: url, res: data, status }, `received 'OK' response from signer`) - const signerResponse: OdisResponse = this.io.validateSignerResponse( - data, - url, - session.logger - ) - if (!signerResponse.success) { - session.logger.error( - { err: signerResponse.error, signer: url, status }, - `Signer request to ${url + this.io.signerEndpoint} failed with 'OK' status` - ) - throw new Error(ErrorMessage.SIGNER_RESPONSE_FAILED_WITH_OK_STATUS) - } - session.logger.info({ signer: url }, `Signer request successful`) - session.responses.push({ url, res: signerResponse, status }) - return signerResponse - } - - private addFailureToSession(signer: Signer, errorCode: number | undefined, session: Session) { - session.logger.warn( - `Received failure from ${session.failedSigners.size}/${this.signers.length} signers` - ) - // Tracking failed request count via signer url prevents - // double counting the same failed request by mistake - session.failedSigners.add(signer.url) - if (errorCode) { - session.incrementErrorCodeCount(errorCode) - } - const { threshold } = session.keyVersionInfo - if (this.signers.length - session.failedSigners.size < threshold) { - session.logger.warn('Not possible to reach a threshold of signer responses. Failing fast') - session.abort.abort() - } - } -} diff --git a/packages/phone-number-privacy/combiner/src/common/controller.ts b/packages/phone-number-privacy/combiner/src/common/controller.ts deleted file mode 100644 index 7726bebad2a..00000000000 --- a/packages/phone-number-privacy/combiner/src/common/controller.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { ErrorMessage, OdisRequest, OdisResponse } from '@celo/phone-number-privacy-common' -import { Request, Response } from 'express' -import { Action } from './action' - -export class Controller { - constructor(readonly action: Action) {} - - public async handle( - request: Request<{}, {}, unknown>, - response: Response> - ): Promise { - try { - const session = await this.action.io.init(request, response) - if (session) { - await this.action.perform(session) - } - } catch (err) { - response.locals.logger.error( - { error: err }, - `Unknown error in handler for ${this.action.io.endpoint}` - ) - this.action.io.sendFailure(ErrorMessage.UNKNOWN_ERROR, 500, response) - } - } -} diff --git a/packages/phone-number-privacy/combiner/src/common/crypto-clients/bls-crypto-client.ts b/packages/phone-number-privacy/combiner/src/common/crypto-clients/bls-crypto-client.ts deleted file mode 100644 index 40a2d8798aa..00000000000 --- a/packages/phone-number-privacy/combiner/src/common/crypto-clients/bls-crypto-client.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { ErrorMessage } from '@celo/phone-number-privacy-common' -import threshold_bls from 'blind-threshold-bls' -import Logger from 'bunyan' -import { CryptoClient, ServicePartialSignature } from './crypto-client' - -function flattenSigsArray(sigs: Uint8Array[]) { - return Uint8Array.from(sigs.reduce((a, b) => a.concat(Array.from(b)), [] as any)) -} -export class BLSCryptographyClient extends CryptoClient { - // Signatures can be verified server-side without knowledge of the blinding factor - private verifiedSignatures: ServicePartialSignature[] = [] - - protected get allSignaturesLength(): number { - return this.unverifiedSignatures.length + this.verifiedSignatures.length - } - - private get allSignatures(): Uint8Array { - const allSigs = this.verifiedSignatures.concat(this.unverifiedSignatures) - const sigBuffers = allSigs.map((response) => Buffer.from(response.signature, 'base64')) - return flattenSigsArray(sigBuffers) - } - - /* - * Computes the BLS signature for the blinded phone number. - * On error, logs and throws exception for not enough signatures, - * and drops the invalid signature for future requests using this instance. - */ - protected _combineBlindedSignatureShares(blindedMessage: string, logger: Logger): string { - // Optimistically attempt to combine unverified signatures - // If combination or verification fails, iterate through each signature and remove invalid ones - // We do this since partial signature verification incurs higher latencies - try { - const result = threshold_bls.combine(this.keyVersionInfo.threshold, this.allSignatures) - this.verifyCombinedSignature(blindedMessage, result, logger) - return Buffer.from(result).toString('base64') - } catch (error) { - logger.error(error) - // Verify each signature and remove invalid ones - // This logging will help us troubleshoot which signers are having issues - this.unverifiedSignatures.forEach((unverifiedSignature) => { - this.verifyPartialSignature(blindedMessage, unverifiedSignature, logger) - }) - this.clearUnverifiedSignatures() - throw new Error(ErrorMessage.NOT_ENOUGH_PARTIAL_SIGNATURES) - } - } - - private verifyCombinedSignature( - blindedMessage: string, - combinedSignature: Uint8Array, - logger: Logger - ) { - try { - // TODO: Address bad documentation in threshold-bls lib. - // Documentation should not specify that verifyBlindSignature verifies the - // signature after it has been unblinded. - threshold_bls.verifyBlindSignature( - Buffer.from(this.keyVersionInfo.pubKey, 'base64'), - Buffer.from(blindedMessage, 'base64'), - combinedSignature - ) - } catch (error) { - logger.error('Combined signature verification failed') - throw error - } - } - - private verifyPartialSignature( - blindedMessage: string, - unverifiedSignature: ServicePartialSignature, - logger: Logger - ) { - const sigBuffer = Buffer.from(unverifiedSignature.signature, 'base64') - if (this.isValidPartialSignature(sigBuffer, blindedMessage)) { - // We move it to the verified set so that we don't need to re-verify in the future - this.verifiedSignatures.push(unverifiedSignature) - } else { - logger.error({ url: unverifiedSignature.url }, ErrorMessage.VERIFY_PARITAL_SIGNATURE_ERROR) - } - } - - private clearUnverifiedSignatures() { - this.unverifiedSignatures = [] - } - - private isValidPartialSignature(signature: Buffer, blindedMessage: string) { - try { - threshold_bls.partialVerifyBlindSignature( - Buffer.from(this.keyVersionInfo.polynomial, 'hex'), - Buffer.from(blindedMessage, 'base64'), - signature - ) - return true - } catch { - return false - } - } -} 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 deleted file mode 100644 index 2fd8580420a..00000000000 --- a/packages/phone-number-privacy/combiner/src/common/crypto-clients/crypto-client.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { ErrorMessage, KeyVersionInfo } from '@celo/phone-number-privacy-common' -import Logger from 'bunyan' - -export interface ServicePartialSignature { - url: string - signature: string -} - -export abstract class CryptoClient { - protected unverifiedSignatures: ServicePartialSignature[] = [] - - constructor(protected readonly keyVersionInfo: KeyVersionInfo) {} - - /** - * Returns true if the number of valid signatures is enough to perform a combination - */ - public hasSufficientSignatures(): boolean { - return this.allSignaturesLength >= this.keyVersionInfo.threshold - } - - public addSignature(serviceResponse: ServicePartialSignature): void { - this.unverifiedSignatures.push(serviceResponse) - } - - /* - * Computes the signature for the blinded phone number using subclass-specific - * logic defined in _combineBlindedSignatureShares. - * Throws an exception if not enough valid signatures or on aggregation failure. - */ - public combineBlindedSignatureShares(blindedMessage: string, logger: Logger): string { - if (!this.hasSufficientSignatures()) { - const { threshold } = this.keyVersionInfo - logger.error( - { signatures: this.allSignaturesLength, required: threshold }, - ErrorMessage.NOT_ENOUGH_PARTIAL_SIGNATURES - ) - throw new Error( - `${ErrorMessage.NOT_ENOUGH_PARTIAL_SIGNATURES} ${this.allSignaturesLength}/${threshold}` - ) - } - return this._combineBlindedSignatureShares(blindedMessage, logger) - } - - /* - * Computes the signature for the blinded phone number. - * Must be implemented by subclass. - */ - protected abstract _combineBlindedSignatureShares(blindedMessage: string, logger: Logger): string - - /** - * Returns total number of signatures received; must be implemented by subclass. - */ - protected abstract get allSignaturesLength(): number -} diff --git a/packages/phone-number-privacy/combiner/src/common/crypto-clients/domain-crypto-client.ts b/packages/phone-number-privacy/combiner/src/common/crypto-clients/domain-crypto-client.ts deleted file mode 100644 index 22a1256d35e..00000000000 --- a/packages/phone-number-privacy/combiner/src/common/crypto-clients/domain-crypto-client.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { ErrorMessage, KeyVersionInfo, PoprfCombiner } from '@celo/phone-number-privacy-common' -import Logger from 'bunyan' -import { CryptoClient } from './crypto-client' - -export class DomainCryptoClient extends CryptoClient { - private poprfCombiner: PoprfCombiner - - constructor(protected readonly keyVersionInfo: KeyVersionInfo) { - super(keyVersionInfo) - this.poprfCombiner = new PoprfCombiner(keyVersionInfo.threshold) - } - - protected get allSignaturesLength(): number { - // No way of verifying signatures on the server-side - return this.unverifiedSignatures.length - } - - private get allSigsAsArray(): Uint8Array[] { - return this.unverifiedSignatures.map((response) => Buffer.from(response.signature, 'base64')) - } - - /* - * Aggregates blind partial signatures into a blind aggregated POPRF evaluation. - * On error, logs and throws exception for not enough signatures. - * Verification of partial signatures is not possible server-side - * (i.e. without the client's blinding factor). - */ - protected _combineBlindedSignatureShares(_blindedMessage: string, logger: Logger): string { - try { - const result = this.poprfCombiner.blindAggregate(this.allSigsAsArray) - if (result !== undefined) { - return result.toString('base64') - } - } catch (error) { - logger.error(ErrorMessage.SIGNATURE_AGGREGATION_FAILURE) - logger.error(error) - } - throw new Error(ErrorMessage.SIGNATURE_AGGREGATION_FAILURE) - } -} diff --git a/packages/phone-number-privacy/combiner/src/common/crypto-session.ts b/packages/phone-number-privacy/combiner/src/common/crypto-session.ts deleted file mode 100644 index f1a0d7d6f98..00000000000 --- a/packages/phone-number-privacy/combiner/src/common/crypto-session.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { KeyVersionInfo, OdisResponse } from '@celo/phone-number-privacy-common' -import { Request, Response } from 'express' -import { CryptoClient } from './crypto-clients/crypto-client' -import { Session } from './session' -import { OdisSignatureRequest } from './sign' - -export class CryptoSession extends Session { - public constructor( - readonly request: Request<{}, {}, R>, - readonly response: Response>, - readonly keyVersionInfo: KeyVersionInfo, - readonly crypto: CryptoClient - ) { - super(request, response, keyVersionInfo) - } -} diff --git a/packages/phone-number-privacy/combiner/src/common/io.ts b/packages/phone-number-privacy/combiner/src/common/io.ts deleted file mode 100644 index 89545648e2f..00000000000 --- a/packages/phone-number-privacy/combiner/src/common/io.ts +++ /dev/null @@ -1,168 +0,0 @@ -import { - CombinerEndpoint, - ErrorMessage, - ErrorType, - FailureResponse, - getRequestKeyVersion, - KEY_VERSION_HEADER, - KeyVersionInfo, - OdisRequest, - OdisResponse, - requestHasValidKeyVersion, - SignerEndpoint, - SuccessResponse, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import Logger from 'bunyan' -import { Request, Response } from 'express' -import * as t from 'io-ts' -import fetch, { Response as FetchResponse } from 'node-fetch' -import { performance } from 'perf_hooks' -import { OdisConfig } from '../config' -import { Signer } from './combine' -import { Session } from './session' - -// tslint:disable-next-line: interface-over-type-literal -export type SignerResponse = { - url: string - res: OdisResponse - status: number -} - -export abstract class IO { - abstract readonly endpoint: CombinerEndpoint - abstract readonly signerEndpoint: SignerEndpoint - abstract readonly requestSchema: t.Type - abstract readonly responseSchema: t.Type, OdisResponse, unknown> - - constructor(readonly config: OdisConfig) {} - - abstract init( - request: Request<{}, {}, unknown>, - response: Response> - ): Promise | null> - - abstract authenticate(request: Request<{}, {}, R>, logger?: Logger): Promise - - abstract sendFailure( - error: ErrorType, - status: number, - response: Response>, - ...args: unknown[] - ): void - - abstract sendSuccess( - status: number, - response: Response>, - ...args: unknown[] - ): void - - validateClientRequest(request: Request<{}, {}, unknown>): request is Request<{}, {}, R> { - return this.requestSchema.is(request.body) - } - - getKeyVersionInfo(request: Request<{}, {}, OdisRequest>, logger: Logger): KeyVersionInfo { - // If an invalid key version is present, we don't want this function to throw but - // to instead replace the key version with the default - // If a valid but unsupported key version is present, we want this function to throw - let requestKeyVersion: number | undefined - if (requestHasValidKeyVersion(request, logger)) { - requestKeyVersion = getRequestKeyVersion(request, logger) - } - const keyVersion = requestKeyVersion ?? this.config.keys.currentVersion - const supportedVersions: KeyVersionInfo[] = JSON.parse(this.config.keys.versions) // TODO add io-ts checks for this and signer array - const filteredSupportedVersions: KeyVersionInfo[] = supportedVersions.filter( - (v) => v.keyVersion === keyVersion - ) - if (!filteredSupportedVersions.length) { - throw new Error(`key version ${keyVersion} not supported`) - } - return filteredSupportedVersions[0] - } - - requestHasSupportedKeyVersion(request: Request<{}, {}, OdisRequest>, logger: Logger): boolean { - try { - this.getKeyVersionInfo(request, logger) - return true - } catch (err) { - logger.debug('Error caught in requestHasSupportedKeyVersion') - logger.debug(err) - return false - } - } - - validateSignerResponse(data: string, url: string, logger: Logger): OdisResponse { - const res: unknown = JSON.parse(data) - if (!this.responseSchema.is(res)) { - logger.error( - { data, signer: url }, - `Signer request to ${url + this.signerEndpoint} returned malformed response` - ) - throw new Error(ErrorMessage.INVALID_SIGNER_RESPONSE) - } - return res - } - - async fetchSignerResponseWithFallback( - signer: Signer, - session: Session - ): Promise { - const start = `Start ${signer.url + this.signerEndpoint}` - const end = `End ${signer.url + this.signerEndpoint}` - performance.mark(start) - - return this.fetchSignerResponse(signer.url, session) - .catch((err) => { - session.logger.error({ url: signer.url, error: err }, `Signer failed with primary url`) - if (signer.fallbackUrl) { - session.logger.warn({ url: signer.fallbackUrl }, `Using fallback url to call signer`) - return this.fetchSignerResponse(signer.fallbackUrl, session) - } - throw err - }) - .finally(() => { - performance.mark(end) - performance.measure(signer.url, start, end) - }) - } - - protected async fetchSignerResponse( - signerUrl: string, - session: Session - ): Promise { - const { request, logger, abort } = session - const url = signerUrl + this.signerEndpoint - logger.debug({ url }, `Sending signer request`) - // prettier-ignore - return fetch(url, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - // Pnp requests provide authorization in the request header - ...(request.headers.authorization ? { Authorization: request.headers.authorization } : {}), - // Forward requested keyVersion if provided by client, otherwise use default keyVersion. - // This will be ignored for non-signing requests. - [KEY_VERSION_HEADER]: session.keyVersionInfo.keyVersion.toString() - }, - body: JSON.stringify(request.body), - // @ts-ignore: missing property `reason` - signal: abort.signal, - }) - } - - protected inputChecks( - request: Request<{}, {}, unknown>, - response: Response> - ): request is Request<{}, {}, R> { - if (!this.config.enabled) { - this.sendFailure(WarningMessage.API_UNAVAILABLE, 503, response) - return false - } - if (!this.validateClientRequest(request)) { - this.sendFailure(WarningMessage.INVALID_INPUT, 400, response) - return false - } - return true - } -} diff --git a/packages/phone-number-privacy/combiner/src/common/session.ts b/packages/phone-number-privacy/combiner/src/common/session.ts deleted file mode 100644 index bfa3ae24b29..00000000000 --- a/packages/phone-number-privacy/combiner/src/common/session.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { - ErrorMessage, - KeyVersionInfo, - OdisRequest, - OdisResponse, -} from '@celo/phone-number-privacy-common' -import AbortController from 'abort-controller' -import Logger from 'bunyan' -import { Request, Response } from 'express' -import { SignerResponse } from './io' - -export class Session { - public timedOut: boolean = false - readonly logger: Logger - readonly abort: AbortController = new AbortController() - readonly failedSigners: Set = new Set() - readonly errorCodes: Map = new Map() - readonly responses: Array> = new Array>() - readonly warnings: string[] = [] - - public constructor( - readonly request: Request<{}, {}, R>, - readonly response: Response>, - readonly keyVersionInfo: KeyVersionInfo - ) { - this.logger = response.locals.logger - } - - incrementErrorCodeCount(errorCode: number) { - this.errorCodes.set(errorCode, (this.errorCodes.get(errorCode) ?? 0) + 1) - } - - getMajorityErrorCode(): number | null { - const uniqueErrorCount = Array.from(this.errorCodes.keys()).length - if (uniqueErrorCount > 1) { - this.logger.error( - { errorCodes: JSON.stringify([...this.errorCodes]) }, - ErrorMessage.INCONSISTENT_SIGNER_RESPONSES - ) - } - - let maxErrorCode = -1 - let maxCount = -1 - this.errorCodes.forEach((count, errorCode) => { - // This gives priority to the lower status codes in the event of a tie - // because 400s are more helpful than 500s for user feedback - if (count > maxCount || (count === maxCount && errorCode < maxErrorCode)) { - maxCount = count - maxErrorCode = errorCode - } - }) - return maxErrorCode > 0 ? maxErrorCode : null - } -} diff --git a/packages/phone-number-privacy/combiner/src/common/sign.ts b/packages/phone-number-privacy/combiner/src/common/sign.ts deleted file mode 100644 index 49b766bb57a..00000000000 --- a/packages/phone-number-privacy/combiner/src/common/sign.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { - DomainRestrictedSignatureRequest, - ErrorMessage, - ErrorType, - LegacySignMessageRequest, - OdisResponse, - responseHasExpectedKeyVersion, - SignMessageRequest, -} from '@celo/phone-number-privacy-common' -import { Response as FetchResponse } from 'node-fetch' -import { OdisConfig } from '../config' -import { DomainThresholdStateService } from '../domain/services/threshold-state' -import { PnpThresholdStateService } from '../pnp/services/threshold-state' -import { CombineAction } from './combine' -import { CryptoSession } from './crypto-session' -import { IO } from './io' - -// prettier-ignore -export type OdisSignatureRequest = - | SignMessageRequest - | LegacySignMessageRequest - | DomainRestrictedSignatureRequest -export type ThresholdStateService = R extends SignMessageRequest - ? PnpThresholdStateService - : never | R extends DomainRestrictedSignatureRequest - ? DomainThresholdStateService - : never - -// tslint:disable-next-line: max-classes-per-file -export abstract class SignAction extends CombineAction { - constructor( - readonly config: OdisConfig, - readonly thresholdStateService: ThresholdStateService, - readonly io: IO - ) { - super(config, io) - } - - // Throws if response is not actually successful - protected async receiveSuccess( - signerResponse: FetchResponse, - url: string, - session: CryptoSession - ): Promise> { - const { keyVersion } = session.keyVersionInfo - - // TODO(2.0.0, deployment) consider this while doing deployment. Signers should be updated before the combiner is - if (!responseHasExpectedKeyVersion(signerResponse, keyVersion, session.logger)) { - throw new Error(ErrorMessage.INVALID_KEY_VERSION_RESPONSE) - } - - const res = await super.receiveSuccess(signerResponse, url, session) - - if (res.success) { - const signatureAdditionStart = Date.now() - session.crypto.addSignature({ url, signature: res.signature }) - session.logger.info( - { - signer: url, - hasSufficientSignatures: session.crypto.hasSufficientSignatures(), - additionLatency: Date.now() - signatureAdditionStart, - }, - 'Added signature' - ) - // Send response immediately once we cross threshold - // BLS threshold signatures can be combined without all partial signatures - if (session.crypto.hasSufficientSignatures()) { - try { - session.crypto.combineBlindedSignatureShares( - this.parseBlindedMessage(session.request.body), - session.logger - ) - // Close outstanding requests - session.abort.abort() - } catch (err) { - // One or more signatures failed verification and were discarded. - session.logger.info('Error caught in receiveSuccess') - session.logger.info(err) - // Continue to collect signatures. - } - } - } - return res - } - - protected handleMissingSignatures(session: CryptoSession) { - const errorCode = session.getMajorityErrorCode() ?? 500 - const error = this.errorCodeToError(errorCode) - this.io.sendFailure(error, errorCode, session.response) - } - - protected abstract errorCodeToError(errorCode: number): ErrorType - - protected abstract parseBlindedMessage(req: OdisSignatureRequest): string -} diff --git a/packages/phone-number-privacy/combiner/src/config.ts b/packages/phone-number-privacy/combiner/src/config.ts deleted file mode 100644 index f9cc2ecda24..00000000000 --- a/packages/phone-number-privacy/combiner/src/config.ts +++ /dev/null @@ -1,182 +0,0 @@ -import { - BlockchainConfig, - FULL_NODE_TIMEOUT_IN_MS, - rootLogger, - TestUtils, - toBool, -} from '@celo/phone-number-privacy-common' -import * as functions from 'firebase-functions' -export function getCombinerVersion(): string { - return process.env.npm_package_version ?? require('../package.json').version ?? '0.0.0' -} -export const DEV_MODE = - process.env.NODE_ENV !== 'production' || process.env.FUNCTIONS_EMULATOR === 'true' - -export const FORNO_ALFAJORES = 'https://alfajores-forno.celo-testnet.org' - -// combiner always thinks these accounts/phoneNumbersa are verified to enable e2e testing -export const E2E_TEST_PHONE_NUMBERS_RAW: string[] = ['+14155550123', '+15555555555', '+14444444444'] - -export const E2E_TEST_ACCOUNTS: string[] = ['0x1be31a94361a391bbafb2a4ccd704f57dc04d4bb'] - -export const MAX_BLOCK_DISCREPANCY_THRESHOLD = 3 -export const MAX_TOTAL_QUOTA_DISCREPANCY_THRESHOLD = 5 -export const MAX_QUERY_COUNT_DISCREPANCY_THRESHOLD = 5 - -export interface OdisConfig { - serviceName: string - enabled: boolean - shouldFailOpen: boolean // TODO (https://github.com/celo-org/celo-monorepo/issues/9862) consider refactoring config, this isn't relevant to domains endpoints - odisServices: { - signers: string - timeoutMilliSeconds: number - } - keys: { - currentVersion: number - versions: string // parse as KeyVersionInfo[] - } - fullNodeTimeoutMs: number -} - -export interface CombinerConfig { - serviceName: string - blockchain: BlockchainConfig - phoneNumberPrivacy: OdisConfig - domains: OdisConfig -} - -let config: CombinerConfig - -const defaultServiceName = 'odis-combiner' - -if (DEV_MODE) { - rootLogger(defaultServiceName).debug('Running in dev mode') - const devSignersString = JSON.stringify([ - { - url: 'http://localhost:3001', - fallbackUrl: 'http://localhost:3001/fallback', - }, - { - url: 'http://localhost:3002', - fallbackUrl: 'http://localhost:3002/fallback', - }, - { - url: 'http://localhost:3003', - fallbackUrl: 'http://localhost:3003/fallback', - }, - ]) - config = { - serviceName: defaultServiceName, - blockchain: { - provider: FORNO_ALFAJORES, - }, - phoneNumberPrivacy: { - serviceName: defaultServiceName, - enabled: true, - shouldFailOpen: false, - odisServices: { - signers: devSignersString, - timeoutMilliSeconds: 5 * 1000, - }, - keys: { - currentVersion: 1, - versions: JSON.stringify([ - { - keyVersion: 1, - threshold: 2, - polynomial: TestUtils.Values.PNP_THRESHOLD_DEV_POLYNOMIAL_V1, - pubKey: TestUtils.Values.PNP_THRESHOLD_DEV_PUBKEY_V1, - }, - { - keyVersion: 2, - threshold: 2, - polynomial: TestUtils.Values.PNP_THRESHOLD_DEV_POLYNOMIAL_V2, - pubKey: TestUtils.Values.PNP_THRESHOLD_DEV_PUBKEY_V2, - }, - { - keyVersion: 3, - threshold: 2, - polynomial: TestUtils.Values.PNP_THRESHOLD_DEV_POLYNOMIAL_V3, - pubKey: TestUtils.Values.PNP_THRESHOLD_DEV_PUBKEY_V3, - }, - ]), - }, - fullNodeTimeoutMs: FULL_NODE_TIMEOUT_IN_MS, - }, - domains: { - serviceName: defaultServiceName, - enabled: true, - shouldFailOpen: false, - odisServices: { - signers: devSignersString, - timeoutMilliSeconds: 5 * 1000, - }, - keys: { - currentVersion: 1, - versions: JSON.stringify([ - { - keyVersion: 1, - threshold: 2, - polynomial: TestUtils.Values.DOMAINS_THRESHOLD_DEV_POLYNOMIAL_V1, - pubKey: TestUtils.Values.DOMAINS_THRESHOLD_DEV_PUBKEY_V1, - }, - { - keyVersion: 2, - threshold: 2, - polynomial: TestUtils.Values.DOMAINS_THRESHOLD_DEV_POLYNOMIAL_V2, - pubKey: TestUtils.Values.DOMAINS_THRESHOLD_DEV_PUBKEY_V2, - }, - { - keyVersion: 3, - threshold: 2, - polynomial: TestUtils.Values.DOMAINS_THRESHOLD_DEV_POLYNOMIAL_V3, - pubKey: TestUtils.Values.DOMAINS_THRESHOLD_DEV_PUBKEY_V3, - }, - ]), - }, - fullNodeTimeoutMs: FULL_NODE_TIMEOUT_IN_MS, - }, - } -} else { - const functionConfig = functions.config() - config = { - serviceName: functionConfig.service.name ?? defaultServiceName, - blockchain: { - provider: functionConfig.blockchain.provider, - apiKey: functionConfig.blockchain.api_key, - }, - phoneNumberPrivacy: { - serviceName: functionConfig.pnp.service_name ?? defaultServiceName, - enabled: toBool(functionConfig.pnp.enabled, false), - shouldFailOpen: toBool(functionConfig.pnp.should_fail_open, false), - odisServices: { - signers: functionConfig.pnp.odisservices, - timeoutMilliSeconds: functionConfig.pnp.timeout_ms - ? Number(functionConfig.pnp.timeout_ms) - : 5 * 1000, - }, - keys: { - currentVersion: Number(functionConfig.pnp_keys.current_version), - versions: functionConfig.pnp_keys.versions, - }, - fullNodeTimeoutMs: Number(functionConfig.pnp.full_node_timeout_ms ?? FULL_NODE_TIMEOUT_IN_MS), - }, - domains: { - serviceName: functionConfig.domains.service_name ?? defaultServiceName, - enabled: toBool(functionConfig.domains.enabled, false), - shouldFailOpen: toBool(functionConfig.domains.auth_should_fail_open, false), - odisServices: { - signers: functionConfig.domains.odisservices, - timeoutMilliSeconds: functionConfig.domains.timeout_ms - ? Number(functionConfig.domains.timeout_ms) - : 5 * 1000, - }, - keys: { - currentVersion: Number(functionConfig.domains_keys.current_version), - versions: functionConfig.domains_keys.versions, - }, - fullNodeTimeoutMs: Number(functionConfig.pnp.full_node_timeout_ms ?? FULL_NODE_TIMEOUT_IN_MS), - }, - } -} -export default config diff --git a/packages/phone-number-privacy/combiner/src/domain/endpoints/disable/action.ts b/packages/phone-number-privacy/combiner/src/domain/endpoints/disable/action.ts deleted file mode 100644 index 21ab840ee9f..00000000000 --- a/packages/phone-number-privacy/combiner/src/domain/endpoints/disable/action.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { DisableDomainRequest, ErrorMessage } from '@celo/phone-number-privacy-common' -import { CombineAction } from '../../../common/combine' -import { IO } from '../../../common/io' -import { Session } from '../../../common/session' -import { OdisConfig } from '../../../config' -import { DomainSignerResponseLogger } from '../../services/log-responses' -import { DomainThresholdStateService } from '../../services/threshold-state' - -export class DomainDisableAction extends CombineAction { - readonly responseLogger: DomainSignerResponseLogger = new DomainSignerResponseLogger() - - constructor( - readonly config: OdisConfig, - readonly thresholdStateService: DomainThresholdStateService, - readonly io: IO - ) { - super(config, io) - } - - combine(session: Session): void { - this.responseLogger.logResponseDiscrepancies(session) - try { - const disableDomainStatus = this.thresholdStateService.findThresholdDomainState(session) - if (disableDomainStatus.disabled) { - this.io.sendSuccess(200, session.response, disableDomainStatus) - return - } - } catch (err) { - session.logger.error({ err }, 'Error combining signer disable domain status responses') - } - - this.io.sendFailure( - ErrorMessage.THRESHOLD_DISABLE_DOMAIN_FAILURE, - session.getMajorityErrorCode() ?? 500, - session.response, - session.logger - ) - } -} diff --git a/packages/phone-number-privacy/combiner/src/domain/endpoints/disable/io.ts b/packages/phone-number-privacy/combiner/src/domain/endpoints/disable/io.ts deleted file mode 100644 index 21b8e81f501..00000000000 --- a/packages/phone-number-privacy/combiner/src/domain/endpoints/disable/io.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { - CombinerEndpoint, - DisableDomainRequest, - disableDomainRequestSchema, - DisableDomainResponse, - DisableDomainResponseFailure, - disableDomainResponseSchema, - DisableDomainResponseSuccess, - DomainSchema, - DomainState, - ErrorType, - getSignerEndpoint, - send, - SequentialDelayDomainStateSchema, - SignerEndpoint, - verifyDisableDomainRequestAuthenticity, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import { Request, Response } from 'express' -import * as t from 'io-ts' -import { IO } from '../../../common/io' -import { Session } from '../../../common/session' -import { getCombinerVersion } from '../../../config' - -export class DomainDisableIO extends IO { - readonly endpoint: CombinerEndpoint = CombinerEndpoint.DISABLE_DOMAIN - readonly signerEndpoint: SignerEndpoint = getSignerEndpoint(this.endpoint) - readonly requestSchema: t.Type = - disableDomainRequestSchema(DomainSchema) - readonly responseSchema: t.Type = - disableDomainResponseSchema(SequentialDelayDomainStateSchema) - - async init( - request: Request<{}, {}, unknown>, - response: Response - ): Promise | null> { - if (!super.inputChecks(request, response)) { - return null - } - if (!(await this.authenticate(request))) { - this.sendFailure(WarningMessage.UNAUTHENTICATED_USER, 401, response) - return null - } - return new Session(request, response, this.getKeyVersionInfo(request, response.locals.logger)) - } - - authenticate(request: Request<{}, {}, DisableDomainRequest>): Promise { - return Promise.resolve(verifyDisableDomainRequestAuthenticity(request.body)) - } - - sendSuccess( - status: number, - response: Response, - domainState: DomainState - ) { - send( - response, - { - success: true, - version: getCombinerVersion(), - status: domainState, - }, - status, - response.locals.logger - ) - } - - sendFailure(error: ErrorType, status: number, response: Response) { - send( - response, - { - success: false, - version: getCombinerVersion(), - error, - }, - status, - response.locals.logger - ) - } -} diff --git a/packages/phone-number-privacy/combiner/src/domain/endpoints/quota/action.ts b/packages/phone-number-privacy/combiner/src/domain/endpoints/quota/action.ts deleted file mode 100644 index 4ba6032fc05..00000000000 --- a/packages/phone-number-privacy/combiner/src/domain/endpoints/quota/action.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { DomainQuotaStatusRequest, ErrorMessage } from '@celo/phone-number-privacy-common' -import { CombineAction } from '../../../common/combine' -import { IO } from '../../../common/io' -import { Session } from '../../../common/session' -import { OdisConfig } from '../../../config' -import { DomainSignerResponseLogger } from '../../services/log-responses' -import { DomainThresholdStateService } from '../../services/threshold-state' - -export class DomainQuotaAction extends CombineAction { - readonly responseLogger = new DomainSignerResponseLogger() - - constructor( - readonly config: OdisConfig, - readonly thresholdStateService: DomainThresholdStateService, - readonly io: IO - ) { - super(config, io) - } - - combine(session: Session): void { - this.responseLogger.logResponseDiscrepancies(session) - const { threshold } = session.keyVersionInfo - if (session.responses.length >= threshold) { - try { - const domainQuotaStatus = this.thresholdStateService.findThresholdDomainState(session) - this.io.sendSuccess(200, session.response, domainQuotaStatus) - return - } catch (err) { - session.logger.error(err, 'Error combining signer quota status responses') - } - } - this.io.sendFailure( - ErrorMessage.THRESHOLD_DOMAIN_QUOTA_STATUS_FAILURE, - session.getMajorityErrorCode() ?? 500, - session.response, - session.logger - ) - } -} diff --git a/packages/phone-number-privacy/combiner/src/domain/endpoints/quota/io.ts b/packages/phone-number-privacy/combiner/src/domain/endpoints/quota/io.ts deleted file mode 100644 index 3469fc2938d..00000000000 --- a/packages/phone-number-privacy/combiner/src/domain/endpoints/quota/io.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { - CombinerEndpoint, - DomainQuotaStatusRequest, - domainQuotaStatusRequestSchema, - DomainQuotaStatusResponse, - DomainQuotaStatusResponseFailure, - domainQuotaStatusResponseSchema, - DomainQuotaStatusResponseSuccess, - DomainSchema, - DomainState, - ErrorType, - getSignerEndpoint, - OdisResponse, - send, - SequentialDelayDomainStateSchema, - SignerEndpoint, - verifyDomainQuotaStatusRequestAuthenticity, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import { Request, Response } from 'express' -import * as t from 'io-ts' -import { IO } from '../../../common/io' -import { Session } from '../../../common/session' -import { getCombinerVersion } from '../../../config' - -export class DomainQuotaIO extends IO { - readonly endpoint: CombinerEndpoint = CombinerEndpoint.DOMAIN_QUOTA_STATUS - readonly signerEndpoint: SignerEndpoint = getSignerEndpoint(this.endpoint) - readonly requestSchema: t.Type = - domainQuotaStatusRequestSchema(DomainSchema) - readonly responseSchema: t.Type = - domainQuotaStatusResponseSchema(SequentialDelayDomainStateSchema) - - async init( - request: Request<{}, {}, unknown>, - response: Response> - ): Promise | null> { - if (!super.inputChecks(request, response)) { - return null - } - if (!(await this.authenticate(request))) { - this.sendFailure(WarningMessage.UNAUTHENTICATED_USER, 401, response) - return null - } - const keyVersionInfo = this.getKeyVersionInfo(request, response.locals.logger) - return new Session(request, response, keyVersionInfo) - } - - authenticate(request: Request<{}, {}, DomainQuotaStatusRequest>): Promise { - return Promise.resolve(verifyDomainQuotaStatusRequestAuthenticity(request.body)) - } - - sendSuccess( - status: number, - response: Response, - domainState: DomainState - ) { - send( - response, - { - success: true, - version: getCombinerVersion(), - status: domainState, - }, - status, - response.locals.logger - ) - } - - sendFailure( - error: ErrorType, - status: number, - response: Response - ) { - send( - response, - { - success: false, - version: getCombinerVersion(), - error, - }, - status, - response.locals.logger - ) - } -} diff --git a/packages/phone-number-privacy/combiner/src/domain/endpoints/sign/action.ts b/packages/phone-number-privacy/combiner/src/domain/endpoints/sign/action.ts deleted file mode 100644 index e7f74b36d21..00000000000 --- a/packages/phone-number-privacy/combiner/src/domain/endpoints/sign/action.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { - DomainRestrictedSignatureRequest, - ErrorMessage, - ErrorType, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import { CryptoSession } from '../../../common/crypto-session' -import { SignAction } from '../../../common/sign' -import { DomainSignerResponseLogger } from '../../services/log-responses' - -export class DomainSignAction extends SignAction { - readonly responseLogger = new DomainSignerResponseLogger() - - combine(session: CryptoSession): void { - this.responseLogger.logResponseDiscrepancies(session) - - if (session.crypto.hasSufficientSignatures()) { - try { - const combinedSignature = session.crypto.combineBlindedSignatureShares( - this.parseBlindedMessage(session.request.body), - session.logger - ) - - return this.io.sendSuccess( - 200, - session.response, - combinedSignature, - this.thresholdStateService.findThresholdDomainState(session) - ) - } catch (err) { - // May fail upon combining signatures if too many sigs are invalid - session.logger.error('Combining signatures failed in combine') - session.logger.error(err) - // Fallback to handleMissingSignatures - } - } - - this.handleMissingSignatures(session) - } - - protected parseBlindedMessage(req: DomainRestrictedSignatureRequest): string { - return req.blindedMessage - } - - protected errorCodeToError(errorCode: number): ErrorType { - switch (errorCode) { - case 429: - return WarningMessage.EXCEEDED_QUOTA - case 401: - // Authentication is checked in the combiner, but invalid nonces are passed through - return WarningMessage.INVALID_NONCE - default: - return ErrorMessage.NOT_ENOUGH_PARTIAL_SIGNATURES - } - } -} diff --git a/packages/phone-number-privacy/combiner/src/domain/endpoints/sign/io.ts b/packages/phone-number-privacy/combiner/src/domain/endpoints/sign/io.ts deleted file mode 100644 index 291564b4468..00000000000 --- a/packages/phone-number-privacy/combiner/src/domain/endpoints/sign/io.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { - CombinerEndpoint, - DomainRestrictedSignatureRequest, - domainRestrictedSignatureRequestSchema, - DomainRestrictedSignatureResponse, - DomainRestrictedSignatureResponseFailure, - domainRestrictedSignatureResponseSchema, - DomainRestrictedSignatureResponseSuccess, - DomainSchema, - DomainState, - ErrorType, - getSignerEndpoint, - send, - SequentialDelayDomainStateSchema, - verifyDomainRestrictedSignatureRequestAuthenticity, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import { Request, Response } from 'express' -import * as t from 'io-ts' -import { DomainCryptoClient } from '../../../common/crypto-clients/domain-crypto-client' -import { CryptoSession } from '../../../common/crypto-session' -import { IO } from '../../../common/io' -import { getCombinerVersion } from '../../../config' - -export class DomainSignIO extends IO { - readonly endpoint = CombinerEndpoint.DOMAIN_SIGN - readonly signerEndpoint = getSignerEndpoint(this.endpoint) - readonly requestSchema: t.Type< - DomainRestrictedSignatureRequest, - DomainRestrictedSignatureRequest, - unknown - > = domainRestrictedSignatureRequestSchema(DomainSchema) - readonly responseSchema: t.Type< - DomainRestrictedSignatureResponse, - DomainRestrictedSignatureResponse, - unknown - > = domainRestrictedSignatureResponseSchema(SequentialDelayDomainStateSchema) - - async init( - request: Request<{}, {}, unknown>, - response: Response - ): Promise | null> { - if (!super.inputChecks(request, response)) { - return null - } - if (!this.requestHasSupportedKeyVersion(request, response.locals.logger)) { - this.sendFailure(WarningMessage.INVALID_KEY_VERSION_REQUEST, 400, response) - return null - } - if (!(await this.authenticate(request))) { - this.sendFailure(WarningMessage.UNAUTHENTICATED_USER, 401, response) - return null - } - const keyVersionInfo = this.getKeyVersionInfo(request, response.locals.logger) - return new CryptoSession( - request, - response, - keyVersionInfo, - new DomainCryptoClient(keyVersionInfo) - ) - } - - authenticate(request: Request<{}, {}, DomainRestrictedSignatureRequest>): Promise { - // Note that signing requests may include a nonce for replay protection that will be checked by - // the signer, but is not checked here. As a result, requests that pass the authentication check - // here may still fail when sent to the signer. - return Promise.resolve(verifyDomainRestrictedSignatureRequestAuthenticity(request.body)) - } - - sendSuccess( - status: number, - response: Response, - signature: string, - domainState: DomainState - ) { - send( - response, - { - success: true, - version: getCombinerVersion(), - signature, - status: domainState, - }, - status, - response.locals.logger - ) - } - - sendFailure( - error: ErrorType, - status: number, - response: Response - ) { - send( - response, - { - success: false, - version: getCombinerVersion(), - error, - }, - status, - response.locals.logger - ) - } -} diff --git a/packages/phone-number-privacy/combiner/src/domain/services/log-responses.ts b/packages/phone-number-privacy/combiner/src/domain/services/log-responses.ts deleted file mode 100644 index 4e78834751a..00000000000 --- a/packages/phone-number-privacy/combiner/src/domain/services/log-responses.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { - DomainRequest, - DomainRestrictedSignatureRequest, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import { CryptoSession } from '../../common/crypto-session' -import { Session } from '../../common/session' - -export class DomainSignerResponseLogger { - logResponseDiscrepancies( - session: Session | CryptoSession - ): void { - const parsedResponses: Array<{ - signerUrl: string - values: { - version: string - counter: number - disabled: boolean - timer: number - } - }> = [] - session.responses.forEach((response) => { - if (response.res.success) { - const { version, status } = response.res - parsedResponses.push({ - signerUrl: response.url, - values: { - version, - counter: status.counter, - disabled: status.disabled, - timer: status.timer, - }, - }) - } - }) - if (parsedResponses.length === 0) { - session.logger.warn('No successful signer responses found!') - return - } - - // log all responses if we notice any discrepancies to aid with debugging - const first = JSON.stringify(parsedResponses[0].values) - for (let i = 1; i < parsedResponses.length; i++) { - if (JSON.stringify(parsedResponses[i].values) !== first) { - session.logger.warn({ parsedResponses }, WarningMessage.SIGNER_RESPONSE_DISCREPANCIES) - break - } - } - - // disabled - const numDisabled = parsedResponses.filter((res) => res.values.disabled).length - if (numDisabled > 0 && numDisabled < parsedResponses.length) { - session.logger.error( - { parsedResponses }, - WarningMessage.INCONSISTENT_SIGNER_DOMAIN_DISABLED_STATES - ) - } - } -} diff --git a/packages/phone-number-privacy/combiner/src/domain/services/threshold-state.ts b/packages/phone-number-privacy/combiner/src/domain/services/threshold-state.ts deleted file mode 100644 index 38cdf62e8e8..00000000000 --- a/packages/phone-number-privacy/combiner/src/domain/services/threshold-state.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { DomainRequest, DomainState } from '@celo/phone-number-privacy-common' -import { Session } from '../../common/session' -import { OdisConfig } from '../../config' - -export class DomainThresholdStateService { - constructor(readonly config: OdisConfig) {} - - findThresholdDomainState(session: Session): DomainState { - // Get the domain status from the responses, filtering out responses that don't have the status. - const domainStates = session.responses - .map((signerResponse) => ('status' in signerResponse.res ? signerResponse.res.status : null)) - .filter((state: DomainState | null | undefined): state is DomainState => !!state) - - const { threshold } = session.keyVersionInfo - - // Note: when the threshold > # total signers - threshold, it's possible that we - // throw an error here when the domain is disabled. While the domain is technically disabled, - // the hope is to increase the "safety margin" of the number of signers that have - // also disabled this domain.This can be changed in the future (if we think that - // the safety margin is no longer needed) by simply checking if the domain is disabled - // before checking if the threshold of enabled responses has been met. - if (domainStates.length < threshold) { - throw new Error('Insufficient number of signer responses') - } - - // Check whether the domain is disabled, either by all signers or by some. - const domainStatesEnabled = domainStates.filter((ds) => !ds.disabled) - const numDisabled = domainStates.length - domainStatesEnabled.length - - const signersLength = JSON.parse(this.config.odisServices.signers).length - if (signersLength - numDisabled < threshold) { - return { timer: 0, counter: 0, disabled: true, now: 0 } - } - - // Ideally users will resubmit the request in this case. - if (domainStatesEnabled.length < threshold) { - throw new Error('Insufficient number of signer responses. Domain may be disabled') - } - - // Set n to last signer index in a quorum of signers are sorted from least to most restrictive. - const n = threshold - 1 - - const domainStatesAscendingByCounter = domainStatesEnabled.sort((a, b) => a.counter - b.counter) - const nthLeastRestrictiveByCounter = domainStatesAscendingByCounter[n] - const thresholdCounter = nthLeastRestrictiveByCounter.counter - - // Client should submit requests with nonce === thresholdCounter - - const domainStatesWithThresholdCounter = domainStatesEnabled.filter( - (ds) => ds.counter <= thresholdCounter - ) - - const domainStatesAscendingByTimestampRestrictiveness = domainStatesWithThresholdCounter.sort( - (a, b) => a.timer - a.now - (b.timer - b.now) - /** - * Please see '@celo/phone-number-privacy-common/src/domains/sequential-delay.ts' - * and https://github.com/celo-org/celo-proposals/blob/master/CIPs/CIP-0040/sequentialDelayDomain.md - * - * For a given DomainState, it is always the case that 'now' >= 'timer'. This ordering ensures - * that we take the 'timer' and 'date' from the same DomainState while still returning a reasonable - * definition of the "nth least restrictive" values. For simplicity, we do not take into consideration - * the 'delay' until the next request will be accepted as that would require calculating this value for - * each DomainState with the checkSequentialDelayDomainState algorithm in sequential-delay.ts. - * This would add complexity because DomainStates may have different values for 'counter' that dramatically - * alter this 'delay' and we want to protect the user's quota by returning the lowest possible - * threshold 'counter'. Feel free to implement a more exact solution if you're up for a coding challenge :) - */ - ) - const nthLeastRestrictiveByTimestamps = domainStatesAscendingByTimestampRestrictiveness[n] - - return { - timer: nthLeastRestrictiveByTimestamps.timer, - counter: thresholdCounter, - disabled: false, - now: nthLeastRestrictiveByTimestamps.now, - } - } -} diff --git a/packages/phone-number-privacy/combiner/src/index.ts b/packages/phone-number-privacy/combiner/src/index.ts deleted file mode 100644 index 4d4e7822dfc..00000000000 --- a/packages/phone-number-privacy/combiner/src/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { getContractKit } from '@celo/phone-number-privacy-common' -import * as functions from 'firebase-functions' -import config from './config' -import { startCombiner } from './server' - -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 ? functions.config().service.min_instances : undefined, - }) - .https.onRequest(startCombiner(config, getContractKit(config.blockchain))) -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 deleted file mode 100644 index e8f607965f1..00000000000 --- a/packages/phone-number-privacy/combiner/src/pnp/endpoints/quota/action.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { ErrorMessage, PnpQuotaRequest } from '@celo/phone-number-privacy-common' -import { CombineAction } from '../../../common/combine' -import { IO } from '../../../common/io' -import { Session } from '../../../common/session' -import { OdisConfig } from '../../../config' -import { PnpSignerResponseLogger } from '../../services/log-responses' -import { PnpThresholdStateService } from '../../services/threshold-state' - -export class PnpQuotaAction extends CombineAction { - readonly responseLogger: PnpSignerResponseLogger = new PnpSignerResponseLogger() - - constructor( - readonly config: OdisConfig, - readonly thresholdStateService: PnpThresholdStateService, - readonly io: IO - ) { - super(config, io) - } - - async combine(session: Session): Promise { - this.responseLogger.logResponseDiscrepancies(session) - this.responseLogger.logFailOpenResponses(session) - - const { threshold } = session.keyVersionInfo - - if (session.responses.length >= threshold) { - try { - const quotaStatus = this.thresholdStateService.findCombinerQuotaState(session) - this.io.sendSuccess(200, session.response, quotaStatus, session.warnings) - return - } catch (err) { - session.logger.error(err, 'Error combining signer quota status responses') - } - } - this.io.sendFailure( - ErrorMessage.THRESHOLD_PNP_QUOTA_STATUS_FAILURE, - session.getMajorityErrorCode() ?? 500, - session.response, - session.logger - ) - } -} diff --git a/packages/phone-number-privacy/combiner/src/pnp/endpoints/quota/io.ts b/packages/phone-number-privacy/combiner/src/pnp/endpoints/quota/io.ts deleted file mode 100644 index 6634f3ba124..00000000000 --- a/packages/phone-number-privacy/combiner/src/pnp/endpoints/quota/io.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { ContractKit } from '@celo/contractkit' -import { - authenticateUser, - CombinerEndpoint, - ErrorType, - getSignerEndpoint, - hasValidAccountParam, - identifierIsValidIfExists, - isBodyReasonablySized, - PnpQuotaRequest, - PnpQuotaRequestSchema, - PnpQuotaResponse, - PnpQuotaResponseFailure, - PnpQuotaResponseSchema, - PnpQuotaResponseSuccess, - PnpQuotaStatus, - send, - SignerEndpoint, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import Logger from 'bunyan' -import { Request, Response } from 'express' -import * as t from 'io-ts' -import { IO } from '../../../common/io' -import { Session } from '../../../common/session' -import { getCombinerVersion, OdisConfig } from '../../../config' - -export class PnpQuotaIO extends IO { - readonly endpoint: CombinerEndpoint = CombinerEndpoint.PNP_QUOTA - readonly signerEndpoint: SignerEndpoint = getSignerEndpoint(this.endpoint) - readonly requestSchema: t.Type = PnpQuotaRequestSchema - readonly responseSchema: t.Type = - PnpQuotaResponseSchema - - constructor(readonly config: OdisConfig, readonly kit: ContractKit) { - super(config) - } - - async init( - request: Request<{}, {}, unknown>, - response: Response - ): Promise | null> { - if (!super.inputChecks(request, response)) { - return null - } - if (!(await this.authenticate(request, response.locals.logger))) { - this.sendFailure(WarningMessage.UNAUTHENTICATED_USER, 401, response) - return null - } - const keyVersionInfo = this.getKeyVersionInfo(request, response.locals.logger) - return new Session(request, response, keyVersionInfo) - } - - validateClientRequest( - request: Request<{}, {}, unknown> - ): request is Request<{}, {}, PnpQuotaRequest> { - return ( - super.validateClientRequest(request) && - hasValidAccountParam(request.body) && - identifierIsValidIfExists(request.body) && - isBodyReasonablySized(request.body) - ) - } - - async authenticate(request: Request<{}, {}, PnpQuotaRequest>, logger: Logger): Promise { - return authenticateUser( - request, - this.kit, - logger, - this.config.shouldFailOpen, - [], - this.config.fullNodeTimeoutMs - ) - } - - sendSuccess( - status: number, - response: Response, - quotaStatus: PnpQuotaStatus, - warnings: string[] - ) { - send( - response, - { - success: true, - version: getCombinerVersion(), - ...quotaStatus, - warnings, - }, - status, - response.locals.logger - ) - } - - sendFailure(error: ErrorType, status: number, response: Response) { - send( - response, - { - success: false, - version: getCombinerVersion(), - error, - }, - status, - response.locals.logger - ) - } -} 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 deleted file mode 100644 index ed011827808..00000000000 --- a/packages/phone-number-privacy/combiner/src/pnp/endpoints/sign/action.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { - ErrorMessage, - ErrorType, - LegacySignMessageRequest, - SignMessageRequest, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import { CryptoSession } from '../../../common/crypto-session' -import { SignAction } from '../../../common/sign' -import { PnpSignerResponseLogger } from '../../services/log-responses' - -export class PnpSignAction extends SignAction { - readonly responseLogger: PnpSignerResponseLogger = new PnpSignerResponseLogger() - - combine(session: CryptoSession): void { - this.responseLogger.logResponseDiscrepancies(session) - this.responseLogger.logFailOpenResponses(session) - - if (session.crypto.hasSufficientSignatures()) { - try { - const combinedSignature = session.crypto.combineBlindedSignatureShares( - this.parseBlindedMessage(session.request.body), - session.logger - ) - - const quotaStatus = this.thresholdStateService.findCombinerQuotaState(session) - return this.io.sendSuccess( - 200, - session.response, - combinedSignature, - quotaStatus, - session.warnings - ) - } catch (error) { - // May fail upon combining signatures if too many sigs are invalid - // Fallback to handleMissingSignatures - session.logger.error(error) - } - } - - this.handleMissingSignatures(session) - } - - protected parseBlindedMessage(req: SignMessageRequest | LegacySignMessageRequest): string { - return req.blindedQueryPhoneNumber - } - - protected errorCodeToError(errorCode: number): ErrorType { - switch (errorCode) { - case 403: - return WarningMessage.EXCEEDED_QUOTA - default: - return ErrorMessage.NOT_ENOUGH_PARTIAL_SIGNATURES - } - } -} diff --git a/packages/phone-number-privacy/combiner/src/pnp/endpoints/sign/io.legacy.ts b/packages/phone-number-privacy/combiner/src/pnp/endpoints/sign/io.legacy.ts deleted file mode 100644 index cc8715bdff6..00000000000 --- a/packages/phone-number-privacy/combiner/src/pnp/endpoints/sign/io.legacy.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { ContractKit } from '@celo/contractkit' -import { - authenticateUser, - CombinerEndpoint, - ErrorType, - getSignerEndpoint, - hasValidAccountParam, - hasValidBlindedPhoneNumberParam, - identifierIsValidIfExists, - isBodyReasonablySized, - LegacySignMessageRequest, - LegacySignMessageRequestSchema, - PnpQuotaStatus, - send, - SignerEndpoint, - SignMessageResponse, - SignMessageResponseFailure, - SignMessageResponseSchema, - SignMessageResponseSuccess, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import Logger from 'bunyan' -import { Request, Response } from 'express' -import * as t from 'io-ts' -import { BLSCryptographyClient } from '../../../common/crypto-clients/bls-crypto-client' -import { CryptoSession } from '../../../common/crypto-session' -import { IO } from '../../../common/io' -import { getCombinerVersion, OdisConfig } from '../../../config' - -export class LegacyPnpSignIO extends IO { - readonly endpoint: CombinerEndpoint = CombinerEndpoint.LEGACY_PNP_SIGN - readonly signerEndpoint: SignerEndpoint = getSignerEndpoint(this.endpoint) - readonly requestSchema: t.Type = - LegacySignMessageRequestSchema - readonly responseSchema: t.Type = - SignMessageResponseSchema - - constructor(readonly config: OdisConfig, readonly kit: ContractKit) { - super(config) - } - - async init( - request: Request<{}, {}, unknown>, - response: Response - ): Promise | null> { - if (!super.inputChecks(request, response)) { - return null - } - if (!this.requestHasSupportedKeyVersion(request, response.locals.logger)) { - this.sendFailure(WarningMessage.INVALID_KEY_VERSION_REQUEST, 400, response) - return null - } - if (!(await this.authenticate(request, response.locals.logger))) { - this.sendFailure(WarningMessage.UNAUTHENTICATED_USER, 401, response) - return null - } - const keyVersionInfo = this.getKeyVersionInfo(request, response.locals.logger) - return new CryptoSession( - request, - response, - keyVersionInfo, - new BLSCryptographyClient(keyVersionInfo) - ) - } - - validateClientRequest( - request: Request<{}, {}, unknown> - ): request is Request<{}, {}, LegacySignMessageRequest> { - return ( - super.validateClientRequest(request) && - hasValidAccountParam(request.body) && - hasValidBlindedPhoneNumberParam(request.body) && - identifierIsValidIfExists(request.body) && - isBodyReasonablySized(request.body) - ) - } - - async authenticate( - request: Request<{}, {}, LegacySignMessageRequest>, - logger: Logger - ): Promise { - return authenticateUser( - request, - this.kit, - logger, - this.config.shouldFailOpen, - [], - this.config.fullNodeTimeoutMs - ) - } - - sendSuccess( - status: number, - response: Response, - signature: string, - quotaStatus: PnpQuotaStatus, - warnings: string[] - ) { - send( - response, - { - success: true, - version: getCombinerVersion(), - signature, - ...quotaStatus, - warnings, - }, - status, - response.locals.logger - ) - } - - sendFailure(error: ErrorType, status: number, response: Response) { - send( - response, - { - success: false, - version: getCombinerVersion(), - error, - }, - status, - response.locals.logger - ) - } -} diff --git a/packages/phone-number-privacy/combiner/src/pnp/endpoints/sign/io.ts b/packages/phone-number-privacy/combiner/src/pnp/endpoints/sign/io.ts deleted file mode 100644 index 2d9a22db0b1..00000000000 --- a/packages/phone-number-privacy/combiner/src/pnp/endpoints/sign/io.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { ContractKit } from '@celo/contractkit' -import { - authenticateUser, - CombinerEndpoint, - ErrorType, - getSignerEndpoint, - hasValidAccountParam, - hasValidBlindedPhoneNumberParam, - isBodyReasonablySized, - PnpQuotaStatus, - send, - SignerEndpoint, - SignMessageRequest, - SignMessageRequestSchema, - SignMessageResponse, - SignMessageResponseFailure, - SignMessageResponseSchema, - SignMessageResponseSuccess, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import Logger from 'bunyan' -import { Request, Response } from 'express' -import * as t from 'io-ts' -import { BLSCryptographyClient } from '../../../common/crypto-clients/bls-crypto-client' -import { CryptoSession } from '../../../common/crypto-session' -import { IO } from '../../../common/io' -import { Session } from '../../../common/session' -import { getCombinerVersion, OdisConfig } from '../../../config' - -export class PnpSignIO extends IO { - readonly endpoint: CombinerEndpoint = CombinerEndpoint.PNP_SIGN - readonly signerEndpoint: SignerEndpoint = getSignerEndpoint(this.endpoint) - readonly requestSchema: t.Type = - SignMessageRequestSchema - readonly responseSchema: t.Type = - SignMessageResponseSchema - - constructor(readonly config: OdisConfig, readonly kit: ContractKit) { - super(config) - } - - async init( - request: Request<{}, {}, unknown>, - response: Response - ): Promise | null> { - if (!super.inputChecks(request, response)) { - return null - } - if (!this.requestHasSupportedKeyVersion(request, response.locals.logger)) { - this.sendFailure(WarningMessage.INVALID_KEY_VERSION_REQUEST, 400, response) - return null - } - if (!(await this.authenticate(request, response.locals.logger))) { - this.sendFailure(WarningMessage.UNAUTHENTICATED_USER, 401, response) - return null - } - const keyVersionInfo = this.getKeyVersionInfo(request, response.locals.logger) - return new CryptoSession( - request, - response, - keyVersionInfo, - new BLSCryptographyClient(keyVersionInfo) - ) - } - - validateClientRequest( - request: Request<{}, {}, unknown> - ): request is Request<{}, {}, SignMessageRequest> { - return ( - super.validateClientRequest(request) && - hasValidAccountParam(request.body) && - hasValidBlindedPhoneNumberParam(request.body) && - isBodyReasonablySized(request.body) - ) - } - - async authenticate( - request: Request<{}, {}, SignMessageRequest>, - logger: Logger - ): Promise { - return authenticateUser( - request, - this.kit, - logger, - this.config.shouldFailOpen, - [], - this.config.fullNodeTimeoutMs - ) - } - - sendSuccess( - status: number, - response: Response, - signature: string, - quotaStatus: PnpQuotaStatus, - warnings: string[] - ) { - send( - response, - { - success: true, - version: getCombinerVersion(), - signature, - ...quotaStatus, - warnings, - }, - status, - response.locals.logger - ) - } - - sendFailure(error: ErrorType, status: number, response: Response) { - send( - response, - { - success: false, - version: getCombinerVersion(), - error, - }, - status, - response.locals.logger - ) - } -} 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 deleted file mode 100644 index 7fdc2b6b6e4..00000000000 --- a/packages/phone-number-privacy/combiner/src/pnp/services/log-responses.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { - ErrorMessage, - PnpQuotaRequest, - SignMessageRequest, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import { Session } from '../../common/session' -import { - MAX_BLOCK_DISCREPANCY_THRESHOLD, - MAX_QUERY_COUNT_DISCREPANCY_THRESHOLD, - MAX_TOTAL_QUOTA_DISCREPANCY_THRESHOLD, -} from '../../config' - -export class PnpSignerResponseLogger { - logResponseDiscrepancies(session: Session | Session): void { - // TODO responses should all already be successes due to CombineAction receiveSuccess - // https://github.com/celo-org/celo-monorepo/issues/9826 - - const parsedResponses: Array<{ - signerUrl: string - values: { - version: string - performedQueryCount: number - totalQuota: number - blockNumber?: number - warnings?: string[] - } - }> = [] - session.responses.forEach((response) => { - if (response.res.success) { - const { version, performedQueryCount, totalQuota, blockNumber, warnings } = response.res - parsedResponses.push({ - signerUrl: response.url, - values: { version, performedQueryCount, totalQuota, blockNumber, warnings }, - }) - } - }) - if (parsedResponses.length === 0) { - session.logger.warn('No successful signer responses found!') - return - } - - // log all responses if we notice any discrepancies to aid with debugging - const first = JSON.stringify(parsedResponses[0].values) - for (let i = 1; i < parsedResponses.length; i++) { - if (JSON.stringify(parsedResponses[i].values) !== first) { - session.logger.warn({ parsedResponses }, WarningMessage.SIGNER_RESPONSE_DISCREPANCIES) - session.warnings.push(WarningMessage.SIGNER_RESPONSE_DISCREPANCIES) - break - } - } - - // blockNumber - parsedResponses.forEach((res) => { - if (res.values.blockNumber === undefined) { - session.logger.warn( - { signerUrl: res.signerUrl }, - 'Signer responded with undefined blockNumber' - ) - } - }) - const sortedByBlockNumber = parsedResponses - .filter((res) => !!res.values.blockNumber) - .sort((a, b) => a.values.blockNumber! - b.values.blockNumber!) - if ( - sortedByBlockNumber.length && - sortedByBlockNumber[sortedByBlockNumber.length - 1].values.blockNumber! - - sortedByBlockNumber[0].values.blockNumber! >= - MAX_BLOCK_DISCREPANCY_THRESHOLD - ) { - session.logger.error( - { sortedByBlockNumber }, - WarningMessage.INCONSISTENT_SIGNER_BLOCK_NUMBERS - ) - session.warnings.push(WarningMessage.INCONSISTENT_SIGNER_BLOCK_NUMBERS) - } - - // totalQuota - const sortedByTotalQuota = parsedResponses.sort( - (a, b) => a.values.totalQuota - b.values.totalQuota - ) - if ( - sortedByTotalQuota[sortedByTotalQuota.length - 1].values.totalQuota - - sortedByTotalQuota[0].values.totalQuota >= - MAX_TOTAL_QUOTA_DISCREPANCY_THRESHOLD - ) { - session.logger.error( - { sortedByTotalQuota }, - WarningMessage.INCONSISTENT_SIGNER_QUOTA_MEASUREMENTS - ) - session.warnings.push(WarningMessage.INCONSISTENT_SIGNER_QUOTA_MEASUREMENTS) - } - - // performedQueryCount - const sortedByQueryCount = parsedResponses.sort( - (a, b) => a.values.performedQueryCount - b.values.performedQueryCount - ) - if ( - sortedByQueryCount[sortedByQueryCount.length - 1].values.performedQueryCount - - sortedByQueryCount[0].values.performedQueryCount >= - MAX_QUERY_COUNT_DISCREPANCY_THRESHOLD - ) { - session.logger.error( - { sortedByQueryCount }, - WarningMessage.INCONSISTENT_SIGNER_QUERY_MEASUREMENTS - ) - session.warnings.push(WarningMessage.INCONSISTENT_SIGNER_QUERY_MEASUREMENTS) - } - } - - logFailOpenResponses(session: Session | Session): void { - session.responses.forEach((response) => { - if (response.res.success) { - const { warnings } = response.res - if (warnings) { - warnings.forEach((warning) => { - switch (warning) { - case ErrorMessage.FAILING_OPEN: - case ErrorMessage.FAILURE_TO_GET_TOTAL_QUOTA: - case ErrorMessage.FAILURE_TO_GET_DEK: - session.logger.error( - { signerWarning: warning, service: response.url }, - WarningMessage.SIGNER_FAILED_OPEN - ) - default: - break - } - }) - } - } - }) - } -} diff --git a/packages/phone-number-privacy/combiner/src/pnp/services/threshold-state.ts b/packages/phone-number-privacy/combiner/src/pnp/services/threshold-state.ts deleted file mode 100644 index ee46bab51d4..00000000000 --- a/packages/phone-number-privacy/combiner/src/pnp/services/threshold-state.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { - PnpQuotaRequest, - PnpQuotaStatus, - SignMessageRequest, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import { Session } from '../../common/session' -import { MAX_TOTAL_QUOTA_DISCREPANCY_THRESHOLD } from '../../config' -export class PnpThresholdStateService { - findCombinerQuotaState(session: Session): PnpQuotaStatus { - const { threshold } = session.keyVersionInfo - const signerResponses = session.responses - .map((signerResponse) => signerResponse.res) - .filter((res) => res.success) as PnpQuotaStatus[] - const sortedResponses = signerResponses.sort( - (a, b) => b.totalQuota - b.performedQueryCount - (a.totalQuota - a.performedQueryCount) - ) - - const totalQuotaAvg = - sortedResponses.map((r) => r.totalQuota).reduce((a, b) => a + b) / sortedResponses.length - const totalQuotaStDev = Math.sqrt( - sortedResponses.map((r) => (r.totalQuota - totalQuotaAvg) ** 2).reduce((a, b) => a + b) / - sortedResponses.length - ) - if (totalQuotaStDev > MAX_TOTAL_QUOTA_DISCREPANCY_THRESHOLD) { - // TODO(2.0.0): add alerting for this - throw new Error(WarningMessage.INCONSISTENT_SIGNER_QUOTA_MEASUREMENTS) - } else if (totalQuotaStDev > 0) { - session.warnings.push( - WarningMessage.INCONSISTENT_SIGNER_QUOTA_MEASUREMENTS + - ', using threshold signer as best guess' - ) - } - - // TODO(2.0.0) currently this check is not needed, as checking for sufficient number of responses and - // filtering for successes is already done in the action. Consider adding back in based on the - // result of https://github.com/celo-org/celo-monorepo/issues/9826 - // if (signerResponses.length < threshold) { - // throw new Error('Insufficient number of successful signer responses') - // } - - const thresholdSigner = sortedResponses[threshold - 1] - return { - performedQueryCount: thresholdSigner.performedQueryCount, - totalQuota: thresholdSigner.totalQuota, - blockNumber: thresholdSigner.blockNumber, - } - } -} diff --git a/packages/phone-number-privacy/combiner/src/server.ts b/packages/phone-number-privacy/combiner/src/server.ts deleted file mode 100644 index 19ad593db14..00000000000 --- a/packages/phone-number-privacy/combiner/src/server.ts +++ /dev/null @@ -1,211 +0,0 @@ -import { ContractKit } from '@celo/contractkit' -import { - CombinerEndpoint, - Endpoint, - ErrorMessage, - KEY_VERSION_HEADER, - loggerMiddleware, - rootLogger, -} from '@celo/phone-number-privacy-common' -import Logger from 'bunyan' -import express, { Request, RequestHandler, Response } from 'express' -// tslint:disable-next-line: ordered-imports -import { PerformanceObserver, performance } from 'perf_hooks' -import { Controller } from './common/controller' -import { CombinerConfig, getCombinerVersion } from './config' -import { DomainDisableAction } from './domain/endpoints/disable/action' -import { DomainDisableIO } from './domain/endpoints/disable/io' -import { DomainQuotaAction } from './domain/endpoints/quota/action' -import { DomainQuotaIO } from './domain/endpoints/quota/io' -import { DomainSignAction } from './domain/endpoints/sign/action' -import { DomainSignIO } from './domain/endpoints/sign/io' -import { DomainThresholdStateService } from './domain/services/threshold-state' -import { PnpQuotaAction } from './pnp/endpoints/quota/action' -import { PnpQuotaIO } from './pnp/endpoints/quota/io' -import { PnpSignAction } from './pnp/endpoints/sign/action' -import { PnpSignIO } from './pnp/endpoints/sign/io' -import { LegacyPnpSignIO } from './pnp/endpoints/sign/io.legacy' -import { PnpThresholdStateService } from './pnp/services/threshold-state' - -require('events').EventEmitter.defaultMaxListeners = 15 - -export function startCombiner(config: CombinerConfig, kit: ContractKit) { - const logger = rootLogger(config.serviceName) - - logger.info('Creating combiner express server') - const app = express() - // TODO get logger to show accurate serviceName - // (https://github.com/celo-org/celo-monorepo/issues/9809) - app.use(express.json({ limit: '0.2mb' }) as RequestHandler, loggerMiddleware(config.serviceName)) - - // Enable cross origin resource sharing from any domain so ODIS can be interacted with from web apps - // - // Security note: Allowing unrestricted cross-origin requests is acceptable here because any authenticated actions - // must include a signature in the request body. In particular, ODIS _does not_ use cookies to transmit authentication - // data. If ODIS is altered to use cookies for authentication data, this CORS policy should be reconsidered. - app.use((_, res, next) => { - res.header('Access-Control-Allow-Origin', '*') - res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS') - res.header( - 'Access-Control-Allow-Headers', - `Origin, X-Requested-With, Content-Type, Accept, Authorization, ${KEY_VERSION_HEADER}` - ) - next() - }) - - app.get(CombinerEndpoint.STATUS, (_req, res) => { - res.status(200).json({ - version: getCombinerVersion(), - }) - }) - - const pnpThresholdStateService = new PnpThresholdStateService() - - const legacyPnpSign = new Controller( - new PnpSignAction( - config.phoneNumberPrivacy, - pnpThresholdStateService, - new LegacyPnpSignIO(config.phoneNumberPrivacy, kit) - ) - ) - app.post(CombinerEndpoint.LEGACY_PNP_SIGN, (req, res) => - meterResponse( - legacyPnpSign.handle.bind(legacyPnpSign), - req, - res, - CombinerEndpoint.LEGACY_PNP_SIGN, - config - ) - ) - - const pnpQuota = new Controller( - new PnpQuotaAction( - config.phoneNumberPrivacy, - pnpThresholdStateService, - new PnpQuotaIO(config.phoneNumberPrivacy, kit) - ) - ) - app.post(CombinerEndpoint.PNP_QUOTA, (req, res) => - meterResponse(pnpQuota.handle.bind(pnpQuota), req, res, CombinerEndpoint.PNP_QUOTA, config) - ) - - const pnpSign = new Controller( - new PnpSignAction( - config.phoneNumberPrivacy, - pnpThresholdStateService, - new PnpSignIO(config.phoneNumberPrivacy, kit) - ) - ) - app.post(CombinerEndpoint.PNP_SIGN, (req, res) => - meterResponse(pnpSign.handle.bind(pnpSign), req, res, CombinerEndpoint.PNP_SIGN, config) - ) - - const domainThresholdStateService = new DomainThresholdStateService(config.domains) - - const domainQuota = new Controller( - new DomainQuotaAction( - config.domains, - domainThresholdStateService, - new DomainQuotaIO(config.domains) - ) - ) - app.post(CombinerEndpoint.DOMAIN_QUOTA_STATUS, (req, res) => - meterResponse( - domainQuota.handle.bind(domainQuota), - req, - res, - CombinerEndpoint.DOMAIN_QUOTA_STATUS, - config - ) - ) - const domainSign = new Controller( - new DomainSignAction( - config.domains, - domainThresholdStateService, - new DomainSignIO(config.domains) - ) - ) - app.post(CombinerEndpoint.DOMAIN_SIGN, (req, res) => - meterResponse( - domainSign.handle.bind(domainSign), - req, - res, - CombinerEndpoint.DOMAIN_SIGN, - config - ) - ) - const domainDisable = new Controller( - new DomainDisableAction( - config.domains, - domainThresholdStateService, - new DomainDisableIO(config.domains) - ) - ) - app.post(CombinerEndpoint.DISABLE_DOMAIN, (req, res) => - meterResponse( - domainDisable.handle.bind(domainDisable), - req, - res, - CombinerEndpoint.DISABLE_DOMAIN, - config - ) - ) - - return app -} - -export async function meterResponse( - handler: (req: Request, res: Response) => Promise, - req: Request, - res: Response, - endpoint: Endpoint, - config: CombinerConfig -) { - if (!res.locals) { - res.locals = {} - } - const logger: Logger = loggerMiddleware(config.serviceName)(req, res) - logger.fields.endpoint = endpoint - logger.info({ req: req.body }, 'Request received') - const eventLoopLagMeasurementStart = Date.now() - setTimeout(() => { - const eventLoopLag = Date.now() - eventLoopLagMeasurementStart - logger.info({ eventLoopLag }, 'Measure event loop lag') - }) - const startMark = `Begin ${endpoint}` - const endMark = `End ${endpoint}` - const entryName = `${endpoint} latency` - - const obs = new PerformanceObserver((list) => { - const entry = list.getEntriesByName(entryName)[0] - if (entry) { - logger.info({ latency: entry }, 'e2e response latency measured') - } - }) - obs.observe({ entryTypes: ['measure'], buffered: false }) - - performance.mark(startMark) - await handler(req, res) - .then(() => { - logger.info({ res }, 'Response sent') - }) - .catch((err) => { - logger.error(ErrorMessage.CAUGHT_ERROR_IN_ENDPOINT_HANDLER) - logger.error(err) - if (!res.headersSent) { - logger.info('Responding with error in outer endpoint handler') - res.status(500).json({ - success: false, - error: ErrorMessage.UNKNOWN_ERROR, - }) - } else { - logger.error(ErrorMessage.ERROR_AFTER_RESPONSE_SENT) - } - }) - .finally(() => { - performance.mark(endMark) - performance.measure(entryName, startMark, endMark) - performance.clearMarks() - obs.disconnect() - }) -} diff --git a/packages/phone-number-privacy/combiner/test/end-to-end/domain.test.ts b/packages/phone-number-privacy/combiner/test/end-to-end/domain.test.ts deleted file mode 100644 index 6ce612b026e..00000000000 --- a/packages/phone-number-privacy/combiner/test/end-to-end/domain.test.ts +++ /dev/null @@ -1,338 +0,0 @@ -import { - BackupErrorTypes, - buildOdisDomain, - E2E_TESTING_ALFAJORES_CONFIG, - NO_QUOTA_ALFAJORES_CONFIG, - odisHardenKey, - odisQueryAuthorizer, - requestOdisDomainQuotaStatus, -} from '@celo/encrypted-backup' -import { OdisUtils } from '@celo/identity' -import { - CombinerEndpoint, - DisableDomainRequest, - disableDomainRequestEIP712, - disableDomainResponseSchema, - DisableDomainResponseSuccess, - domainHash, - DomainQuotaStatusResponseSuccess, - DomainRequestTypeTag, - DomainRestrictedSignatureRequest, - domainRestrictedSignatureRequestEIP712, - domainRestrictedSignatureResponseSchema, - DomainRestrictedSignatureResponseSuccess, - genSessionID, - KEY_VERSION_HEADER, - PoprfClient, - SequentialDelayDomain, - SequentialDelayDomainStateSchema, - TestUtils, -} from '@celo/phone-number-privacy-common' -import { defined, noNumber, noString } from '@celo/utils/lib/sign-typed-data-utils' -import * as crypto from 'crypto' -import 'isomorphic-fetch' -import { getCombinerVersion } from '../../src' -import { getTestContextName } from './resources' - -require('dotenv').config() - -jest.setTimeout(60000) - -const { ErrorMessages, getServiceContext, OdisAPI } = OdisUtils.Query - -const SERVICE_CONTEXT = getServiceContext(getTestContextName(), OdisAPI.DOMAIN) -const combinerUrl = SERVICE_CONTEXT.odisUrl -const fullNodeUrl = process.env.ODIS_BLOCKCHAIN_PROVIDER - -const authorizer = odisQueryAuthorizer(Buffer.from('combiner e2e authorizer test seed')) - -const domain = buildOdisDomain(E2E_TESTING_ALFAJORES_CONFIG.odis!, authorizer.address) - -const expectedVersion = getCombinerVersion() - -describe(`Running against service deployed at ${combinerUrl} w/ blockchain provider ${fullNodeUrl}`, () => { - it('Service is deployed at correct version', async () => { - const response = await fetch(combinerUrl + CombinerEndpoint.STATUS, { - method: 'GET', - }) - const body = await response.json() - // This checks against local package.json version, change if necessary - expect(body.version).toBe(expectedVersion) - }) - - describe(`${CombinerEndpoint.DOMAIN_QUOTA_STATUS}`, () => { - const testThatValidRequestSucceeds = async () => { - const res = await requestOdisDomainQuotaStatus( - domain, - SERVICE_CONTEXT, - genSessionID(), - authorizer.wallet - ) - - expect(res.ok).toBe(true) - if (res.ok) { - expect(res.result).toStrictEqual({ - success: true, - version: expectedVersion, - status: { - timer: res.result.status.timer, - counter: res.result.status.counter, - now: res.result.status.now, - disabled: false, - }, - }) - } - } - - it('Should succeed on valid request', async () => { - await testThatValidRequestSucceeds() - }) - - it('Should succeed on repeated valid requests', async () => { - await testThatValidRequestSucceeds() - await testThatValidRequestSucceeds() - }) - }) - - describe(`${CombinerEndpoint.DOMAIN_SIGN}`, () => { - const testThatValidRequestSucceeds = async () => { - const res = await odisHardenKey( - Buffer.from('password'), - domain, - SERVICE_CONTEXT, - authorizer.wallet - ) - // odisHardenKey verifies the signature against the service public key - expect(res.ok).toBe(true) - } - - it('Should succeed on valid request', async () => { - await testThatValidRequestSucceeds() - }) - - it('Should succeed on repeated valid request', async () => { - await testThatValidRequestSucceeds() - await testThatValidRequestSucceeds() - }) - - const signatureRequest = async ( - keyVersion: number, - nonce: number, - _domain: SequentialDelayDomain = domain - ): Promise<[DomainRestrictedSignatureRequest, PoprfClient]> => { - const poprfClient = new PoprfClient( - Buffer.from(TestUtils.Values.DOMAINS_THRESHOLD_DEV_PUBKEYS[keyVersion - 1], 'base64'), - domainHash(_domain), - Buffer.from('test message', 'utf8') - ) - - const req: DomainRestrictedSignatureRequest = { - type: DomainRequestTypeTag.SIGN, - domain: _domain, - options: { - signature: noString, - nonce: defined(nonce), - }, - blindedMessage: poprfClient.blindedMessage.toString('base64'), - sessionID: defined(genSessionID()), - } - req.options.signature = defined( - await authorizer.wallet.signTypedData( - authorizer.address, - domainRestrictedSignatureRequestEIP712(req) - ) - ) - return [req, poprfClient] - } - - for (let i = 1; i <= 2; i++) { - it(`Should succeed on valid request with key version header ${i}`, async () => { - const quotaRes = await requestOdisDomainQuotaStatus( - domain, - SERVICE_CONTEXT, - genSessionID(), - authorizer.wallet - ) - - let nonce = 0 - - expect(quotaRes.ok).toBe(true) - if (quotaRes.ok) { - expect(quotaRes.result).toStrictEqual({ - success: true, - version: expectedVersion, - status: { - timer: quotaRes.result.status.timer, - counter: quotaRes.result.status.counter, - now: quotaRes.result.status.now, - disabled: false, - }, - }) - nonce = quotaRes.result.status.counter - } - - const keyVersion = 1 - const [req, _] = await signatureRequest(keyVersion, nonce) - const res = await OdisUtils.Query.sendOdisDomainRequest( - req, - SERVICE_CONTEXT, - CombinerEndpoint.DOMAIN_SIGN, - domainRestrictedSignatureResponseSchema(SequentialDelayDomainStateSchema), - { - [KEY_VERSION_HEADER]: keyVersion.toString(), - } - ) - - expect(res.success).toBe(true) - if (res.success) { - expect(res).toStrictEqual({ - success: true, - version: expectedVersion, - signature: res.signature, - status: { - timer: res.status.timer, - counter: res.status.counter, - now: res.status.now, - disabled: false, - }, - }) - } - }) - } - - it('Should throw on invalid authentication', async () => { - const quotaRes = await requestOdisDomainQuotaStatus( - domain, - SERVICE_CONTEXT, - genSessionID(), - authorizer.wallet - ) - - let nonce = 0 - - expect(quotaRes.ok).toBe(true) - if (quotaRes.ok) { - expect(quotaRes.result).toStrictEqual({ - success: true, - version: expectedVersion, - status: { - timer: quotaRes.result.status.timer, - counter: quotaRes.result.status.counter, - now: quotaRes.result.status.now, - disabled: false, - }, - }) - nonce = quotaRes.result.status.counter - } - - const keyVersion = 1 - const [req, _] = await signatureRequest(keyVersion, nonce) - - req.domain.salt = defined('badSalt') - - await expect( - OdisUtils.Query.sendOdisDomainRequest( - req, - SERVICE_CONTEXT, - CombinerEndpoint.DOMAIN_SIGN, - domainRestrictedSignatureResponseSchema(SequentialDelayDomainStateSchema), - { - [KEY_VERSION_HEADER]: keyVersion.toString(), - } - ) - ).rejects.toThrow(ErrorMessages.ODIS_AUTH_ERROR) - }) - - it('Should return error on out of quota', async () => { - const noQuotaDomain = buildOdisDomain(NO_QUOTA_ALFAJORES_CONFIG.odis!, authorizer.address) - const res = await odisHardenKey( - Buffer.from('password'), - noQuotaDomain, - SERVICE_CONTEXT, - authorizer.wallet - ) - expect(res.ok).toBe(false) - if (!res.ok) { - expect(res.error.errorType).toEqual(BackupErrorTypes.ODIS_RATE_LIMITING_ERROR) - } - }) - }) - - describe(`${CombinerEndpoint.DISABLE_DOMAIN}`, () => { - const domainForDisabling = buildOdisDomain( - E2E_TESTING_ALFAJORES_CONFIG.odis!, - authorizer.address, - 'e2e testing, okay to disable ' + crypto.randomBytes(16).toString('base64') - ) - const disableRequest = async (): Promise> => { - const req: DisableDomainRequest = { - type: DomainRequestTypeTag.DISABLE, - domain: domainForDisabling, - options: { - signature: noString, - nonce: noNumber, - }, - sessionID: defined(genSessionID()), - } - req.options.signature = defined( - await authorizer.wallet.signTypedData(authorizer.address, disableDomainRequestEIP712(req)) - ) - return req - } - - const testThatDomainIsNotAlreadyDisabled = async () => { - const quotaRes = await requestOdisDomainQuotaStatus( - domainForDisabling, - SERVICE_CONTEXT, - genSessionID(), - authorizer.wallet - ) - expect(quotaRes.ok).toBe(true) - if (quotaRes.ok) { - expect(quotaRes.result).toStrictEqual({ - success: true, - version: expectedVersion, - status: { - timer: quotaRes.result.status.timer, - counter: quotaRes.result.status.counter, - now: quotaRes.result.status.now, - disabled: false, // checking that domain is not already disabled - }, - }) - } - } - - const testThatValidRequestSucceeds = async () => { - const res = await OdisUtils.Query.sendOdisDomainRequest( - await disableRequest(), - SERVICE_CONTEXT, - CombinerEndpoint.DISABLE_DOMAIN, - disableDomainResponseSchema(SequentialDelayDomainStateSchema) - ) - - expect(res.success).toBe(true) - if (res.success) { - expect(res).toStrictEqual({ - success: true, - version: expectedVersion, - status: { - timer: res.status.timer, - now: res.status.now, - disabled: true, // checking that domain is now disabled - counter: res.status.counter, - }, - }) - } - } - - it('Should succeed on valid request', async () => { - await testThatDomainIsNotAlreadyDisabled() - await testThatValidRequestSucceeds() - }) - - it('Should succeed on repeated valid request', async () => { - await testThatValidRequestSucceeds() - await testThatValidRequestSucceeds() - }) - }) -}) diff --git a/packages/phone-number-privacy/combiner/test/end-to-end/legacypnp.test.ts b/packages/phone-number-privacy/combiner/test/end-to-end/legacypnp.test.ts deleted file mode 100644 index 6d8cd160148..00000000000 --- a/packages/phone-number-privacy/combiner/test/end-to-end/legacypnp.test.ts +++ /dev/null @@ -1,376 +0,0 @@ -import { newKit } from '@celo/contractkit' -import { OdisUtils } from '@celo/identity' -import { PhoneNumberHashDetails } from '@celo/identity/lib/odis/phone-number-identifier' -import { - ErrorMessages, - getOdisPnpRequestAuth, - getServiceContext, - OdisAPI, - WalletKeySigner, -} from '@celo/identity/lib/odis/query' -import { - AuthenticationMethod, - CombinerEndpoint, - LegacySignMessageRequest, - SignMessageResponseSchema, - SignMessageResponseSuccess, -} from '@celo/phone-number-privacy-common' -import threshold_bls from 'blind-threshold-bls' -import { randomBytes } from 'crypto' -import 'isomorphic-fetch' -import { getCombinerVersion } from '../../src' -import { getBlindedPhoneNumber } from '../utils' -import { - ACCOUNT_ADDRESS, - ACCOUNT_ADDRESS_NO_QUOTA, - DEFAULT_FORNO_URL, - dekAuthSigner, - getTestContextName, - PHONE_NUMBER, - walletAuthSigner, -} from './resources' - -require('dotenv').config() - -jest.setTimeout(60000) - -const SERVICE_CONTEXT = getServiceContext(getTestContextName(), OdisAPI.PNP) -const combinerUrl = SERVICE_CONTEXT.odisUrl -const fullNodeUrl = process.env.ODIS_BLOCKCHAIN_PROVIDER - -const expectedVersion = getCombinerVersion() - -describe(`Running against service deployed at ${combinerUrl} w/ blockchain provider ${fullNodeUrl}`, () => { - it('Service is deployed at correct version', async () => { - const response = await fetch(combinerUrl + CombinerEndpoint.STATUS, { - method: 'GET', - }) - const body = await response.json() - // This checks against local package.json version, change if necessary - expect(body.version).toBe(expectedVersion) - }) - - describe(`${CombinerEndpoint.LEGACY_PNP_SIGN}`, () => { - it('Should succeed when authenticated with WALLET_KEY', async () => { - const res = await OdisUtils.PhoneNumberIdentifier.getPhoneNumberIdentifier( - PHONE_NUMBER, - ACCOUNT_ADDRESS, - walletAuthSigner, - SERVICE_CONTEXT, - undefined, - undefined, - undefined, - undefined, - undefined, - CombinerEndpoint.LEGACY_PNP_SIGN - ) - threshold_bls.verify( - Buffer.from(SERVICE_CONTEXT.odisPubKey, 'base64'), - Buffer.from(PHONE_NUMBER), - Buffer.from(res.unblindedSignature!, 'base64') - ) - }) - - it('Should succeed when authenticated with DEK', async () => { - const res = await OdisUtils.PhoneNumberIdentifier.getPhoneNumberIdentifier( - PHONE_NUMBER, - ACCOUNT_ADDRESS, - dekAuthSigner(0), - SERVICE_CONTEXT, - undefined, - undefined, - undefined, - undefined, - undefined, - CombinerEndpoint.LEGACY_PNP_SIGN - ) - threshold_bls.verify( - Buffer.from(SERVICE_CONTEXT.odisPubKey, 'base64'), - Buffer.from(PHONE_NUMBER), - Buffer.from(res.unblindedSignature!, 'base64') - ) - }) - - it('Should succeed on repeated valid requests', async () => { - const res1 = await OdisUtils.PhoneNumberIdentifier.getPhoneNumberIdentifier( - PHONE_NUMBER, - ACCOUNT_ADDRESS, - dekAuthSigner(0), - SERVICE_CONTEXT, - undefined, - undefined, - undefined, - undefined, - undefined, - CombinerEndpoint.LEGACY_PNP_SIGN - ) - threshold_bls.verify( - Buffer.from(SERVICE_CONTEXT.odisPubKey, 'base64'), - Buffer.from(PHONE_NUMBER), - Buffer.from(res1.unblindedSignature!, 'base64') - ) - const res2 = await OdisUtils.PhoneNumberIdentifier.getPhoneNumberIdentifier( - PHONE_NUMBER, - ACCOUNT_ADDRESS, - dekAuthSigner(0), - SERVICE_CONTEXT, - undefined, - undefined, - undefined, - undefined, - undefined, - CombinerEndpoint.LEGACY_PNP_SIGN - ) - expect(res2).toStrictEqual(res1) - }) - - it('Should increment performedQueryCount on success', async () => { - const req: LegacySignMessageRequest = { - account: ACCOUNT_ADDRESS, - blindedQueryPhoneNumber: getBlindedPhoneNumber(PHONE_NUMBER, randomBytes(32)), - authenticationMethod: dekAuthSigner(0).authenticationMethod, - } - const res1 = (await OdisUtils.Query.queryOdis( - req, - SERVICE_CONTEXT, - CombinerEndpoint.LEGACY_PNP_SIGN, - SignMessageResponseSchema, - { - Authorization: await getOdisPnpRequestAuth(req, dekAuthSigner(0)), - } - )) as SignMessageResponseSuccess - expect(res1.success).toBe(true) - const req2 = req - req2.blindedQueryPhoneNumber = getBlindedPhoneNumber(PHONE_NUMBER, randomBytes(32)) - const res2 = (await OdisUtils.Query.queryOdis( - req2, - SERVICE_CONTEXT, - CombinerEndpoint.LEGACY_PNP_SIGN, - SignMessageResponseSchema, - { - Authorization: await getOdisPnpRequestAuth(req, dekAuthSigner(0)), - } - )) as SignMessageResponseSuccess - - expect(res2.success).toBe(true) - // There may be warnings but that's ok - expect(res2).toMatchObject({ - success: true, - version: expectedVersion, - signature: res2.signature, - performedQueryCount: res1.performedQueryCount + 1, - totalQuota: res1.totalQuota, - blockNumber: res2.blockNumber, - }) - }) - - it('Should not increment performedQueryCount on replayed request when using DEK auth', async () => { - const req: LegacySignMessageRequest = { - account: ACCOUNT_ADDRESS, - blindedQueryPhoneNumber: getBlindedPhoneNumber(PHONE_NUMBER, randomBytes(32)), - authenticationMethod: dekAuthSigner(0).authenticationMethod, - } - const res1 = (await OdisUtils.Query.queryOdis( - req, - SERVICE_CONTEXT, - CombinerEndpoint.LEGACY_PNP_SIGN, - SignMessageResponseSchema, - { - Authorization: await getOdisPnpRequestAuth(req, dekAuthSigner(0)), - } - )) as SignMessageResponseSuccess - expect(res1.success).toBe(true) - const res2 = (await OdisUtils.Query.queryOdis( - req, - SERVICE_CONTEXT, - CombinerEndpoint.LEGACY_PNP_SIGN, - SignMessageResponseSchema, - { - Authorization: await getOdisPnpRequestAuth(req, dekAuthSigner(0)), - } - )) as SignMessageResponseSuccess - expect(res2.success).toBe(true) - // There may be warnings but that's ok - expect(res2).toMatchObject({ - success: true, - version: expectedVersion, - signature: res2.signature, - performedQueryCount: res1.performedQueryCount, - totalQuota: res1.totalQuota, - blockNumber: res2.blockNumber, - }) - }) - - for (let i = 1; i <= 2; i++) { - it(`Should succeed on valid request with key version header ${i}`, async () => { - const res = await OdisUtils.PhoneNumberIdentifier.getPhoneNumberIdentifier( - PHONE_NUMBER, - ACCOUNT_ADDRESS, - dekAuthSigner(0), - SERVICE_CONTEXT, - undefined, - undefined, - undefined, - undefined, - i, - CombinerEndpoint.LEGACY_PNP_SIGN - ) - threshold_bls.verify( - Buffer.from(SERVICE_CONTEXT.odisPubKey, 'base64'), - Buffer.from(PHONE_NUMBER), - Buffer.from(res.unblindedSignature!, 'base64') - ) - }) - } - - it(`Should succeed on invalid key version`, async () => { - const res = await OdisUtils.PhoneNumberIdentifier.getPhoneNumberIdentifier( - PHONE_NUMBER, - ACCOUNT_ADDRESS, - dekAuthSigner(0), - SERVICE_CONTEXT, - undefined, - undefined, - undefined, - undefined, - 1.5, - CombinerEndpoint.LEGACY_PNP_SIGN - ) - threshold_bls.verify( - Buffer.from(SERVICE_CONTEXT.odisPubKey, 'base64'), - Buffer.from(PHONE_NUMBER), - Buffer.from(res.unblindedSignature!, 'base64') - ) - }) - - it(`Should reject to throw ${ErrorMessages.ODIS_INPUT_ERROR} on unsupported key version`, async () => { - await expect( - OdisUtils.PhoneNumberIdentifier.getPhoneNumberIdentifier( - PHONE_NUMBER, - ACCOUNT_ADDRESS, - dekAuthSigner(0), - SERVICE_CONTEXT, - undefined, - undefined, - undefined, - undefined, - 10, - CombinerEndpoint.LEGACY_PNP_SIGN - ) - ).rejects.toThrow(ErrorMessages.ODIS_INPUT_ERROR) - }) - - it(`Should reject to throw ${ErrorMessages.ODIS_INPUT_ERROR} on invalid address`, async () => { - await expect( - OdisUtils.PhoneNumberIdentifier.getPhoneNumberIdentifier( - PHONE_NUMBER, - 'not an address', - dekAuthSigner(0), - SERVICE_CONTEXT, - undefined, - undefined, - undefined, - undefined, - 1, - CombinerEndpoint.LEGACY_PNP_SIGN - ) - ).rejects.toThrow(ErrorMessages.ODIS_INPUT_ERROR) - }) - - it(`Should reject to throw ${ErrorMessages.ODIS_INPUT_ERROR} on invalid phone number`, async () => { - await expect( - OdisUtils.PhoneNumberIdentifier.getPhoneNumberIdentifier( - 'not a phone number', - ACCOUNT_ADDRESS, - dekAuthSigner(0), - SERVICE_CONTEXT, - undefined, - undefined, - undefined, - undefined, - 1, - CombinerEndpoint.LEGACY_PNP_SIGN - ) - ).rejects.toThrow('Invalid phone number: not a phone number') - }) - - it(`Should reject to throw 'unknown account' with invalid WALLET_KEY auth`, async () => { - const badWalletAuthSigner: WalletKeySigner = { - authenticationMethod: AuthenticationMethod.WALLET_KEY, - contractKit: newKit(DEFAULT_FORNO_URL), // doesn't have any private keys - } - await expect( - OdisUtils.PhoneNumberIdentifier.getPhoneNumberIdentifier( - PHONE_NUMBER, - ACCOUNT_ADDRESS, - badWalletAuthSigner, - SERVICE_CONTEXT, - undefined, - undefined, - undefined, - undefined, - undefined, - CombinerEndpoint.LEGACY_PNP_SIGN - ) - ).rejects.toThrow('unknown account') - }) - - it(`Should reject to throw ${ErrorMessages.ODIS_AUTH_ERROR} with invalid WALLET_KEY auth`, async () => { - const req: LegacySignMessageRequest = { - account: ACCOUNT_ADDRESS, - blindedQueryPhoneNumber: getBlindedPhoneNumber(PHONE_NUMBER, randomBytes(32)), - authenticationMethod: walletAuthSigner.authenticationMethod, - } - await expect( - OdisUtils.Query.queryOdis( - req, - SERVICE_CONTEXT, - CombinerEndpoint.LEGACY_PNP_SIGN, - SignMessageResponseSchema, - { - Authorization: await walletAuthSigner.contractKit.connection.sign( - JSON.stringify(req), - ACCOUNT_ADDRESS_NO_QUOTA - ), - } - ) - ).rejects.toThrow(ErrorMessages.ODIS_AUTH_ERROR) - }) - - it(`Should reject to throw ${ErrorMessages.ODIS_AUTH_ERROR} with invalid DEK auth`, async () => { - await expect( - OdisUtils.PhoneNumberIdentifier.getPhoneNumberIdentifier( - PHONE_NUMBER, - ACCOUNT_ADDRESS, - dekAuthSigner(1), - SERVICE_CONTEXT, - undefined, - undefined, - undefined, - undefined, - undefined, - CombinerEndpoint.LEGACY_PNP_SIGN - ) - ).rejects.toThrow(ErrorMessages.ODIS_AUTH_ERROR) - }) - - it(`Should reject to throw ${ErrorMessages.ODIS_QUOTA_ERROR} when account has no quota`, async () => { - // Ensure it's not a replayed request. - const unusedPN = `+1${Date.now()}` - await expect( - OdisUtils.PhoneNumberIdentifier.getPhoneNumberIdentifier( - unusedPN, - ACCOUNT_ADDRESS_NO_QUOTA, - dekAuthSigner(0), - SERVICE_CONTEXT, - undefined, - undefined, - undefined, - undefined, - undefined, - CombinerEndpoint.LEGACY_PNP_SIGN - ) - ).rejects.toThrow(ErrorMessages.ODIS_QUOTA_ERROR) - }) - }) -}) diff --git a/packages/phone-number-privacy/combiner/test/end-to-end/pnp.test.ts b/packages/phone-number-privacy/combiner/test/end-to-end/pnp.test.ts deleted file mode 100644 index 3efa088bb66..00000000000 --- a/packages/phone-number-privacy/combiner/test/end-to-end/pnp.test.ts +++ /dev/null @@ -1,454 +0,0 @@ -import { StableToken } from '@celo/contractkit' -import { OdisUtils } from '@celo/identity' -import { ErrorMessages, getServiceContext, OdisAPI } from '@celo/identity/lib/odis/query' -import { PnpClientQuotaStatus } from '@celo/identity/lib/odis/quota' -import { - CombinerEndpoint, - PnpQuotaRequest, - PnpQuotaResponseSchema, - SignMessageRequest, - SignMessageResponseSchema, -} from '@celo/phone-number-privacy-common' -import threshold_bls from 'blind-threshold-bls' -import { randomBytes } from 'crypto' -import 'isomorphic-fetch' -import { config as signerConfig } from '../../../signer/src/config' -import { getCombinerVersion } from '../../src' -import { - ACCOUNT_ADDRESS, - ACCOUNT_ADDRESS_NO_QUOTA, - BLINDED_PHONE_NUMBER, - dekAuthSigner, - getTestContextName, - PHONE_NUMBER, - walletAuthSigner, -} from './resources' - -const { IdentifierPrefix } = OdisUtils.Identifier - -require('dotenv').config() - -jest.setTimeout(60000) - -const SERVICE_CONTEXT = getServiceContext(getTestContextName(), OdisAPI.PNP) -const combinerUrl = SERVICE_CONTEXT.odisUrl -const fullNodeUrl = process.env.ODIS_BLOCKCHAIN_PROVIDER - -const expectedVersion = getCombinerVersion() - -describe(`Running against service deployed at ${combinerUrl} w/ blockchain provider ${fullNodeUrl}`, () => { - it('Service is deployed at correct version', async () => { - const response = await fetch(combinerUrl + CombinerEndpoint.STATUS, { - method: 'GET', - }) - const body = await response.json() - // This checks against local package.json version, change if necessary - expect(body.version).toBe(expectedVersion) - }) - - describe(`${CombinerEndpoint.PNP_QUOTA}`, () => { - it('Should succeed when authenticated with WALLET_KEY', async () => { - const res = await OdisUtils.Quota.getPnpQuotaStatus( - ACCOUNT_ADDRESS, - walletAuthSigner, - SERVICE_CONTEXT - ) - expect(res).toStrictEqual({ - version: expectedVersion, - performedQueryCount: res.performedQueryCount, - totalQuota: res.totalQuota, - remainingQuota: res.totalQuota - res.performedQueryCount, - blockNumber: res.blockNumber, - warnings: [], - }) - }) - - it('Should succeed when authenticated with DEK', async () => { - const res = await OdisUtils.Quota.getPnpQuotaStatus( - ACCOUNT_ADDRESS, - dekAuthSigner(0), - SERVICE_CONTEXT - ) - expect(res).toStrictEqual({ - version: expectedVersion, - performedQueryCount: res.performedQueryCount, - totalQuota: res.totalQuota, - remainingQuota: res.totalQuota - res.performedQueryCount, - blockNumber: res.blockNumber, - warnings: [], - }) - }) - - it('Should succeed on repeated valid requests', async () => { - const res1 = await OdisUtils.Quota.getPnpQuotaStatus( - ACCOUNT_ADDRESS, - dekAuthSigner(0), - SERVICE_CONTEXT - ) - const expectedRes: PnpClientQuotaStatus = { - version: expectedVersion, - performedQueryCount: res1.performedQueryCount, - totalQuota: res1.totalQuota, - remainingQuota: res1.totalQuota - res1.performedQueryCount, - blockNumber: res1.blockNumber, - warnings: [], - } - expect(res1).toStrictEqual(expectedRes) - const res2 = await OdisUtils.Quota.getPnpQuotaStatus( - ACCOUNT_ADDRESS, - dekAuthSigner(0), - SERVICE_CONTEXT - ) - expectedRes.blockNumber = res2.blockNumber - expect(res2).toStrictEqual(expectedRes) - }) - - it(`Should reject to throw ${ErrorMessages.ODIS_INPUT_ERROR} with invalid address`, async () => { - await expect( - OdisUtils.Quota.getPnpQuotaStatus('not an address', dekAuthSigner(0), SERVICE_CONTEXT) - ).rejects.toThrow(ErrorMessages.ODIS_INPUT_ERROR) - }) - - it(`Should reject to throw ${ErrorMessages.ODIS_AUTH_ERROR} with invalid WALLET_KEY auth`, async () => { - const req: PnpQuotaRequest = { - account: ACCOUNT_ADDRESS, - authenticationMethod: walletAuthSigner.authenticationMethod, - } - await expect( - OdisUtils.Query.queryOdis( - req, - SERVICE_CONTEXT, - CombinerEndpoint.PNP_QUOTA, - PnpQuotaResponseSchema, - { - Authorization: await walletAuthSigner.contractKit.connection.sign( - JSON.stringify(req), - ACCOUNT_ADDRESS_NO_QUOTA - ), - } - ) - ).rejects.toThrow(ErrorMessages.ODIS_AUTH_ERROR) - }) - - it(`Should reject to throw ${ErrorMessages.ODIS_AUTH_ERROR} with invalid DEK auth`, async () => { - await expect( - OdisUtils.Quota.getPnpQuotaStatus( - ACCOUNT_ADDRESS, - dekAuthSigner(1), // DEK auth signer doesn't match the registered DEK for ACCOUNT_ADDRESS - SERVICE_CONTEXT - ) - ).rejects.toThrow(ErrorMessages.ODIS_AUTH_ERROR) - }) - }) - - describe(`${CombinerEndpoint.PNP_SIGN}`, () => { - describe('new requests', () => { - beforeAll(async () => { - // Replenish quota for ACCOUNT_ADDRESS - // If this fails, may be necessary to faucet ACCOUNT_ADDRESS more funds - const numQueriesToReplenish = 2 - const amountInWei = signerConfig.quota.queryPriceInCUSD - .times(1e18) - .times(numQueriesToReplenish) - .toString() - const stableToken = await walletAuthSigner.contractKit.contracts.getStableToken( - StableToken.cUSD - ) - const odisPayments = await walletAuthSigner.contractKit.contracts.getOdisPayments() - await stableToken - .approve(odisPayments.address, amountInWei) - .sendAndWaitForReceipt({ from: ACCOUNT_ADDRESS }) - await odisPayments - .payInCUSD(ACCOUNT_ADDRESS, amountInWei) - .sendAndWaitForReceipt({ from: ACCOUNT_ADDRESS }) - }) - - // Requests made for PHONE_NUMBER from ACCOUNT_ADDRESS & same blinding factor - // are replayed from previous test runs (for every run after the very first) - let startingPerformedQueryCount: number - let startingTotalQuota: number - beforeEach(async () => { - const res = await OdisUtils.Quota.getPnpQuotaStatus( - ACCOUNT_ADDRESS, - dekAuthSigner(0), - SERVICE_CONTEXT - ) - startingPerformedQueryCount = res.performedQueryCount - startingTotalQuota = res.totalQuota - }) - - it('Should increment performedQueryCount on success with DEK auth', async () => { - // Raw key is used as the blinding client's seed, so we need a new PN - // Create a fake PN that is always incrementing and shouldn't ever repeat - const unusedPN = `+1${Date.now()}` - await OdisUtils.Identifier.getObfuscatedIdentifier( - unusedPN, - IdentifierPrefix.PHONE_NUMBER, - ACCOUNT_ADDRESS, - dekAuthSigner(0), - SERVICE_CONTEXT - ) - const quotaRes = await OdisUtils.Quota.getPnpQuotaStatus( - ACCOUNT_ADDRESS, - dekAuthSigner(0), - SERVICE_CONTEXT - ) - expect(quotaRes).toStrictEqual({ - version: expectedVersion, - performedQueryCount: startingPerformedQueryCount + 1, - totalQuota: startingTotalQuota, - remainingQuota: startingTotalQuota - (startingPerformedQueryCount + 1), - blockNumber: quotaRes.blockNumber, - warnings: [], - }) - }) - - it('Should increment performedQueryCount on success with WALLET_KEY auth', async () => { - await OdisUtils.Identifier.getObfuscatedIdentifier( - PHONE_NUMBER, - IdentifierPrefix.PHONE_NUMBER, - ACCOUNT_ADDRESS, - walletAuthSigner, - SERVICE_CONTEXT, - Buffer.from(randomBytes(32)).toString('base64') - ) - const quotaRes = await OdisUtils.Quota.getPnpQuotaStatus( - ACCOUNT_ADDRESS, - walletAuthSigner, - SERVICE_CONTEXT - ) - expect(quotaRes).toStrictEqual({ - version: expectedVersion, - performedQueryCount: startingPerformedQueryCount + 1, - totalQuota: startingTotalQuota, - remainingQuota: startingTotalQuota - (startingPerformedQueryCount + 1), - blockNumber: quotaRes.blockNumber, - warnings: [], - }) - }) - }) - - describe('replayed requests', () => { - const replayedBlindingFactor = Buffer.from('test string for blinding factor').toString( - 'base64' - ) - beforeAll(async () => { - // Ensure that these are each called at least once for the first test runs - await OdisUtils.Identifier.getObfuscatedIdentifier( - PHONE_NUMBER, - IdentifierPrefix.PHONE_NUMBER, - ACCOUNT_ADDRESS, - walletAuthSigner, - SERVICE_CONTEXT, - replayedBlindingFactor - ) - await OdisUtils.Identifier.getObfuscatedIdentifier( - PHONE_NUMBER, - IdentifierPrefix.PHONE_NUMBER, - ACCOUNT_ADDRESS, - dekAuthSigner(0), - SERVICE_CONTEXT - ) - }) - - // Requests made for PHONE_NUMBER from ACCOUNT_ADDRESS - // are replayed from previous test runs (for every run after the very first) - let startingPerformedQueryCount: number - let startingTotalQuota: number - beforeEach(async () => { - const res = await OdisUtils.Quota.getPnpQuotaStatus( - ACCOUNT_ADDRESS, - dekAuthSigner(0), - SERVICE_CONTEXT - ) - startingPerformedQueryCount = res.performedQueryCount - startingTotalQuota = res.totalQuota - }) - - it('Should succeed and not update performedQueryCount when authenticated with WALLET_KEY', async () => { - const res = await OdisUtils.Identifier.getObfuscatedIdentifier( - PHONE_NUMBER, - IdentifierPrefix.PHONE_NUMBER, - ACCOUNT_ADDRESS, - walletAuthSigner, - SERVICE_CONTEXT, - replayedBlindingFactor - ) - threshold_bls.verify( - Buffer.from(SERVICE_CONTEXT.odisPubKey, 'base64'), - Buffer.from(PHONE_NUMBER), - Buffer.from(res.unblindedSignature!, 'base64') - ) - const quotaRes = await OdisUtils.Quota.getPnpQuotaStatus( - ACCOUNT_ADDRESS, - walletAuthSigner, - SERVICE_CONTEXT - ) - expect(quotaRes.performedQueryCount).toEqual(startingPerformedQueryCount) - expect(quotaRes.totalQuota).toEqual(startingTotalQuota) - }) - - it('Should succeed and not update performedQueryCount when authenticated with DEK', async () => { - const res = await OdisUtils.Identifier.getObfuscatedIdentifier( - PHONE_NUMBER, - IdentifierPrefix.PHONE_NUMBER, - ACCOUNT_ADDRESS, - dekAuthSigner(0), - SERVICE_CONTEXT - ) - threshold_bls.verify( - Buffer.from(SERVICE_CONTEXT.odisPubKey, 'base64'), - Buffer.from(PHONE_NUMBER), - Buffer.from(res.unblindedSignature!, 'base64') - ) - const quotaRes = await OdisUtils.Quota.getPnpQuotaStatus( - ACCOUNT_ADDRESS, - dekAuthSigner(0), - SERVICE_CONTEXT - ) - expect(quotaRes.performedQueryCount).toEqual(startingPerformedQueryCount) - expect(quotaRes.totalQuota).toEqual(startingTotalQuota) - }) - }) - - // NOTE: these are also replayed requests - for (let i = 1; i <= 2; i++) { - it(`Should succeed on valid request with key version header ${i}`, async () => { - const res = await OdisUtils.Identifier.getObfuscatedIdentifier( - PHONE_NUMBER, - IdentifierPrefix.PHONE_NUMBER, - ACCOUNT_ADDRESS, - dekAuthSigner(0), - SERVICE_CONTEXT, - undefined, - undefined, - undefined, - undefined, - i - ) - threshold_bls.verify( - Buffer.from(SERVICE_CONTEXT.odisPubKey, 'base64'), - Buffer.from(PHONE_NUMBER), - Buffer.from(res.unblindedSignature!, 'base64') - ) - }) - } - - it(`Should succeed on invalid key version`, async () => { - const res = await OdisUtils.Identifier.getObfuscatedIdentifier( - PHONE_NUMBER, - IdentifierPrefix.PHONE_NUMBER, - ACCOUNT_ADDRESS, - dekAuthSigner(0), - SERVICE_CONTEXT, - undefined, - undefined, - undefined, - undefined, - 1.5 - ) - threshold_bls.verify( - Buffer.from(SERVICE_CONTEXT.odisPubKey, 'base64'), - Buffer.from(PHONE_NUMBER), - Buffer.from(res.unblindedSignature!, 'base64') - ) - }) - - it(`Should reject to throw ${ErrorMessages.ODIS_INPUT_ERROR} on unsupported key version`, async () => { - await expect( - OdisUtils.Identifier.getObfuscatedIdentifier( - PHONE_NUMBER, - IdentifierPrefix.PHONE_NUMBER, - ACCOUNT_ADDRESS, - dekAuthSigner(0), - SERVICE_CONTEXT, - undefined, - undefined, - undefined, - undefined, - 10 - ) - ).rejects.toThrow(ErrorMessages.ODIS_INPUT_ERROR) - }) - - it(`Should reject to throw ${ErrorMessages.ODIS_INPUT_ERROR} on invalid address`, async () => { - await expect( - OdisUtils.Identifier.getObfuscatedIdentifier( - PHONE_NUMBER, - IdentifierPrefix.PHONE_NUMBER, - 'not an address', - dekAuthSigner(0), - SERVICE_CONTEXT, - undefined, - undefined, - undefined, - undefined, - 1 - ) - ).rejects.toThrow(ErrorMessages.ODIS_INPUT_ERROR) - }) - - it(`Should reject to throw ${ErrorMessages.ODIS_INPUT_ERROR} on invalid phone number`, async () => { - await expect( - OdisUtils.Identifier.getObfuscatedIdentifier( - '12345', - IdentifierPrefix.PHONE_NUMBER, - ACCOUNT_ADDRESS, - dekAuthSigner(0), - SERVICE_CONTEXT, - undefined, - undefined, - undefined, - undefined, - 1 - ) - ).rejects.toThrow('Invalid phone number: 12345') - }) - - it(`Should reject to throw ${ErrorMessages.ODIS_AUTH_ERROR} with invalid WALLET_KEY auth`, async () => { - const req: SignMessageRequest = { - account: ACCOUNT_ADDRESS, - blindedQueryPhoneNumber: BLINDED_PHONE_NUMBER, - authenticationMethod: walletAuthSigner.authenticationMethod, - } - await expect( - OdisUtils.Query.queryOdis( - req, - SERVICE_CONTEXT, - CombinerEndpoint.PNP_SIGN, - SignMessageResponseSchema, - { - Authorization: await walletAuthSigner.contractKit.connection.sign( - JSON.stringify(req), - ACCOUNT_ADDRESS_NO_QUOTA - ), - } - ) - ).rejects.toThrow(ErrorMessages.ODIS_AUTH_ERROR) - }) - - it(`Should reject to throw ${ErrorMessages.ODIS_AUTH_ERROR} with invalid DEK auth`, async () => { - await expect( - OdisUtils.Identifier.getObfuscatedIdentifier( - PHONE_NUMBER, - IdentifierPrefix.PHONE_NUMBER, - ACCOUNT_ADDRESS, - dekAuthSigner(1), // DEK auth signer doesn't match the registered DEK for ACCOUNT_ADDRESS - SERVICE_CONTEXT - ) - ).rejects.toThrow(ErrorMessages.ODIS_AUTH_ERROR) - }) - - it(`Should reject to throw ${ErrorMessages.ODIS_QUOTA_ERROR} when account has no quota`, async () => { - await expect( - OdisUtils.Identifier.getObfuscatedIdentifier( - PHONE_NUMBER, - IdentifierPrefix.PHONE_NUMBER, - ACCOUNT_ADDRESS_NO_QUOTA, - dekAuthSigner(0), - SERVICE_CONTEXT - ) - ).rejects.toThrow(ErrorMessages.ODIS_QUOTA_ERROR) - }) - }) -}) 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 deleted file mode 100644 index c90c4a7b9be..00000000000 --- a/packages/phone-number-privacy/combiner/test/end-to-end/resources.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { newKit } from '@celo/contractkit' -import { - EncryptionKeySigner, - OdisContextName, - WalletKeySigner, -} from '@celo/identity/lib/odis/query' -import { AuthenticationMethod } from '@celo/phone-number-privacy-common' -import { PhoneNumberUtils } from '@celo/phone-utils' -import { - ensureLeading0x, - normalizeAddressWith0x, - privateKeyToAddress, -} from '@celo/utils/lib/address' -import 'isomorphic-fetch' - -require('dotenv').config() - -export const getTestContextName = (): OdisContextName => { - switch (process.env.CONTEXT_NAME) { - case 'alfajores': - return OdisContextName.ALFAJORES - case 'staging': - return OdisContextName.STAGING - case 'mainnet': - return OdisContextName.MAINNET - default: - throw new Error('CONTEXT_NAME env var is undefined or invalid') - } -} - -/** - * CONSTS - */ -export const DEFAULT_FORNO_URL = - process.env.ODIS_BLOCKCHAIN_PROVIDER ?? 'https://alfajores-forno.celo-testnet.org' - -export const PRIVATE_KEY = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' -export const ACCOUNT_ADDRESS = normalizeAddressWith0x(privateKeyToAddress(PRIVATE_KEY)) // 0x1be31a94361a391bbafb2a4ccd704f57dc04d4bb - -export const PRIVATE_KEY_NO_QUOTA = - '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890000000' -export const ACCOUNT_ADDRESS_NO_QUOTA = privateKeyToAddress(PRIVATE_KEY_NO_QUOTA) - -export const PHONE_NUMBER = '+17777777777' -export const BLINDING_FACTOR = Buffer.from('0IsBvRfkBrkKCIW6HV0/T1zrzjQSe8wRyU3PKojCnww=', 'base64') -// BLINDED_PHONE_NUMBER value is dependent on PHONE_NUMBER AND BLINDING_FACTOR -// hardcoding to avoid importing blind_threshols_bls library -export const BLINDED_PHONE_NUMBER = - 'hZXDhpC5onzBSFa1agZ9vfHzqwJ/QeJg77NGvWiQG/sFWsvHETzZvdWr2GpF3QkB' - -export const PHONE_HASH_IDENTIFIER = PhoneNumberUtils.getPhoneHash(PHONE_NUMBER) - -export const CONTACT_PHONE_NUMBER = '+14155559999' -export const CONTACT_PHONE_NUMBERS = [CONTACT_PHONE_NUMBER] - -/** - * RESOURCES AND UTILS - */ -export const contractKit = newKit(DEFAULT_FORNO_URL) -contractKit.addAccount(PRIVATE_KEY_NO_QUOTA) -contractKit.addAccount(PRIVATE_KEY) -contractKit.defaultAccount = ACCOUNT_ADDRESS - -interface DEK { - privateKey: string - publicKey: string - address: string -} - -export const deks: DEK[] = [ - { - privateKey: 'bf8a2b73baf8402f8fe906ad3f42b560bf14b39f7df7797ece9e293d6f162188', - publicKey: '034846bc781cacdafc66f3a77aa9fc3c56a9dadcd683c72be3c446fee8da041070', - address: '0x7b33dF2607b85e3211738a49A6Ad6E8Ed4d13F6E', - }, - { - privateKey: '0975b0c565abc75b6638a749ea3008cb52676af3eabe4b80e19c516d82330364', - publicKey: '03b1ac8c445f0796978018c087b97e8213b32c39e6a8642ae63dce71da33a19f65', - address: '0x34332049B07Fab9a2e843A7C8991469d93cF6Ae6', - }, -] -// The following code can be used to generate more test DEKs -// const generateDEKs = (n: number): Promise => Promise.all([...Array(n).keys()].map( -// async () => await deriveDek(await generateMnemonic()) -// )) - -export const dekAuthSigner = (index: number): EncryptionKeySigner => { - return { - authenticationMethod: AuthenticationMethod.ENCRYPTION_KEY, - rawKey: ensureLeading0x(deks[index].privateKey), - } -} - -export const walletAuthSigner: WalletKeySigner = { - authenticationMethod: AuthenticationMethod.WALLET_KEY, - contractKit, -} diff --git a/packages/phone-number-privacy/combiner/test/end-to-end/tmpBackwardsCompatibility.test.ts b/packages/phone-number-privacy/combiner/test/end-to-end/tmpBackwardsCompatibility.test.ts deleted file mode 100644 index 9db2ebe2ed1..00000000000 --- a/packages/phone-number-privacy/combiner/test/end-to-end/tmpBackwardsCompatibility.test.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { newKit } from '@celo/contractkit' -import { OdisUtils } from '@celo/identity-prev' -import { getServiceContext, SignMessageRequest } from '@celo/identity-prev/lib/odis/query' -import { ErrorMessages } from '@celo/identity/lib/odis/query' -import { AuthenticationMethod, Endpoint } from '@celo/phone-number-privacy-common' -import { replenishQuota } from '@celo/phone-number-privacy-common/lib/test/utils' -import { genSessionID } from '@celo/phone-number-privacy-common/lib/utils/logger' -import { ensureLeading0x } from '@celo/utils/lib/address' -import 'isomorphic-fetch' -import { - ACCOUNT_ADDRESS, - ACCOUNT_ADDRESS_NO_QUOTA, - BLINDED_PHONE_NUMBER, - DEFAULT_FORNO_URL, - dekAuthSigner, - deks, - getTestContextName, - PHONE_NUMBER, - PRIVATE_KEY, - PRIVATE_KEY_NO_QUOTA, -} from './resources' - -require('dotenv').config() - -jest.setTimeout(60000) - -const contractKit = newKit(DEFAULT_FORNO_URL) -contractKit.addAccount(PRIVATE_KEY_NO_QUOTA) -contractKit.addAccount(PRIVATE_KEY) -contractKit.defaultAccount = ACCOUNT_ADDRESS - -const SERVICE_CONTEXT = getServiceContext(getTestContextName()) - -const fullNodeUrl = process.env.ODIS_BLOCKCHAIN_PROVIDER - -describe(`Running against service deployed at ${SERVICE_CONTEXT.odisUrl} w/ blockchain provider ${fullNodeUrl}`, () => { - beforeAll(async () => { - const dek0 = ensureLeading0x(deks[0].publicKey) - const accountsWrapper = await contractKit.contracts.getAccounts() - if ((await accountsWrapper.getDataEncryptionKey(ACCOUNT_ADDRESS)) !== dek0) { - await accountsWrapper - .setAccountDataEncryptionKey(dek0) - .sendAndWaitForReceipt({ from: ACCOUNT_ADDRESS }) - } - if ((await accountsWrapper.getDataEncryptionKey(ACCOUNT_ADDRESS_NO_QUOTA)) !== dek0) { - await accountsWrapper - .setAccountDataEncryptionKey(dek0) - .sendAndWaitForReceipt({ from: ACCOUNT_ADDRESS_NO_QUOTA }) - } - }) - - describe('Returns status ODIS_INPUT_ERROR', () => { - it('With invalid address', async () => { - const body: SignMessageRequest = { - account: '0x1234', - authenticationMethod: AuthenticationMethod.ENCRYPTION_KEY, - blindedQueryPhoneNumber: BLINDED_PHONE_NUMBER, - version: 'ignore', - sessionID: genSessionID(), - } - - await expect( - OdisUtils.Query.queryOdis(dekAuthSigner(0), body, SERVICE_CONTEXT, Endpoint.LEGACY_PNP_SIGN) - ).rejects.toThrow(ErrorMessages.ODIS_INPUT_ERROR) - }) - - it('With missing blindedQueryPhoneNumber', async () => { - const body: SignMessageRequest = { - account: ACCOUNT_ADDRESS, - authenticationMethod: AuthenticationMethod.ENCRYPTION_KEY, - blindedQueryPhoneNumber: '', - version: 'ignore', - sessionID: genSessionID(), - } - await expect( - OdisUtils.Query.queryOdis(dekAuthSigner(0), body, SERVICE_CONTEXT, Endpoint.LEGACY_PNP_SIGN) - ).rejects.toThrow(ErrorMessages.ODIS_INPUT_ERROR) - }) - }) - - describe('Returns ODIS_AUTH_ERROR', () => { - it('With invalid authentication', async () => { - const body: SignMessageRequest = { - account: ACCOUNT_ADDRESS, - authenticationMethod: AuthenticationMethod.WALLET_KEY, - blindedQueryPhoneNumber: BLINDED_PHONE_NUMBER, - version: 'ignore', - } - await expect( - OdisUtils.Query.queryOdis(dekAuthSigner(0), body, SERVICE_CONTEXT, Endpoint.LEGACY_PNP_SIGN) - ).rejects.toThrow(ErrorMessages.ODIS_AUTH_ERROR) - }) - }) - - describe('Returns ODIS_QUOTA_ERROR', () => { - it('When querying out of quota', async () => { - await expect( - OdisUtils.PhoneNumberIdentifier.getPhoneNumberIdentifier( - PHONE_NUMBER, - ACCOUNT_ADDRESS_NO_QUOTA, - dekAuthSigner(0), - SERVICE_CONTEXT - ) - ).rejects.toThrow(ErrorMessages.ODIS_QUOTA_ERROR) - }) - }) - - describe('With enough quota', () => { - // if these tests are failing, it may just be that the address needs to be fauceted: - // celotooljs account faucet --account 0x1be31a94361a391bbafb2a4ccd704f57dc04d4bb --dollar 1 --gold 1 -e --verbose - it('Returns sig when querying with unused and used request', async () => { - await replenishQuota(ACCOUNT_ADDRESS, contractKit) - const body: SignMessageRequest = { - account: ACCOUNT_ADDRESS, - authenticationMethod: AuthenticationMethod.ENCRYPTION_KEY, - blindedQueryPhoneNumber: BLINDED_PHONE_NUMBER, - version: 'ignore', - sessionID: genSessionID(), - } - // Query twice to test reusing the request - for (let i = 0; i < 2; i++) { - const result = OdisUtils.Query.queryOdis( - dekAuthSigner(0), - body, - SERVICE_CONTEXT, - Endpoint.LEGACY_PNP_SIGN - ) - await expect(result).resolves.toMatchObject({ success: true }) - } - }) - }) -}) diff --git a/packages/phone-number-privacy/combiner/test/integration/domain.test.ts b/packages/phone-number-privacy/combiner/test/integration/domain.test.ts deleted file mode 100644 index 5f7f1f1fe50..00000000000 --- a/packages/phone-number-privacy/combiner/test/integration/domain.test.ts +++ /dev/null @@ -1,1285 +0,0 @@ -import { - CombinerEndpoint, - DisableDomainRequest, - disableDomainRequestEIP712, - DisableDomainResponse, - domainHash, - DomainIdentifiers, - DomainQuotaStatusRequest, - domainQuotaStatusRequestEIP712, - DomainQuotaStatusResponse, - DomainRequestTypeTag, - DomainRestrictedSignatureRequest, - domainRestrictedSignatureRequestEIP712, - DomainRestrictedSignatureResponse, - ErrorMessage, - FULL_NODE_TIMEOUT_IN_MS, - genSessionID, - getContractKit, - KEY_VERSION_HEADER, - PoprfClient, - SequentialDelayDomain, - SequentialDelayStage, - TestUtils, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import { - initDatabase as initSignerDatabase, - startSigner, - SupportedDatabase, - SupportedKeystore, -} from '@celo/phone-number-privacy-signer' -import { - DefaultKeyName, - KeyProvider, -} from '@celo/phone-number-privacy-signer/dist/common/key-management/key-provider-base' -import { SignerConfig } from '@celo/phone-number-privacy-signer/dist/config' -import { defined, noBool, noNumber, noString } from '@celo/utils/lib/sign-typed-data-utils' -import { LocalWallet } from '@celo/wallet-local' -import BigNumber from 'bignumber.js' -import { Server as HttpsServer } from 'https' -import { Knex } from 'knex' -import { Server } from 'net' -import request from 'supertest' -import { MockKeyProvider } from '../../../signer/dist/common/key-management/mock-key-provider' -import config from '../../src/config' -import { startCombiner } from '../../src/server' - -const { - DOMAINS_THRESHOLD_DEV_PK_SHARE_1_V1, - DOMAINS_THRESHOLD_DEV_PK_SHARE_1_V2, - DOMAINS_THRESHOLD_DEV_PK_SHARE_1_V3, - DOMAINS_THRESHOLD_DEV_PK_SHARE_2_V1, - DOMAINS_THRESHOLD_DEV_PK_SHARE_2_V2, - DOMAINS_THRESHOLD_DEV_PK_SHARE_2_V3, - DOMAINS_THRESHOLD_DEV_PK_SHARE_3_V1, - DOMAINS_THRESHOLD_DEV_PK_SHARE_3_V2, - DOMAINS_THRESHOLD_DEV_PK_SHARE_3_V3, -} = TestUtils.Values - -// create deep copy of config -const combinerConfig: typeof config = JSON.parse(JSON.stringify(config)) -combinerConfig.domains.enabled = true - -const signerConfig: SignerConfig = { - serviceName: 'odis-signer', - server: { - port: undefined, - sslKeyPath: undefined, - sslCertPath: undefined, - }, - quota: { - unverifiedQueryMax: 10, - additionalVerifiedQueryMax: 30, - queryPerTransaction: 2, - // Min balance is .01 cUSD - minDollarBalance: new BigNumber(1e16), - // Min balance is .01 cEUR - minEuroBalance: new BigNumber(1e16), - // Min balance is .005 CELO - minCeloBalance: new BigNumber(5e15), - // Equivalent to 0.001 cUSD/query - queryPriceInCUSD: new BigNumber(0.001), - }, - api: { - domains: { - enabled: true, - }, - phoneNumberPrivacy: { - enabled: false, - shouldFailOpen: false, - }, - legacyPhoneNumberPrivacy: { - enabled: false, - shouldFailOpen: false, - }, - }, - attestations: { - numberAttestationsRequired: 3, - }, - blockchain: { - provider: 'https://alfajores-forno.celo-testnet.org', - apiKey: undefined, - }, - db: { - type: SupportedDatabase.Sqlite, - user: '', - password: '', - database: '', - host: 'http://localhost', - port: undefined, - ssl: true, - poolMaxSize: 50, - }, - keystore: { - type: SupportedKeystore.MOCK_SECRET_MANAGER, - keys: { - phoneNumberPrivacy: { - name: 'phoneNumberPrivacy', - latest: 2, - }, - domains: { - name: 'domains', - latest: 1, - }, - }, - azure: { - clientID: '', - clientSecret: '', - tenant: '', - vaultName: '', - }, - google: { - projectId: '', - }, - aws: { - region: '', - secretKey: '', - }, - }, - timeout: 5000, - test_quota_bypass_percentage: 0, - fullNodeTimeoutMs: FULL_NODE_TIMEOUT_IN_MS, -} - -describe('domainService', () => { - const wallet = new LocalWallet() - wallet.addAccount('0x00000000000000000000000000000000000000000000000000000000deadbeef') - const walletAddress = wallet.getAccounts()[0]! - - const domainStages = (): SequentialDelayStage[] => [ - { delay: 0, resetTimer: noBool, batchSize: defined(2), repetitions: defined(10) }, - ] - - const authenticatedDomain = (_stages?: SequentialDelayStage[]): SequentialDelayDomain => ({ - name: DomainIdentifiers.SequentialDelay, - version: '1', - stages: _stages ?? domainStages(), - address: defined(walletAddress), - salt: defined('himalayanPink'), - }) - - const DEFAULT_PUB_KEY = - TestUtils.Values.DOMAINS_THRESHOLD_DEV_PUBKEYS[config.domains.keys.currentVersion - 1] - const signatureRequest = async ( - _domain?: SequentialDelayDomain, - _nonce?: number, - _pubKey: string = DEFAULT_PUB_KEY - ): Promise<[DomainRestrictedSignatureRequest, PoprfClient]> => { - const domain = _domain ?? authenticatedDomain() - const poprfClient = new PoprfClient( - Buffer.from(_pubKey, 'base64'), - domainHash(domain), - Buffer.from('test message', 'utf8') - ) - - const req: DomainRestrictedSignatureRequest = { - type: DomainRequestTypeTag.SIGN, - domain: domain, - options: { - signature: noString, - nonce: defined(_nonce ?? 0), - }, - blindedMessage: poprfClient.blindedMessage.toString('base64'), - sessionID: defined(genSessionID()), - } - req.options.signature = defined( - await wallet.signTypedData(walletAddress, domainRestrictedSignatureRequestEIP712(req)) - ) - return [req, poprfClient] - } - - const quotaRequest = async (): Promise> => { - const req: DomainQuotaStatusRequest = { - type: DomainRequestTypeTag.QUOTA, - domain: authenticatedDomain(), - options: { - signature: noString, - nonce: noNumber, - }, - sessionID: defined(genSessionID()), - } - req.options.signature = defined( - await wallet.signTypedData(walletAddress, domainQuotaStatusRequestEIP712(req)) - ) - return req - } - - // Build and sign an example disable domain request. - const disableRequest = async ( - _domain?: SequentialDelayDomain - ): Promise> => { - const req: DisableDomainRequest = { - type: DomainRequestTypeTag.DISABLE, - domain: _domain ?? authenticatedDomain(), - options: { - signature: noString, - nonce: noNumber, - }, - sessionID: defined(genSessionID()), - } - req.options.signature = defined( - await wallet.signTypedData(walletAddress, disableDomainRequestEIP712(req)) - ) - return req - } - - let keyProvider1: KeyProvider - let keyProvider2: KeyProvider - let keyProvider3: KeyProvider - let signerDB1: Knex - let signerDB2: Knex - let signerDB3: Knex - let signer1: Server | HttpsServer - let signer2: Server | HttpsServer - let signer3: Server | HttpsServer - let app: any - - const signerMigrationsPath = '../signer/dist/common/database/migrations' - - describe('with n=3, t=2', () => { - const expectedEvals: string[] = [ - '3QLFPV6VvnhhnZ7mOu0xm7BUUJIUVY6vEHvZONOtZ/c=', - 'BBG0fAZJ6VNQwjge+3vOCF3uBo5KCs2+er/f/2QcV58=', - '1/otd1fW1nhUoU3ubjFDS8/RX0OClvHDsmGdnz6fZVE=', - ] - const expectedEval = expectedEvals[config.domains.keys.currentVersion - 1] - - beforeAll(async () => { - keyProvider1 = new MockKeyProvider( - new Map([ - [`${DefaultKeyName.DOMAINS}-1`, DOMAINS_THRESHOLD_DEV_PK_SHARE_1_V1], - [`${DefaultKeyName.DOMAINS}-2`, DOMAINS_THRESHOLD_DEV_PK_SHARE_1_V2], - [`${DefaultKeyName.DOMAINS}-3`, DOMAINS_THRESHOLD_DEV_PK_SHARE_1_V3], - ]) - ) - keyProvider2 = new MockKeyProvider( - new Map([ - [`${DefaultKeyName.DOMAINS}-1`, DOMAINS_THRESHOLD_DEV_PK_SHARE_2_V1], - [`${DefaultKeyName.DOMAINS}-2`, DOMAINS_THRESHOLD_DEV_PK_SHARE_2_V2], - [`${DefaultKeyName.DOMAINS}-3`, DOMAINS_THRESHOLD_DEV_PK_SHARE_2_V3], - ]) - ) - keyProvider3 = new MockKeyProvider( - new Map([ - [`${DefaultKeyName.DOMAINS}-1`, DOMAINS_THRESHOLD_DEV_PK_SHARE_3_V1], - [`${DefaultKeyName.DOMAINS}-2`, DOMAINS_THRESHOLD_DEV_PK_SHARE_3_V2], - [`${DefaultKeyName.DOMAINS}-3`, DOMAINS_THRESHOLD_DEV_PK_SHARE_3_V3], - ]) - ) - - app = startCombiner(combinerConfig, getContractKit(combinerConfig.blockchain)) - }) - - beforeEach(async () => { - signerDB1 = await initSignerDatabase(signerConfig, signerMigrationsPath) - signerDB2 = await initSignerDatabase(signerConfig, signerMigrationsPath) - signerDB3 = await initSignerDatabase(signerConfig, signerMigrationsPath) - }) - - afterEach(async () => { - await signerDB1?.destroy() - await signerDB2?.destroy() - await signerDB3?.destroy() - signer1?.close() - signer2?.close() - signer3?.close() - }) - - describe('when signers are operating correctly', () => { - beforeEach(async () => { - signer1 = startSigner(signerConfig, signerDB1, keyProvider1).listen(3001) - signer2 = startSigner(signerConfig, signerDB2, keyProvider2).listen(3002) - signer3 = startSigner(signerConfig, signerDB3, keyProvider3).listen(3003) - }) - - describe(`${CombinerEndpoint.DISABLE_DOMAIN}`, () => { - it('Should respond with 200 on valid request', async () => { - const res = await request(app) - .post(CombinerEndpoint.DISABLE_DOMAIN) - .send(await disableRequest()) - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: res.body.version, - status: { disabled: true, counter: 0, timer: 0, now: res.body.status.now }, - }) - }) - - it('Should respond with 200 on repeated valid requests', async () => { - const req = await disableRequest() - const res1 = await request(app).post(CombinerEndpoint.DISABLE_DOMAIN).send(req) - expect(res1.status).toBe(200) - const expectedResponse: DisableDomainResponse = { - success: true, - version: res1.body.version, - status: { disabled: true, counter: 0, timer: 0, now: res1.body.status.now }, - } - expect(res1.body).toStrictEqual(expectedResponse) - const res2 = await request(app).post(CombinerEndpoint.DISABLE_DOMAIN).send(req) - expect(res2.status).toBe(200) - expectedResponse.status.now = res2.body.status.now - expect(res2.body).toStrictEqual(expectedResponse) - }) - - it('Should respond with 200 on extra request fields', async () => { - const req = await disableRequest() - // @ts-ignore Intentionally adding an extra field to the request type - req.options.extraField = noString - - const res = await request(app).post(CombinerEndpoint.DISABLE_DOMAIN).send(req) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: res.body.version, - status: { disabled: true, counter: 0, timer: 0, now: res.body.status.now }, - }) - }) - - it('Should respond with 400 on missing request fields', async () => { - const badRequest = await disableRequest() - // @ts-ignore Intentionally deleting required field - delete badRequest.domain.version - - const res = await request(app).post(CombinerEndpoint.DISABLE_DOMAIN).send(badRequest) - - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 on unknown domain', async () => { - // Create a requests with an invalid domain identifier. - const unknownRequest = await disableRequest() - // @ts-ignore UnknownDomain is (intentionally) not a valid domain identifier. - unknownRequest.domain.name = 'UnknownDomain' - - const res = await request(app).post(CombinerEndpoint.DISABLE_DOMAIN).send(unknownRequest) - - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 on bad encoding', async () => { - const badRequest1 = await disableRequest() - // @ts-ignore Intentionally not JSON - badRequest1.domain = 'Freddy' - - const res1 = await request(app).post(CombinerEndpoint.DISABLE_DOMAIN).send(badRequest1) - - expect(res1.status).toBe(400) - expect(res1.body).toStrictEqual({ - success: false, - version: res1.body.version, - error: WarningMessage.INVALID_INPUT, - }) - - const badRequest2 = '' - - const res2 = await request(app).post(CombinerEndpoint.DISABLE_DOMAIN).send(badRequest2) - - expect(res2.status).toBe(400) - expect(res2.body).toStrictEqual({ - success: false, - version: res2.body.version, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 401 on failed auth', async () => { - // Create a manipulated request, which will have a bad signature. - const badRequest = await disableRequest() - badRequest.domain.salt = defined('badSalt') - - const res = await request(app).post(CombinerEndpoint.DISABLE_DOMAIN).send(badRequest) - - expect(res.status).toBe(401) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.UNAUTHENTICATED_USER, - }) - }) - - it('Should respond with 503 on disabled api', async () => { - const configWithApiDisabled: typeof combinerConfig = JSON.parse( - JSON.stringify(combinerConfig) - ) - configWithApiDisabled.domains.enabled = false - const appWithApiDisabled = startCombiner( - configWithApiDisabled, - getContractKit(configWithApiDisabled.blockchain) - ) - const req = await disableRequest() - - const res = await request(appWithApiDisabled) - .post(CombinerEndpoint.DISABLE_DOMAIN) - .send(req) - - expect(res.status).toBe(503) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.API_UNAVAILABLE, - }) - }) - }) - - describe(`${CombinerEndpoint.DOMAIN_QUOTA_STATUS}`, () => { - it('Should respond with 200 on valid request', async () => { - const res = await request(app) - .post(CombinerEndpoint.DOMAIN_QUOTA_STATUS) - .send(await quotaRequest()) - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: res.body.version, - status: { disabled: false, counter: 0, timer: 0, now: res.body.status.now }, - }) - }) - - it('Should respond with 200 on repeated valid requests', async () => { - const req = await quotaRequest() - const res1 = await request(app).post(CombinerEndpoint.DOMAIN_QUOTA_STATUS).send(req) - const expectedResponse: DomainQuotaStatusResponse = { - success: true, - version: res1.body.version, - status: { disabled: false, counter: 0, timer: 0, now: res1.body.status.now }, - } - - expect(res1.status).toBe(200) - expect(res1.body).toStrictEqual(expectedResponse) - - const res2 = await request(app).post(CombinerEndpoint.DOMAIN_QUOTA_STATUS).send(req) - expect(res2.status).toBe(200) - // Prevent flakiness due to slight timing inconsistencies - expectedResponse.status.now = res2.body.status.now - expect(res2.body).toStrictEqual(expectedResponse) - }) - - it('Should respond with 200 on extra request fields', async () => { - const req = await quotaRequest() - // @ts-ignore Intentionally adding an extra field to the request type - req.options.extraField = noString - - const res = await request(app).post(CombinerEndpoint.DOMAIN_QUOTA_STATUS).send(req) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: res.body.version, - status: { disabled: false, counter: 0, timer: 0, now: res.body.status.now }, - }) - }) - - it('Should respond with 400 on missing request fields', async () => { - const badRequest = await quotaRequest() - // @ts-ignore Intentionally deleting required field - delete badRequest.domain.version - - const res = await request(app).post(CombinerEndpoint.DOMAIN_QUOTA_STATUS).send(badRequest) - - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 on unknown domain', async () => { - // Create a requests with an invalid domain identifier. - const unknownRequest = await quotaRequest() - // @ts-ignore UnknownDomain is (intentionally) not a valid domain identifier. - unknownRequest.domain.name = 'UnknownDomain' - - const res = await request(app) - .post(CombinerEndpoint.DOMAIN_QUOTA_STATUS) - .send(unknownRequest) - - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 on bad encoding', async () => { - const badRequest1 = await quotaRequest() - // @ts-ignore Intentionally not JSON - badRequest1.domain = 'Freddy' - - const res1 = await request(app) - .post(CombinerEndpoint.DOMAIN_QUOTA_STATUS) - .send(badRequest1) - - expect(res1.status).toBe(400) - expect(res1.body).toStrictEqual({ - success: false, - version: res1.body.version, - error: WarningMessage.INVALID_INPUT, - }) - - const badRequest2 = '' - - const res2 = await request(app) - .post(CombinerEndpoint.DOMAIN_QUOTA_STATUS) - .send(badRequest2) - - expect(res2.status).toBe(400) - expect(res2.body).toStrictEqual({ - success: false, - version: res2.body.version, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 401 on failed auth', async () => { - // Create a manipulated request, which will have a bad signature. - const badRequest = await quotaRequest() - badRequest.domain.salt = defined('badSalt') - - const res = await request(app).post(CombinerEndpoint.DOMAIN_QUOTA_STATUS).send(badRequest) - - expect(res.status).toBe(401) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.UNAUTHENTICATED_USER, - }) - }) - - it('Should respond with 503 on disabled api', async () => { - const configWithApiDisabled: typeof combinerConfig = JSON.parse( - JSON.stringify(combinerConfig) - ) - configWithApiDisabled.domains.enabled = false - const appWithApiDisabled = startCombiner( - configWithApiDisabled, - getContractKit(configWithApiDisabled.blockchain) - ) - - const req = await quotaRequest() - - const res = await request(appWithApiDisabled) - .post(CombinerEndpoint.DOMAIN_QUOTA_STATUS) - .send(req) - - expect(res.status).toBe(503) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.API_UNAVAILABLE, - }) - }) - }) - - describe(`${CombinerEndpoint.DOMAIN_SIGN}`, () => { - it('Should respond with 200 on valid request', async () => { - const [req, poprfClient] = await signatureRequest() - const res = await request(app).post(CombinerEndpoint.DOMAIN_SIGN).send(req) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: res.body.version, - signature: res.body.signature, - status: { - disabled: false, - counter: 1, - timer: res.body.status.timer, - now: res.body.status.now, - }, - }) - const evaluation = poprfClient.unblindResponse(Buffer.from(res.body.signature, 'base64')) - expect(evaluation.toString('base64')).toEqual(expectedEval) - }) - - for (let i = 1; i <= 3; i++) { - it(`Should respond with 200 on valid request with key version header ${i}`, async () => { - const [req, poprfClient] = await signatureRequest( - undefined, - undefined, - TestUtils.Values.DOMAINS_THRESHOLD_DEV_PUBKEYS[i - 1] - ) - - const res = await request(app) - .post(CombinerEndpoint.DOMAIN_SIGN) - .set(KEY_VERSION_HEADER, i.toString()) - .send(req) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: res.body.version, - signature: res.body.signature, - status: { - disabled: false, - counter: 1, - timer: res.body.status.timer, - now: res.body.status.now, - }, - }) - const evaluation = poprfClient.unblindResponse( - Buffer.from(res.body.signature, 'base64') - ) - expect(evaluation.toString('base64')).toEqual(expectedEvals[i - 1]) - }) - } - - it('Should respond with 200 if nonce > domainState', async () => { - const [req, poprfClient] = await signatureRequest(undefined, 2) - - const res = await request(app).post(CombinerEndpoint.DOMAIN_SIGN).send(req) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: res.body.version, - signature: res.body.signature, - status: { - disabled: false, - counter: 1, // counter gets incremented, not set to nonce value - timer: res.body.status.timer, - now: res.body.status.now, - }, - }) - const evaluation = poprfClient.unblindResponse(Buffer.from(res.body.signature, 'base64')) - expect(evaluation.toString('base64')).toEqual(expectedEval) - }) - - it('Should respond with 200 on repeated valid requests', async () => { - const [req1, poprfClient] = await signatureRequest() - - const res1 = await request(app).post(CombinerEndpoint.DOMAIN_SIGN).send(req1) - expect(res1.status).toBe(200) - expect(res1.body).toStrictEqual({ - success: true, - version: res1.body.version, - signature: res1.body.signature, - status: { - disabled: false, - counter: 1, - timer: res1.body.status.timer, - now: res1.body.status.now, - }, - }) - const eval1 = poprfClient.unblindResponse(Buffer.from(res1.body.signature, 'base64')) - expect(eval1.toString('base64')).toEqual(expectedEval) - - // submit identical request with nonce set to 1 - req1.options.nonce = defined(1) - req1.options.signature = noString - req1.options.signature = defined( - await wallet.signTypedData(walletAddress, domainRestrictedSignatureRequestEIP712(req1)) - ) - const res2 = await request(app).post(CombinerEndpoint.DOMAIN_SIGN).send(req1) - - expect(res2.status).toBe(200) - expect(res2.body).toStrictEqual({ - success: true, - version: res2.body.version, - signature: res2.body.signature, - status: { - disabled: false, - counter: 2, - timer: res2.body.status.timer, - now: res2.body.status.now, - }, - }) - const eval2 = poprfClient.unblindResponse(Buffer.from(res1.body.signature, 'base64')) - expect(eval2).toEqual(eval1) - }) - - it('Should respond with 200 on extra request fields', async () => { - const [req, poprfClient] = await signatureRequest() - // @ts-ignore Intentionally adding an extra field to the request type - req.options.extraField = noString - - const res = await request(app).post(CombinerEndpoint.DOMAIN_SIGN).send(req) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: res.body.version, - signature: res.body.signature, - status: { - disabled: false, - counter: 1, - timer: res.body.status.timer, - now: res.body.status.now, - }, - }) - const evaluation = poprfClient.unblindResponse(Buffer.from(res.body.signature, 'base64')) - expect(evaluation.toString('base64')).toEqual(expectedEval) - }) - - it('Should respond with 400 on missing request fields', async () => { - const [badRequest, _] = await signatureRequest() - // @ts-ignore Intentionally deleting required field - delete badRequest.domain.version - - const res = await request(app).post(CombinerEndpoint.DOMAIN_SIGN).send(badRequest) - - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 on unknown domain', async () => { - // Create a requests with an invalid domain identifier. - const [unknownRequest, _] = await signatureRequest() - // @ts-ignore UnknownDomain is (intentionally) not a valid domain identifier. - unknownRequest.domain.name = 'UnknownDomain' - - const res = await request(app).post(CombinerEndpoint.DOMAIN_SIGN).send(unknownRequest) - - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 on bad encoding', async () => { - const [badRequest1, _] = await signatureRequest() - // @ts-ignore Intentionally not JSON - badRequest1.domain = 'Freddy' - - const res1 = await request(app).post(CombinerEndpoint.DOMAIN_SIGN).send(badRequest1) - - expect(res1.status).toBe(400) - expect(res1.body).toStrictEqual({ - success: false, - version: res1.body.version, - error: WarningMessage.INVALID_INPUT, - }) - - const badRequest2 = '' - - const res2 = await request(app).post(CombinerEndpoint.DOMAIN_SIGN).send(badRequest2) - - expect(res2.status).toBe(400) - expect(res2.body).toStrictEqual({ - success: false, - version: res2.body.version, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 on unsupported key version', async () => { - const [badRequest, _] = await signatureRequest() - - const res = await request(app) - .post(CombinerEndpoint.DOMAIN_SIGN) - .set(KEY_VERSION_HEADER, '4') - .send(badRequest) - - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.INVALID_KEY_VERSION_REQUEST, - }) - }) - - it('Should respond with 401 on failed auth', async () => { - // Create a manipulated request, which will have a bad signature. - const [badRequest, _] = await signatureRequest() - badRequest.domain.salt = defined('badSalt') - - const res = await request(app).post(CombinerEndpoint.DOMAIN_SIGN).send(badRequest) - - expect(res.status).toBe(401) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.UNAUTHENTICATED_USER, - }) - }) - - it('Should respond with 401 on invalid nonce', async () => { - // Request must be sent first since nonce check is >= 0 - const [req1, _] = await signatureRequest() - const res1 = await request(app).post(CombinerEndpoint.DOMAIN_SIGN).send(req1) - - expect(res1.status).toBe(200) - expect(res1.body).toStrictEqual({ - success: true, - version: res1.body.version, - signature: res1.body.signature, - status: { - disabled: false, - counter: 1, - timer: res1.body.status.timer, - now: res1.body.status.now, - }, - }) - const res2 = await request(app).post(CombinerEndpoint.DOMAIN_SIGN).send(req1) - expect(res2.status).toBe(401) - expect(res2.body).toStrictEqual({ - success: false, - version: res2.body.version, - error: WarningMessage.INVALID_NONCE, - }) - }) - - it('Should respond with 429 on out of quota', async () => { - const noQuotaDomain = authenticatedDomain([ - { delay: 0, resetTimer: noBool, batchSize: defined(0), repetitions: defined(0) }, - ]) - const [badRequest, _] = await signatureRequest(noQuotaDomain) - - const res = await request(app).post(CombinerEndpoint.DOMAIN_SIGN).send(badRequest) - - expect(res.status).toBe(429) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.EXCEEDED_QUOTA, - }) - }) - - it('Should respond with 429 on request too early', async () => { - // This domain won't accept requests until ~10 seconds after test execution - const noQuotaDomain = authenticatedDomain([ - { - delay: Math.floor(Date.now() / 1000) + 10, - resetTimer: noBool, - batchSize: defined(2), - repetitions: defined(1), - }, - ]) - const [badRequest, _] = await signatureRequest(noQuotaDomain) - - const res = await request(app).post(CombinerEndpoint.DOMAIN_SIGN).send(badRequest) - - expect(res.status).toBe(429) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.EXCEEDED_QUOTA, - }) - }) - - it('Should respond with 429 when requesting a signature from a disabled domain', async () => { - const testDomain = authenticatedDomain() - const resDisable = await request(app) - .post(CombinerEndpoint.DISABLE_DOMAIN) - .send(await disableRequest(testDomain)) - expect(resDisable.status).toBe(200) - expect(resDisable.body).toStrictEqual({ - success: true, - version: resDisable.body.version, - status: { disabled: true, counter: 0, timer: 0, now: resDisable.body.status.now }, - }) - }) - - it('Should respond with 503 on disabled api', async () => { - const configWithApiDisabled: typeof combinerConfig = JSON.parse( - JSON.stringify(combinerConfig) - ) - configWithApiDisabled.domains.enabled = false - const appWithApiDisabled = startCombiner( - configWithApiDisabled, - getContractKit(configWithApiDisabled.blockchain) - ) - - const [req, _] = await signatureRequest() - - const res = await request(appWithApiDisabled).post(CombinerEndpoint.DOMAIN_SIGN).send(req) - - expect(res.status).toBe(503) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.API_UNAVAILABLE, - }) - }) - }) - }) - - describe('when signers are not operating correctly', () => { - // In this case (1/3 signers are correct), response unblinding is guaranteed to fail - // Testing 2/3 signers is flaky since the combiner sometimes combines two - // correct signatures and returns, and sometimes combines one wrong/one correct - // since it cannot verify the sigs server-side. - describe('when 1/3 signers return correct signatures', () => { - beforeEach(async () => { - // Signer 1 & 2's v1 keys are misconfigured to point to the v3 share - const badKeyProvider1 = new MockKeyProvider( - new Map([[`${DefaultKeyName.DOMAINS}-1`, DOMAINS_THRESHOLD_DEV_PK_SHARE_1_V3]]) - ) - const badKeyProvider2 = new MockKeyProvider( - new Map([[`${DefaultKeyName.DOMAINS}-1`, DOMAINS_THRESHOLD_DEV_PK_SHARE_2_V3]]) - ) - signer1 = startSigner(signerConfig, signerDB1, badKeyProvider1).listen(3001) - signer2 = startSigner(signerConfig, signerDB2, badKeyProvider2).listen(3002) - signer3 = startSigner(signerConfig, signerDB3, keyProvider3).listen(3003) - }) - - describe(`${CombinerEndpoint.DOMAIN_SIGN}`, () => { - it('Should respond with 200 on valid request', async () => { - // Ensure requested keyVersion is one that signer1 does not have - const [req, poprfClient] = await signatureRequest( - undefined, - undefined, - TestUtils.Values.DOMAINS_THRESHOLD_DEV_PUBKEY_V1 - ) - const res = await request(app).post(CombinerEndpoint.DOMAIN_SIGN).send(req) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: res.body.version, - signature: res.body.signature, - status: { - disabled: false, - counter: 1, - timer: res.body.status.timer, - now: res.body.status.now, - }, - }) - expect(() => - poprfClient.unblindResponse(Buffer.from(res.body.signature, 'base64')) - ).toThrow(/verification failed/) - }) - }) - }) - - describe('when 2/3 of signers are disabled', () => { - beforeEach(async () => { - const configWithApiDisabled: SignerConfig = JSON.parse(JSON.stringify(signerConfig)) - configWithApiDisabled.api.domains.enabled = false - signer1 = startSigner(signerConfig, signerDB1, keyProvider1).listen(3001) - signer2 = startSigner(configWithApiDisabled, signerDB2, keyProvider2).listen(3002) - signer3 = startSigner(configWithApiDisabled, signerDB3, keyProvider3).listen(3003) - }) - - describe(`${CombinerEndpoint.DISABLE_DOMAIN}`, () => { - it('Should fail to reach threshold of signers on valid request', async () => { - const res = await request(app) - .post(CombinerEndpoint.DISABLE_DOMAIN) - .send(await disableRequest()) - expect(res.status).toBe(503) // majority error code in this case - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: ErrorMessage.THRESHOLD_DISABLE_DOMAIN_FAILURE, - }) - }) - }) - - describe(`${CombinerEndpoint.DOMAIN_QUOTA_STATUS}`, () => { - it('Should fail to reach threshold of signers on valid request', async () => { - const res = await request(app) - .post(CombinerEndpoint.DOMAIN_QUOTA_STATUS) - .send(await quotaRequest()) - expect(res.status).toBe(503) // majority error code in this case - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: ErrorMessage.THRESHOLD_DOMAIN_QUOTA_STATUS_FAILURE, - }) - }) - }) - - describe(`${CombinerEndpoint.DOMAIN_SIGN}`, () => { - it('Should fail to reach threshold of signers on valid request', async () => { - const [req, _] = await signatureRequest() - const res = await request(app).post(CombinerEndpoint.DOMAIN_SIGN).send(req) - expect(res.status).toBe(503) // majority error code in this case - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: ErrorMessage.NOT_ENOUGH_PARTIAL_SIGNATURES, - }) - }) - }) - }) - - describe('when 1/3 of signers are disabled', () => { - beforeEach(async () => { - const configWithApiDisabled: SignerConfig = JSON.parse(JSON.stringify(signerConfig)) - configWithApiDisabled.api.domains.enabled = false - signer1 = startSigner(signerConfig, signerDB1, keyProvider1).listen(3001) - signer2 = startSigner(signerConfig, signerDB2, keyProvider2).listen(3002) - signer3 = startSigner(configWithApiDisabled, signerDB3, keyProvider3).listen(3003) - }) - - describe(`${CombinerEndpoint.DISABLE_DOMAIN}`, () => { - it('Should respond with 200 on valid request', async () => { - const res = await request(app) - .post(CombinerEndpoint.DISABLE_DOMAIN) - .send(await disableRequest()) - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: res.body.version, - status: { disabled: true, counter: 0, timer: 0, now: res.body.status.now }, - }) - }) - }) - - describe(`${CombinerEndpoint.DOMAIN_QUOTA_STATUS}`, () => { - it('Should respond with 200 on valid request', async () => { - const res = await request(app) - .post(CombinerEndpoint.DOMAIN_QUOTA_STATUS) - .send(await quotaRequest()) - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: res.body.version, - status: { disabled: false, counter: 0, timer: 0, now: res.body.status.now }, - }) - }) - }) - - describe(`${CombinerEndpoint.DOMAIN_SIGN}`, () => { - it('Should respond with 200 on valid request', async () => { - const [req, poprfClient] = await signatureRequest() - const res = await request(app).post(CombinerEndpoint.DOMAIN_SIGN).send(req) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: res.body.version, - signature: res.body.signature, - status: { - disabled: false, - counter: 1, - timer: res.body.status.timer, - now: res.body.status.now, - }, - }) - const evaluation = poprfClient.unblindResponse( - Buffer.from(res.body.signature, 'base64') - ) - expect(evaluation.toString('base64')).toEqual(expectedEval) - }) - }) - }) - - describe('when signers timeout', () => { - beforeEach(async () => { - const testTimeoutMS = 0 - - const configWithShortTimeout: SignerConfig = JSON.parse(JSON.stringify(signerConfig)) - configWithShortTimeout.timeout = testTimeoutMS - // Test this with all signers timing out to decrease possibility of race conditions - signer1 = startSigner(configWithShortTimeout, signerDB1, keyProvider1).listen(3001) - signer2 = startSigner(configWithShortTimeout, signerDB2, keyProvider2).listen(3002) - signer3 = startSigner(configWithShortTimeout, signerDB3, keyProvider3).listen(3003) - }) - - describe(`${CombinerEndpoint.DISABLE_DOMAIN}`, () => { - it('Should fail to reach threshold of signers on valid request', async () => { - const res = await request(app) - .post(CombinerEndpoint.DISABLE_DOMAIN) - .send(await disableRequest()) - expect(res.status).toBe(500) // majority error code in this case - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: ErrorMessage.THRESHOLD_DISABLE_DOMAIN_FAILURE, - }) - }) - }) - - describe(`${CombinerEndpoint.DOMAIN_QUOTA_STATUS}`, () => { - it('Should fail to reach threshold of signers on valid request', async () => { - const res = await request(app) - .post(CombinerEndpoint.DOMAIN_QUOTA_STATUS) - .send(await quotaRequest()) - expect(res.status).toBe(500) // majority error code in this case - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: ErrorMessage.THRESHOLD_DOMAIN_QUOTA_STATUS_FAILURE, - }) - }) - }) - - describe(`${CombinerEndpoint.DOMAIN_SIGN}`, () => { - it('Should fail to reach threshold of signers on valid request', async () => { - const [req, _] = await signatureRequest() - const res = await request(app).post(CombinerEndpoint.DOMAIN_SIGN).send(req) - expect(res.status).toBe(500) // majority error code in this case - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: ErrorMessage.NOT_ENOUGH_PARTIAL_SIGNATURES, - }) - }) - }) - }) - }) - }) - - // Ensure the same behavior when a minority of signers can block the threshold. - // On failure, the majority error code should not reflect the abort. - describe('with n=5, t=4', () => { - let keyProvider4: KeyProvider - let keyProvider5: KeyProvider - let signerDB4: Knex - let signerDB5: Knex - let signer4: Server | HttpsServer - let signer5: Server | HttpsServer - - const combinerConfigLargerN: typeof config = JSON.parse(JSON.stringify(combinerConfig)) - combinerConfigLargerN.domains.odisServices.signers = JSON.stringify([ - { - url: 'http://localhost:3001', - fallbackUrl: 'http://localhost:3001/fallback', - }, - { - url: 'http://localhost:3002', - fallbackUrl: 'http://localhost:3002/fallback', - }, - { - url: 'http://localhost:3003', - fallbackUrl: 'http://localhost:3003/fallback', - }, - { - url: 'http://localhost:3004', - fallbackUrl: 'http://localhost:3004/fallback', - }, - { - url: 'http://localhost:3005', - fallbackUrl: 'http://localhost:3005/fallback', - }, - ]) - const DOMAINS_PUBKEY_N5_T4 = - 'gEAedm5Gq+6s/r4ohLrduNmb7IznkYxHQ46+IW0iwEjcjWCi3lkJuItDVa3EXaoBKF4yFAa7wtuX7I8hB3m730XdEpd/77C2GOVGtwDshtCgajSzx7+0zvrnat5QmTkB' - - combinerConfigLargerN.domains.keys.versions = JSON.stringify([ - { - keyVersion: 1, - threshold: 4, - polynomial: - '040000000000000080401e766e46abeeacfebe2884baddb8d99bec8ce7918c47438ebe216d22c048dc8d60a2de5909b88b4355adc45daa01285e321406bbc2db97ec8f210779bbdf45dd12977fefb0b618e546b700ec86d0a06a34b3c7bfb4cefae76ade50993901bbf28cb0b2245e5de0926ce13338440e3b5a378dfe10ba41f61d145d9d5df29ce32abba7562804919101e4803bb47301da265654875d06a0c355b93918ff58efe29c6225d42c2b60c4efbdf7984e3b1ed4e997d53e719aa93fdc10171202460055955ad375e460a181f701a22f543365622c4a5f6ad16fce62e24584b77c23db48312840d0301197c3529cda8b712e01897a2cefab5437f658c59a2a3d880315c3268de1128b333a51d36b2999bf587d25ec82f3695db4c75f9825baf88a460002b11295e74511608041574063faa86f27251a2766861ada6a89bf45454aa4b933992f80622a810b8aead298964f37004ad57215433da765e1d3aae5d9b57ad2d9afcdf77f227e48040ac5701abce7995f94ac0c4c70996333396620e6cf8e00', - pubKey: DOMAINS_PUBKEY_N5_T4, - }, - ]) - - beforeAll(async () => { - keyProvider1 = new MockKeyProvider( - new Map([ - [ - `${DefaultKeyName.DOMAINS}-1`, - '01000000fa9f3c7a0ed050b3b4ab9df241e3e3e2069e36c96369b2bf378d7edd66e37a0e', - ], - ]) - ) - keyProvider2 = new MockKeyProvider( - new Map([ - [ - `${DefaultKeyName.DOMAINS}-1`, - '02000000b03e8d5203edb8b27a9c56185df0ee94e8be45f0c91e116beac68c851cc4ba10', - ], - ]) - ) - keyProvider3 = new MockKeyProvider( - new Map([ - [ - `${DefaultKeyName.DOMAINS}-1`, - '03000000bad95bc039a5418bd57e7f5bad4bf6c19ff85e523462925e226e6ac0a6770005', - ], - ]) - ) - keyProvider4 = new MockKeyProvider( - new Map([ - [ - `${DefaultKeyName.DOMAINS}-1`, - '040000002ffdcc94fd5322e4f3e0d7ba00b591c38f77e584b61b59dd6e62cc6cfed5fd06', - ], - ]) - ) - keyProvider5 = new MockKeyProvider( - new Map([ - [ - `${DefaultKeyName.DOMAINS}-1`, - '05000000243505a19a546f5002511f9527fe03401908cd6427991f69b2380e355fec0d0d', - ], - ]) - ) - app = startCombiner(combinerConfigLargerN, getContractKit(combinerConfigLargerN.blockchain)) - }) - - beforeEach(async () => { - signerDB1 = await initSignerDatabase(signerConfig, signerMigrationsPath) - signerDB2 = await initSignerDatabase(signerConfig, signerMigrationsPath) - signerDB3 = await initSignerDatabase(signerConfig, signerMigrationsPath) - signerDB4 = await initSignerDatabase(signerConfig, signerMigrationsPath) - signerDB5 = await initSignerDatabase(signerConfig, signerMigrationsPath) - - signer1 = startSigner(signerConfig, signerDB1, keyProvider1).listen(3001) - signer2 = startSigner(signerConfig, signerDB2, keyProvider2).listen(3002) - signer3 = startSigner(signerConfig, signerDB3, keyProvider3).listen(3003) - signer4 = startSigner(signerConfig, signerDB4, keyProvider4).listen(3004) - signer5 = startSigner(signerConfig, signerDB5, keyProvider5).listen(3005) - }) - - afterEach(async () => { - await signerDB1?.destroy() - await signerDB2?.destroy() - await signerDB3?.destroy() - await signerDB4?.destroy() - await signerDB5?.destroy() - signer1?.close() - signer2?.close() - signer3?.close() - signer4?.close() - signer5?.close() - }) - - it('Should respond with 200 on valid request', async () => { - const [req, poprfClient] = await signatureRequest(undefined, undefined, DOMAINS_PUBKEY_N5_T4) - const res = await request(app).post(CombinerEndpoint.DOMAIN_SIGN).send(req) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: res.body.version, - signature: res.body.signature, - status: { - disabled: false, - counter: 1, - timer: res.body.status.timer, - now: res.body.status.now, - }, - }) - poprfClient.unblindResponse(Buffer.from(res.body.signature, 'base64')) - }) - - // This previously incorrectly returned 502 instead of 429 - it('Should respond with 429 on out of quota', async () => { - const noQuotaDomain = authenticatedDomain([ - { delay: 0, resetTimer: noBool, batchSize: defined(0), repetitions: defined(0) }, - ]) - const [badRequest, _] = await signatureRequest(noQuotaDomain, undefined, DOMAINS_PUBKEY_N5_T4) - - const res = await request(app).post(CombinerEndpoint.DOMAIN_SIGN).send(badRequest) - - expect(res.status).toBe(429) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.EXCEEDED_QUOTA, - }) - }) - }) -}) diff --git a/packages/phone-number-privacy/combiner/test/integration/legacypnp.test.ts b/packages/phone-number-privacy/combiner/test/integration/legacypnp.test.ts deleted file mode 100644 index 1506015c289..00000000000 --- a/packages/phone-number-privacy/combiner/test/integration/legacypnp.test.ts +++ /dev/null @@ -1,793 +0,0 @@ -import { newKit } from '@celo/contractkit' -import { - AuthenticationMethod, - CombinerEndpoint, - ErrorMessage, - FULL_NODE_TIMEOUT_IN_MS, - genSessionID, - KEY_VERSION_HEADER, - LegacySignMessageRequest, - SignMessageResponseFailure, - SignMessageResponseSuccess, - TestUtils, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import { AttestationsStatus } from '@celo/phone-number-privacy-common/lib/test/utils' -import { IDENTIFIER } from '@celo/phone-number-privacy-common/lib/test/values' -import { - initDatabase as initSignerDatabase, - startSigner, - SupportedDatabase, - SupportedKeystore, -} from '@celo/phone-number-privacy-signer' -import { - DefaultKeyName, - KeyProvider, -} from '@celo/phone-number-privacy-signer/dist/common/key-management/key-provider-base' -import { MockKeyProvider } from '@celo/phone-number-privacy-signer/dist/common/key-management/mock-key-provider' -import { SignerConfig } from '@celo/phone-number-privacy-signer/dist/config' -import BigNumber from 'bignumber.js' -import threshold_bls from 'blind-threshold-bls' -import { Server as HttpsServer } from 'https' -import { Knex } from 'knex' -import { Server } from 'net' -import request from 'supertest' -import config, { getCombinerVersion } from '../../src/config' -import { startCombiner } from '../../src/server' - -const { - ContractRetrieval, - createMockContractKit, - createMockAccounts, - createMockToken, - createMockWeb3, - getPnpRequestAuthorization, - createMockAttestation, -} = TestUtils.Utils -const { - PRIVATE_KEY1, - ACCOUNT_ADDRESS1, - mockAccount, - DEK_PRIVATE_KEY, - DEK_PUBLIC_KEY, - PNP_THRESHOLD_DEV_PK_SHARE_1_V1, - PNP_THRESHOLD_DEV_PK_SHARE_1_V2, - PNP_THRESHOLD_DEV_PK_SHARE_1_V3, - PNP_THRESHOLD_DEV_PK_SHARE_2_V1, - PNP_THRESHOLD_DEV_PK_SHARE_2_V2, - PNP_THRESHOLD_DEV_PK_SHARE_2_V3, - PNP_THRESHOLD_DEV_PK_SHARE_3_V1, - PNP_THRESHOLD_DEV_PK_SHARE_3_V2, - PNP_THRESHOLD_DEV_PK_SHARE_3_V3, -} = TestUtils.Values - -// create deep copy -const combinerConfig: typeof config = JSON.parse(JSON.stringify(config)) -combinerConfig.phoneNumberPrivacy.enabled = true - -const signerConfig: SignerConfig = { - serviceName: 'odis-signer', - server: { - port: undefined, - sslKeyPath: undefined, - sslCertPath: undefined, - }, - quota: { - unverifiedQueryMax: 10, - additionalVerifiedQueryMax: 30, - queryPerTransaction: 2, - // Min balance is .01 cUSD - minDollarBalance: new BigNumber(1e16), - // Min balance is .01 cEUR - minEuroBalance: new BigNumber(1e16), - // Min balance is .005 CELO - minCeloBalance: new BigNumber(5e15), - // Equivalent to 0.001 cUSD/query - queryPriceInCUSD: new BigNumber(0.001), - }, - api: { - domains: { - enabled: false, - }, - phoneNumberPrivacy: { - enabled: false, - shouldFailOpen: true, - }, - legacyPhoneNumberPrivacy: { - enabled: true, - shouldFailOpen: true, - }, - }, - attestations: { - numberAttestationsRequired: 3, - }, - blockchain: { - provider: 'https://alfajores-forno.celo-testnet.org', - apiKey: undefined, - }, - db: { - type: SupportedDatabase.Sqlite, - user: '', - password: '', - database: '', - host: 'http://localhost', - port: undefined, - ssl: true, - poolMaxSize: 50, - }, - keystore: { - type: SupportedKeystore.MOCK_SECRET_MANAGER, - keys: { - phoneNumberPrivacy: { - name: 'phoneNumberPrivacy', - latest: 2, - }, - domains: { - name: 'domains', - latest: 1, - }, - }, - azure: { - clientID: '', - clientSecret: '', - tenant: '', - vaultName: '', - }, - google: { - projectId: '', - }, - aws: { - region: '', - secretKey: '', - }, - }, - timeout: 5000, - test_quota_bypass_percentage: 0, - fullNodeTimeoutMs: FULL_NODE_TIMEOUT_IN_MS, -} - -const testBlockNumber = 1000000 - -const mockTokenBalance = jest.fn() -const mockGetVerifiedStatus = jest.fn() -const mockGetWalletAddress = jest.fn() -const mockGetDataEncryptionKey = jest.fn() - -const mockContractKit = createMockContractKit( - { - // getWalletAddress stays constant across all old query-quota.test.ts unit tests - [ContractRetrieval.getAccounts]: createMockAccounts( - mockGetWalletAddress, - mockGetDataEncryptionKey - ), - [ContractRetrieval.getStableToken]: createMockToken(mockTokenBalance), - [ContractRetrieval.getGoldToken]: createMockToken(mockTokenBalance), - [ContractRetrieval.getAttestations]: createMockAttestation(mockGetVerifiedStatus), - }, - createMockWeb3(5, testBlockNumber) -) - -// Mock newKit as opposed to the CK constructor -// Returns an object of type ContractKit that can be passed into the signers + combiner -jest.mock('@celo/contractkit', () => ({ - ...jest.requireActual('@celo/contractkit'), - newKit: jest.fn().mockImplementation(() => mockContractKit), -})) - -describe(`legacyPnpService: ${CombinerEndpoint.LEGACY_PNP_SIGN}`, () => { - let keyProvider1: KeyProvider - let keyProvider2: KeyProvider - let keyProvider3: KeyProvider - let signerDB1: Knex - let signerDB2: Knex - let signerDB3: Knex - let signer1: Server | HttpsServer - let signer2: Server | HttpsServer - let signer3: Server | HttpsServer - let app: any - - // Used by PNP_SIGN tests for various configurations of signers - let userSeed: Uint8Array - let blindedMsgResult: threshold_bls.BlindedMessage - - const signerMigrationsPath = '../signer/dist/common/database/migrations' - const expectedVersion = getCombinerVersion() - - const message = Buffer.from('test message', 'utf8') - const expectedQuota = 410 - const expectedSignatures: string[] = [ - 'xgFMQtcgAMHJAEX/m9B4VFopYtxqPFSw0024sWzRYvQDvnmFqhXOPdnRDfa8WCEA', - 'wUuFV8yFBXGyEzKbyWjBChG6dER264nwjOsqErd/UZieVKE0oDMZcMDG+qObu4QB', - 'PJHqBGavcQG3NGFl3hiR8GymeDNumxbl1DnCJzWz+Ik5yCN2ZpAITBe24RTX0iMA', - ] - const expectedSignature = expectedSignatures[config.phoneNumberPrivacy.keys.currentVersion - 1] - - const expectedUnblindedSigs: string[] = [ - 'lOASnDJNbJBTMYfkbU4fMiK7FcNwSyqZo8iQSM95X8YK+/158be4S1A+jcQsCUYA', - 'QIT7HtHTe/d0Tq40Mf3rpHCT8qY20+8q7ZW9PXHFMWGvwSGhk7l3Pfwnx8YdXomB', - 'XW//DolLzaXYS/gk9WBHfeKy5HKrGjuF/OpCok/i6fprE4AGFH2PjE7zeKTfOQ+A', - ] - const expectedUnblindedSig = - expectedUnblindedSigs[config.phoneNumberPrivacy.keys.currentVersion - 1] - - // In current setup, the same mocked kit is used for the combiner and signers - const mockKit = newKit('dummyKit') - - const sendLegacyPnpSignRequest = async ( - req: LegacySignMessageRequest, - authorization: string, - app: any, - keyVersionHeader?: string - ) => { - let reqWithHeaders = request(app) - .post(CombinerEndpoint.LEGACY_PNP_SIGN) - .set('Authorization', authorization) - - if (keyVersionHeader) { - reqWithHeaders = reqWithHeaders.set(KEY_VERSION_HEADER, keyVersionHeader) - } - return reqWithHeaders.send(req) - } - - const getLegacySignRequest = ( - _blindedMsgResult: threshold_bls.BlindedMessage - ): LegacySignMessageRequest => { - return { - account: ACCOUNT_ADDRESS1, - blindedQueryPhoneNumber: Buffer.from(_blindedMsgResult.message).toString('base64'), - sessionID: genSessionID(), - } - } - - const prepMocks = (hasQuota: boolean) => { - const [transactionCount, isVerified, balanceToken] = hasQuota - ? [100, true, new BigNumber(200000000000000000)] - : [0, false, new BigNumber(0)] - ;[ - mockContractKit.connection.getTransactionCount, - mockGetVerifiedStatus, - mockGetVerifiedStatus, - mockTokenBalance, - mockGetDataEncryptionKey, - mockGetWalletAddress, - ].forEach((mockFn) => mockFn.mockReset()) - - mockContractKit.connection.getTransactionCount.mockReturnValue(transactionCount) - mockGetVerifiedStatus.mockReturnValue( - // only the isVerified value below matters - { isVerified, completed: 1, total: 1, numAttestationsRemaining: 1 } - ) - mockTokenBalance.mockReturnValue(balanceToken) - mockGetDataEncryptionKey.mockReturnValue(DEK_PUBLIC_KEY) - mockGetWalletAddress.mockReturnValue(mockAccount) - } - - beforeAll(async () => { - keyProvider1 = new MockKeyProvider( - new Map([ - [`${DefaultKeyName.PHONE_NUMBER_PRIVACY}-1`, PNP_THRESHOLD_DEV_PK_SHARE_1_V1], - [`${DefaultKeyName.PHONE_NUMBER_PRIVACY}-2`, PNP_THRESHOLD_DEV_PK_SHARE_1_V2], - [`${DefaultKeyName.PHONE_NUMBER_PRIVACY}-3`, PNP_THRESHOLD_DEV_PK_SHARE_1_V3], - ]) - ) - keyProvider2 = new MockKeyProvider( - new Map([ - [`${DefaultKeyName.PHONE_NUMBER_PRIVACY}-1`, PNP_THRESHOLD_DEV_PK_SHARE_2_V1], - [`${DefaultKeyName.PHONE_NUMBER_PRIVACY}-2`, PNP_THRESHOLD_DEV_PK_SHARE_2_V2], - [`${DefaultKeyName.PHONE_NUMBER_PRIVACY}-3`, PNP_THRESHOLD_DEV_PK_SHARE_2_V3], - ]) - ) - keyProvider3 = new MockKeyProvider( - new Map([ - [`${DefaultKeyName.PHONE_NUMBER_PRIVACY}-1`, PNP_THRESHOLD_DEV_PK_SHARE_3_V1], - [`${DefaultKeyName.PHONE_NUMBER_PRIVACY}-2`, PNP_THRESHOLD_DEV_PK_SHARE_3_V2], - [`${DefaultKeyName.PHONE_NUMBER_PRIVACY}-3`, PNP_THRESHOLD_DEV_PK_SHARE_3_V3], - ]) - ) - - app = startCombiner(combinerConfig, mockKit) - }) - - let req: LegacySignMessageRequest - beforeEach(async () => { - signerDB1 = await initSignerDatabase(signerConfig, signerMigrationsPath) - signerDB2 = await initSignerDatabase(signerConfig, signerMigrationsPath) - signerDB3 = await initSignerDatabase(signerConfig, signerMigrationsPath) - - // this needs to be defined here to avoid errors - userSeed = new Uint8Array(32) - for (let i = 0; i < userSeed.length - 1; i++) { - userSeed[i] = i - } - - blindedMsgResult = threshold_bls.blind(message, userSeed) - - req = getLegacySignRequest(blindedMsgResult) - prepMocks(true) - }) - - afterEach(async () => { - await signerDB1?.destroy() - await signerDB2?.destroy() - await signerDB3?.destroy() - signer1?.close() - signer2?.close() - signer3?.close() - }) - - describe('when signers are operating correctly', () => { - beforeEach(async () => { - signer1 = startSigner(signerConfig, signerDB1, keyProvider1, mockKit).listen(3001) - signer2 = startSigner(signerConfig, signerDB2, keyProvider2, mockKit).listen(3002) - signer3 = startSigner(signerConfig, signerDB3, keyProvider3, mockKit).listen(3003) - }) - - it('Should respond with 200 on valid request', async () => { - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendLegacyPnpSignRequest(req, authorization, app) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: 1, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - const unblindedSig = threshold_bls.unblind( - Buffer.from(res.body.signature, 'base64'), - blindedMsgResult.blindingFactor - ) - - expect(Buffer.from(unblindedSig).toString('base64')).toEqual(expectedUnblindedSig) - }) - - for (let i = 1; i <= 3; i++) { - it(`Should respond with 200 on valid request with key version header ${i}`, async () => { - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendLegacyPnpSignRequest(req, authorization, app, i.toString()) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignatures[i - 1], - performedQueryCount: 1, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - const unblindedSig = threshold_bls.unblind( - Buffer.from(res.body.signature, 'base64'), - blindedMsgResult.blindingFactor - ) - - expect(Buffer.from(unblindedSig).toString('base64')).toEqual(expectedUnblindedSigs[i - 1]) - }) - } - - it('Should respond with 200 on valid request with identifier', async () => { - // Ensure that this gets passed through the combiner to the signer - req.hashedPhoneNumber = IDENTIFIER - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendLegacyPnpSignRequest(req, authorization, app) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: 1, - totalQuota: 440, // Additional quota gets unlocked with an identifier - blockNumber: testBlockNumber, - warnings: [], - }) - }) - - it('Should respond with 200 on repeated valid requests', async () => { - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res1 = await sendLegacyPnpSignRequest(req, authorization, app) - - expect(res1.status).toBe(200) - expect(res1.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: 1, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - - // performedQueryCount should remain the same; same request should not - // consume any quota - const res2 = await sendLegacyPnpSignRequest(req, authorization, app) - expect(res2.status).toBe(200) - expect(res2.body).toStrictEqual(res1.body) - }) - - it('Should increment performedQueryCount on request from the same account with a new message', async () => { - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res1 = await sendLegacyPnpSignRequest(req, authorization, app) - - const expectedResponse: SignMessageResponseSuccess = { - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: 1, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [], - } - - expect(res1.status).toBe(200) - expect(res1.body).toStrictEqual(expectedResponse) - - // Second request for the same account but with new message - const message2 = Buffer.from('second test message', 'utf8') - const blindedMsg2 = threshold_bls.blind(message2, userSeed) - const req2 = getLegacySignRequest(blindedMsg2) - const authorization2 = getPnpRequestAuthorization(req2, PRIVATE_KEY1) - - // Expect performedQueryCount to increase - expectedResponse.performedQueryCount++ - expectedResponse.signature = - 'PWvuSYIA249x1dx+qzgl6PKSkoulXXE/P4WHJvGmtw77pCRilEWTn3xSp+6JS9+A' - const res2 = await sendLegacyPnpSignRequest(req2, authorization2, app) - expect(res2.status).toBe(200) - expect(res2.body).toStrictEqual(expectedResponse) - }) - - it('Should respond with 200 on extra request fields', async () => { - // @ts-ignore Intentionally adding an extra field to the request type - req.extraField = 'dummyString' - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendLegacyPnpSignRequest(req, authorization, app) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: 1, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - }) - - it('Should respond with 200 when authenticated with DEK', async () => { - req.authenticationMethod = AuthenticationMethod.ENCRYPTION_KEY - const authorization = getPnpRequestAuthorization(req, DEK_PRIVATE_KEY) - const res = await sendLegacyPnpSignRequest(req, authorization, app) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: 1, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - }) - - it('Should get the same unblinded signatures from the same message (different seed)', async () => { - const authorization1 = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res1 = await sendLegacyPnpSignRequest(req, authorization1, app) - - expect(res1.status).toBe(200) - expect(res1.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: 1, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - - const secondUserSeed = new Uint8Array(userSeed) - secondUserSeed[0]++ - // Ensure message is identical except for message - const req2 = { ...req } - const blindedMsgResult2 = threshold_bls.blind(message, secondUserSeed) - req2.blindedQueryPhoneNumber = Buffer.from(blindedMsgResult2.message).toString('base64') - - // Sanity check - expect(req2.blindedQueryPhoneNumber).not.toEqual(req.blindedQueryPhoneNumber) - - const authorization2 = getPnpRequestAuthorization(req2, PRIVATE_KEY1) - const res2 = await sendLegacyPnpSignRequest(req2, authorization2, app) - expect(res2.status).toBe(200) - const unblindedSig1 = threshold_bls.unblind( - Buffer.from(res1.body.signature, 'base64'), - blindedMsgResult.blindingFactor - ) - const unblindedSig2 = threshold_bls.unblind( - Buffer.from(res2.body.signature, 'base64'), - blindedMsgResult2.blindingFactor - ) - expect(Buffer.from(unblindedSig1).toString('base64')).toEqual(expectedUnblindedSig) - expect(unblindedSig1).toEqual(unblindedSig2) - }) - - it('Should respond with 400 on missing request fields', async () => { - // @ts-ignore Intentionally deleting required field - delete req.account - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendLegacyPnpSignRequest(req, authorization, app) - - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 on unsupported key version', async () => { - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendLegacyPnpSignRequest(req, authorization, app, '4') - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_KEY_VERSION_REQUEST, - }) - }) - - it('Should respond with 400 on request with invalid identifier', async () => { - // Ensure that this gets passed through the combiner to the signer - req.hashedPhoneNumber = '+1234567890' - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendLegacyPnpSignRequest(req, authorization, app) - - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 401 on failed WALLET_KEY auth', async () => { - req.account = mockAccount - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendLegacyPnpSignRequest(req, authorization, app) - - expect(res.status).toBe(401) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.UNAUTHENTICATED_USER, - }) - }) - - it('Should respond with 401 on failed DEK auth', async () => { - req.account = mockAccount - req.authenticationMethod = AuthenticationMethod.ENCRYPTION_KEY - const differentPk = '0x00000000000000000000000000000000000000000000000000000000ddddbbbb' - const authorization = getPnpRequestAuthorization(req, differentPk) - const res = await sendLegacyPnpSignRequest(req, authorization, app) - - expect(res.status).toBe(401) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.UNAUTHENTICATED_USER, - }) - }) - - it('Should respond with 403 on out of quota', async () => { - prepMocks(false) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendLegacyPnpSignRequest(req, authorization, app) - - expect(res.status).toBe(403) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.EXCEEDED_QUOTA, - }) - }) - - it('Should respond with 503 on disabled api', async () => { - const configWithApiDisabled: typeof combinerConfig = JSON.parse( - JSON.stringify(combinerConfig) - ) - configWithApiDisabled.phoneNumberPrivacy.enabled = false - const appWithApiDisabled = startCombiner(configWithApiDisabled, mockKit) - - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendLegacyPnpSignRequest(req, authorization, appWithApiDisabled) - - expect(res.status).toBe(503) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.API_UNAVAILABLE, - }) - }) - - describe('functionality in case of errors', () => { - it('Should respond with 200 on failure to fetch DEK when shouldFailOpen is true', async () => { - mockGetDataEncryptionKey.mockImplementation(() => { - throw new Error() - }) - - // Would fail authentication if getDataEncryptionKey succeeded - const differentPk = '0x00000000000000000000000000000000000000000000000000000000ddddbbbb' - req.authenticationMethod = AuthenticationMethod.ENCRYPTION_KEY - const authorization = getPnpRequestAuthorization(req, differentPk) - - const combinerConfigWithFailOpenEnabled: typeof combinerConfig = JSON.parse( - JSON.stringify(combinerConfig) - ) - combinerConfigWithFailOpenEnabled.phoneNumberPrivacy.shouldFailOpen = true - const appWithFailOpenEnabled = startCombiner(combinerConfigWithFailOpenEnabled, mockKit) - const res = await sendLegacyPnpSignRequest(req, authorization, appWithFailOpenEnabled) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: 1, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - const unblindedSig = threshold_bls.unblind( - Buffer.from(res.body.signature, 'base64'), - blindedMsgResult.blindingFactor - ) - expect(Buffer.from(unblindedSig).toString('base64')).toEqual(expectedUnblindedSig) - }) - }) - }) - - // For testing combiner code paths when signers do not behave as expected - describe('when signers are not operating correctly', () => { - let authorization: string - - beforeEach(() => { - authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - }) - - describe('when 2/3 signers return correct signatures', () => { - beforeEach(async () => { - const badBlsShare1 = - '000000002e50aa714ef6b865b5de89c56969ef9f8f27b6b0a6d157c9cc01c574ac9df604' - const badKeyProvider1 = new MockKeyProvider( - new Map([[`${DefaultKeyName.PHONE_NUMBER_PRIVACY}-1`, badBlsShare1]]) - ) - - signer1 = startSigner(signerConfig, signerDB1, badKeyProvider1, mockKit).listen(3001) - signer2 = startSigner(signerConfig, signerDB2, keyProvider2, mockKit).listen(3002) - signer3 = startSigner(signerConfig, signerDB3, keyProvider3, mockKit).listen(3003) - }) - - it('Should respond with 200 on valid request', async () => { - const res = await sendLegacyPnpSignRequest(req, authorization, app) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: 1, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - const unblindedSig = threshold_bls.unblind( - Buffer.from(res.body.signature, 'base64'), - blindedMsgResult.blindingFactor - ) - expect(Buffer.from(unblindedSig).toString('base64')).toEqual(expectedUnblindedSig) - }) - }) - - describe('when 1/3 signers return correct signatures', () => { - beforeEach(async () => { - const badBlsShare1 = - '000000002e50aa714ef6b865b5de89c56969ef9f8f27b6b0a6d157c9cc01c574ac9df604' - const badBlsShare2 = - '01000000b8f0ef841dcf8d7bd1da5e8025e47d729eb67f513335784183b8fa227a0b9a0b' - - const badKeyProvider1 = new MockKeyProvider( - new Map([[`${DefaultKeyName.PHONE_NUMBER_PRIVACY}-1`, badBlsShare1]]) - ) - const badKeyProvider2 = new MockKeyProvider( - new Map([[`${DefaultKeyName.PHONE_NUMBER_PRIVACY}-1`, badBlsShare2]]) - ) - - signer1 = startSigner(signerConfig, signerDB1, keyProvider1, mockKit).listen(3001) - signer2 = startSigner(signerConfig, signerDB2, badKeyProvider1, mockKit).listen(3002) - signer3 = startSigner(signerConfig, signerDB3, badKeyProvider2, mockKit).listen(3003) - }) - - it('Should respond with 500 even if request is valid', async () => { - const res = await sendLegacyPnpSignRequest(req, authorization, app) - - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: ErrorMessage.NOT_ENOUGH_PARTIAL_SIGNATURES, - }) - }) - }) - - describe('when 2/3 of signers are disabled', () => { - beforeEach(async () => { - const configWithApiDisabled: SignerConfig = JSON.parse(JSON.stringify(signerConfig)) - configWithApiDisabled.api.legacyPhoneNumberPrivacy.enabled = false - signer1 = startSigner(signerConfig, signerDB1, keyProvider1, mockKit).listen(3001) - signer2 = startSigner(configWithApiDisabled, signerDB2, keyProvider2, mockKit).listen(3002) - signer3 = startSigner(configWithApiDisabled, signerDB3, keyProvider3, mockKit).listen(3003) - }) - - it('Should fail to reach threshold of signers on valid request', async () => { - const res = await sendLegacyPnpSignRequest(req, authorization, app) - - expect(res.status).toBe(503) // majority error code in this case - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: ErrorMessage.NOT_ENOUGH_PARTIAL_SIGNATURES, - }) - }) - }) - - describe('when 1/3 of signers are disabled', () => { - beforeEach(async () => { - const configWithApiDisabled: SignerConfig = JSON.parse(JSON.stringify(signerConfig)) - configWithApiDisabled.api.legacyPhoneNumberPrivacy.enabled = false - signer1 = startSigner(signerConfig, signerDB1, keyProvider1, mockKit).listen(3001) - signer2 = startSigner(signerConfig, signerDB2, keyProvider2, mockKit).listen(3002) - signer3 = startSigner(configWithApiDisabled, signerDB3, keyProvider3, mockKit).listen(3003) - }) - - it('Should respond with 200 on valid request', async () => { - const res = await sendLegacyPnpSignRequest(req, authorization, app) - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: 1, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - }) - }) - - describe('when signers timeout', () => { - beforeEach(async () => { - const testTimeoutMS = 0 - - const configWithShortTimeout: SignerConfig = JSON.parse(JSON.stringify(signerConfig)) - configWithShortTimeout.timeout = testTimeoutMS - // Test this with all signers timing out to decrease possibility of race conditions - signer1 = startSigner(configWithShortTimeout, signerDB1, keyProvider1, mockKit).listen(3001) - signer2 = startSigner(configWithShortTimeout, signerDB2, keyProvider2, mockKit).listen(3002) - signer3 = startSigner(configWithShortTimeout, signerDB3, keyProvider3, mockKit).listen(3003) - }) - it('Should fail to reach threshold of signers on valid request', async () => { - const res = await sendLegacyPnpSignRequest(req, authorization, app) - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: ErrorMessage.NOT_ENOUGH_PARTIAL_SIGNATURES, - }) - }) - }) - }) -}) diff --git a/packages/phone-number-privacy/combiner/test/integration/pnp.test.ts b/packages/phone-number-privacy/combiner/test/integration/pnp.test.ts deleted file mode 100644 index f74d4432bf5..00000000000 --- a/packages/phone-number-privacy/combiner/test/integration/pnp.test.ts +++ /dev/null @@ -1,1376 +0,0 @@ -import { newKit } from '@celo/contractkit' -import { - AuthenticationMethod, - CombinerEndpoint, - ErrorMessage, - FULL_NODE_TIMEOUT_IN_MS, - genSessionID, - KEY_VERSION_HEADER, - PnpQuotaRequest, - PnpQuotaResponseFailure, - PnpQuotaResponseSuccess, - SignerEndpoint, - SignMessageRequest, - SignMessageResponseFailure, - SignMessageResponseSuccess, - TestUtils, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import { - initDatabase as initSignerDatabase, - startSigner, - SupportedDatabase, - SupportedKeystore, -} from '@celo/phone-number-privacy-signer' -import { - DefaultKeyName, - KeyProvider, -} from '@celo/phone-number-privacy-signer/dist/common/key-management/key-provider-base' -import { MockKeyProvider } from '@celo/phone-number-privacy-signer/dist/common/key-management/mock-key-provider' -import { SignerConfig } from '@celo/phone-number-privacy-signer/dist/config' -import BigNumber from 'bignumber.js' -import threshold_bls from 'blind-threshold-bls' -import { Server as HttpsServer } from 'https' -import { Knex } from 'knex' -import { Server } from 'net' -import request from 'supertest' -import config, { getCombinerVersion } from '../../src/config' -import { startCombiner } from '../../src/server' -import { getBlindedPhoneNumber } from '../utils' - -const { - ContractRetrieval, - createMockContractKit, - createMockAccounts, - createMockOdisPayments, - createMockWeb3, - getPnpRequestAuthorization, -} = TestUtils.Utils -const { - PRIVATE_KEY1, - ACCOUNT_ADDRESS1, - mockAccount, - DEK_PRIVATE_KEY, - DEK_PUBLIC_KEY, - PNP_THRESHOLD_DEV_PK_SHARE_1_V1, - PNP_THRESHOLD_DEV_PK_SHARE_1_V2, - PNP_THRESHOLD_DEV_PK_SHARE_1_V3, - PNP_THRESHOLD_DEV_PK_SHARE_2_V1, - PNP_THRESHOLD_DEV_PK_SHARE_2_V2, - PNP_THRESHOLD_DEV_PK_SHARE_2_V3, - PNP_THRESHOLD_DEV_PK_SHARE_3_V1, - PNP_THRESHOLD_DEV_PK_SHARE_3_V2, - PNP_THRESHOLD_DEV_PK_SHARE_3_V3, - ACCOUNT_ADDRESS2, - BLINDING_FACTOR, -} = TestUtils.Values - -// create deep copy of config -const combinerConfig: typeof config = JSON.parse(JSON.stringify(config)) -combinerConfig.phoneNumberPrivacy.enabled = true - -const signerConfig: SignerConfig = { - serviceName: 'odis-signer', - server: { - port: undefined, - sslKeyPath: undefined, - sslCertPath: undefined, - }, - quota: { - unverifiedQueryMax: 10, - additionalVerifiedQueryMax: 30, - queryPerTransaction: 2, - // Min balance is .01 cUSD - minDollarBalance: new BigNumber(1e16), - // Min balance is .01 cEUR - minEuroBalance: new BigNumber(1e16), - // Min balance is .005 CELO - minCeloBalance: new BigNumber(5e15), - // Equivalent to 0.001 cUSD/query - queryPriceInCUSD: new BigNumber(0.001), - }, - api: { - domains: { - enabled: false, - }, - phoneNumberPrivacy: { - enabled: true, - shouldFailOpen: true, - }, - legacyPhoneNumberPrivacy: { - enabled: false, - shouldFailOpen: true, - }, - }, - attestations: { - numberAttestationsRequired: 3, - }, - blockchain: { - provider: 'https://alfajores-forno.celo-testnet.org', - apiKey: undefined, - }, - db: { - type: SupportedDatabase.Sqlite, - user: '', - password: '', - database: '', - host: 'http://localhost', - port: undefined, - ssl: true, - poolMaxSize: 50, - }, - keystore: { - type: SupportedKeystore.MOCK_SECRET_MANAGER, - keys: { - phoneNumberPrivacy: { - name: 'phoneNumberPrivacy', - latest: 2, - }, - domains: { - name: 'domains', - latest: 1, - }, - }, - azure: { - clientID: '', - clientSecret: '', - tenant: '', - vaultName: '', - }, - google: { - projectId: '', - }, - aws: { - region: '', - secretKey: '', - }, - }, - timeout: 5000, - test_quota_bypass_percentage: 0, - fullNodeTimeoutMs: FULL_NODE_TIMEOUT_IN_MS, -} - -const testBlockNumber = 1000000 - -const mockOdisPaymentsTotalPaidCUSD = jest.fn() -const mockGetWalletAddress = jest.fn() -const mockGetDataEncryptionKey = jest.fn() - -const mockContractKit = createMockContractKit( - { - [ContractRetrieval.getAccounts]: createMockAccounts( - mockGetWalletAddress, - mockGetDataEncryptionKey - ), - [ContractRetrieval.getOdisPayments]: createMockOdisPayments(mockOdisPaymentsTotalPaidCUSD), - }, - createMockWeb3(5, testBlockNumber) -) - -// Mock newKit as opposed to the CK constructor -// Returns an object of type ContractKit that can be passed into the signers + combiner -jest.mock('@celo/contractkit', () => ({ - ...jest.requireActual('@celo/contractkit'), - newKit: jest.fn().mockImplementation(() => mockContractKit), -})) - -describe('pnpService', () => { - let keyProvider1: KeyProvider - let keyProvider2: KeyProvider - let keyProvider3: KeyProvider - let signerDB1: Knex - let signerDB2: Knex - let signerDB3: Knex - let signer1: Server | HttpsServer - let signer2: Server | HttpsServer - let signer3: Server | HttpsServer - let app: any - - // Used by PNP_SIGN tests for various configurations of signers - let userSeed: Uint8Array - let blindedMsgResult: threshold_bls.BlindedMessage - - const signerMigrationsPath = '../signer/dist/common/database/migrations' - const expectedVersion = getCombinerVersion() - - const onChainPaymentsDefault = new BigNumber(1e18) - const expectedTotalQuota = 1000 - - const message = Buffer.from('test message', 'utf8') - - // In current setup, the same mocked kit is used for the combiner and signers - const mockKit = newKit('dummyKit') - - const sendPnpSignRequest = async ( - req: SignMessageRequest, - authorization: string, - app: any, - keyVersionHeader?: string - ) => { - let reqWithHeaders = request(app) - .post(CombinerEndpoint.PNP_SIGN) - .set('Authorization', authorization) - - if (keyVersionHeader) { - reqWithHeaders = reqWithHeaders.set(KEY_VERSION_HEADER, keyVersionHeader) - } - return reqWithHeaders.send(req) - } - - const getSignRequest = (_blindedMsgResult: threshold_bls.BlindedMessage): SignMessageRequest => { - return { - account: ACCOUNT_ADDRESS1, - blindedQueryPhoneNumber: Buffer.from(_blindedMsgResult.message).toString('base64'), - sessionID: genSessionID(), - } - } - - const useQuery = async (performedQueryCount: number, signer: Server | HttpsServer) => { - for (let i = 0; i < performedQueryCount; i++) { - const phoneNumber = '+1' + Math.floor(Math.random() * 10 ** 10) - const blindedNumber = getBlindedPhoneNumber(phoneNumber, BLINDING_FACTOR) - const req = { - account: ACCOUNT_ADDRESS1, - blindedQueryPhoneNumber: blindedNumber, - } - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - await request(signer) - .post(SignerEndpoint.PNP_SIGN) - .set('Authorization', authorization) - .send(req) - } - } - - const getCombinerQuotaResponse = async ( - req: PnpQuotaRequest, - authorization: string, - _app: any = app - ) => { - const res = await request(_app) - .post(CombinerEndpoint.PNP_QUOTA) - .set('Authorization', authorization) - .send(req) - return res - } - - describe('with n=3, t=2', () => { - const expectedSignatures: string[] = [ - 'xgFMQtcgAMHJAEX/m9B4VFopYtxqPFSw0024sWzRYvQDvnmFqhXOPdnRDfa8WCEA', - 'wUuFV8yFBXGyEzKbyWjBChG6dER264nwjOsqErd/UZieVKE0oDMZcMDG+qObu4QB', - 'PJHqBGavcQG3NGFl3hiR8GymeDNumxbl1DnCJzWz+Ik5yCN2ZpAITBe24RTX0iMA', - ] - const expectedSignature = expectedSignatures[config.phoneNumberPrivacy.keys.currentVersion - 1] - - const expectedUnblindedSigs: string[] = [ - 'lOASnDJNbJBTMYfkbU4fMiK7FcNwSyqZo8iQSM95X8YK+/158be4S1A+jcQsCUYA', - 'QIT7HtHTe/d0Tq40Mf3rpHCT8qY20+8q7ZW9PXHFMWGvwSGhk7l3Pfwnx8YdXomB', - 'XW//DolLzaXYS/gk9WBHfeKy5HKrGjuF/OpCok/i6fprE4AGFH2PjE7zeKTfOQ+A', - ] - const expectedUnblindedSig = - expectedUnblindedSigs[config.phoneNumberPrivacy.keys.currentVersion - 1] - - beforeAll(async () => { - keyProvider1 = new MockKeyProvider( - new Map([ - [`${DefaultKeyName.PHONE_NUMBER_PRIVACY}-1`, PNP_THRESHOLD_DEV_PK_SHARE_1_V1], - [`${DefaultKeyName.PHONE_NUMBER_PRIVACY}-2`, PNP_THRESHOLD_DEV_PK_SHARE_1_V2], - [`${DefaultKeyName.PHONE_NUMBER_PRIVACY}-3`, PNP_THRESHOLD_DEV_PK_SHARE_1_V3], - ]) - ) - keyProvider2 = new MockKeyProvider( - new Map([ - [`${DefaultKeyName.PHONE_NUMBER_PRIVACY}-1`, PNP_THRESHOLD_DEV_PK_SHARE_2_V1], - [`${DefaultKeyName.PHONE_NUMBER_PRIVACY}-2`, PNP_THRESHOLD_DEV_PK_SHARE_2_V2], - [`${DefaultKeyName.PHONE_NUMBER_PRIVACY}-3`, PNP_THRESHOLD_DEV_PK_SHARE_2_V3], - ]) - ) - keyProvider3 = new MockKeyProvider( - new Map([ - [`${DefaultKeyName.PHONE_NUMBER_PRIVACY}-1`, PNP_THRESHOLD_DEV_PK_SHARE_3_V1], - [`${DefaultKeyName.PHONE_NUMBER_PRIVACY}-2`, PNP_THRESHOLD_DEV_PK_SHARE_3_V2], - [`${DefaultKeyName.PHONE_NUMBER_PRIVACY}-3`, PNP_THRESHOLD_DEV_PK_SHARE_3_V3], - ]) - ) - app = startCombiner(combinerConfig, mockKit) - }) - - beforeEach(async () => { - signerDB1 = await initSignerDatabase(signerConfig, signerMigrationsPath) - signerDB2 = await initSignerDatabase(signerConfig, signerMigrationsPath) - signerDB3 = await initSignerDatabase(signerConfig, signerMigrationsPath) - - userSeed = new Uint8Array(32) - for (let i = 0; i < userSeed.length - 1; i++) { - userSeed[i] = i - } - - blindedMsgResult = threshold_bls.blind(message, userSeed) - - mockGetDataEncryptionKey.mockReset().mockReturnValue(DEK_PUBLIC_KEY) - mockGetWalletAddress.mockReset().mockReturnValue(mockAccount) - }) - - afterEach(async () => { - await signerDB1?.destroy() - await signerDB2?.destroy() - await signerDB3?.destroy() - signer1?.close() - signer2?.close() - signer3?.close() - }) - - describe('when signers are operating correctly', () => { - beforeEach(async () => { - signer1 = startSigner(signerConfig, signerDB1, keyProvider1, mockKit).listen(3001) - signer2 = startSigner(signerConfig, signerDB2, keyProvider2, mockKit).listen(3002) - signer3 = startSigner(signerConfig, signerDB3, keyProvider3, mockKit).listen(3003) - }) - - describe(`${CombinerEndpoint.PNP_QUOTA}`, () => { - const totalQuota = 10 - const weiTocusd = new BigNumber(1e18) - beforeAll(async () => { - mockOdisPaymentsTotalPaidCUSD.mockReturnValue( - weiTocusd.multipliedBy(totalQuota).multipliedBy(signerConfig.quota.queryPriceInCUSD) - ) - }) - - const queryCountParams = [ - { signerQueries: [0, 0, 0], expectedQueryCount: 0, expectedWarnings: [] }, - { - signerQueries: [1, 0, 0], - expectedQueryCount: 0, - expectedWarnings: [WarningMessage.SIGNER_RESPONSE_DISCREPANCIES], - }, // does not reach threshold - { - signerQueries: [1, 1, 0], - expectedQueryCount: 1, - expectedWarnings: [WarningMessage.SIGNER_RESPONSE_DISCREPANCIES], - }, // threshold reached - { - signerQueries: [0, 1, 1], - expectedQueryCount: 1, - expectedWarnings: [WarningMessage.SIGNER_RESPONSE_DISCREPANCIES], - }, // order of signers shouldn't matter - { - signerQueries: [1, 4, 9], - expectedQueryCount: 4, - expectedWarnings: [ - WarningMessage.SIGNER_RESPONSE_DISCREPANCIES, - WarningMessage.INCONSISTENT_SIGNER_QUERY_MEASUREMENTS, - ], - }, - ] - queryCountParams.forEach(({ signerQueries, expectedQueryCount, expectedWarnings }) => { - it(`should get ${expectedQueryCount} performedQueryCount given signer responses of ${signerQueries}`, async () => { - await useQuery(signerQueries[0], signer1) - await useQuery(signerQueries[1], signer2) - await useQuery(signerQueries[2], signer3) - - const req = { - account: ACCOUNT_ADDRESS1, - } - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await getCombinerQuotaResponse(req, authorization) - - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - performedQueryCount: expectedQueryCount, - totalQuota, - blockNumber: testBlockNumber, - warnings: expectedWarnings, - }) - }) - }) - - it('Should respond with 200 on valid request', async () => { - const req = { - account: ACCOUNT_ADDRESS1, - } - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await getCombinerQuotaResponse(req, authorization) - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - performedQueryCount: 0, - totalQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - }) - - it('Should respond with 200 on repeated valid requests', async () => { - const req = { - account: ACCOUNT_ADDRESS1, - } - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res1 = await getCombinerQuotaResponse(req, authorization) - expect(res1.status).toBe(200) - expect(res1.body).toStrictEqual({ - success: true, - version: expectedVersion, - performedQueryCount: 0, - totalQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - const res2 = await getCombinerQuotaResponse(req, authorization) - expect(res2.status).toBe(200) - expect(res2.body).toStrictEqual(res1.body) - }) - - it('Should respond with 200 on extra request fields', async () => { - const req = { - account: ACCOUNT_ADDRESS1, - extraField: 'dummy', - } - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await getCombinerQuotaResponse(req, authorization) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - performedQueryCount: 0, - totalQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - }) - - it('Should respond with 200 when authenticated with DEK', async () => { - const req = { - account: ACCOUNT_ADDRESS1, - authenticationMethod: AuthenticationMethod.ENCRYPTION_KEY, - } - const authorization = getPnpRequestAuthorization(req, DEK_PRIVATE_KEY) - const res = await getCombinerQuotaResponse(req, authorization) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - performedQueryCount: 0, - totalQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - }) - - it('Should respond with a warning when there are slight discrepancies in total quota', async () => { - mockOdisPaymentsTotalPaidCUSD.mockReturnValueOnce( - weiTocusd.multipliedBy(totalQuota + 1).multipliedBy(signerConfig.quota.queryPriceInCUSD) - ) - const req = { - account: ACCOUNT_ADDRESS1, - } - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await getCombinerQuotaResponse(req, authorization) - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - performedQueryCount: 0, - totalQuota, - blockNumber: testBlockNumber, - warnings: [ - WarningMessage.SIGNER_RESPONSE_DISCREPANCIES, - WarningMessage.INCONSISTENT_SIGNER_QUOTA_MEASUREMENTS + - ', using threshold signer as best guess', - ], - }) - }) - - it('Should respond with 500 when there are large discrepancies in total quota', async () => { - mockOdisPaymentsTotalPaidCUSD.mockReturnValueOnce(weiTocusd.multipliedBy(totalQuota + 15)) - const req = { - account: ACCOUNT_ADDRESS1, - } - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await getCombinerQuotaResponse(req, authorization) - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: ErrorMessage.THRESHOLD_PNP_QUOTA_STATUS_FAILURE, - }) - }) - - it('Should respond with 400 on missing request fields', async () => { - // @ts-ignore Intentionally missing required fields - const req: PnpQuotaRequest = {} - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await getCombinerQuotaResponse(req, authorization) - - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 with invalid address', async () => { - const req = { - account: 'not an address', - } - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await getCombinerQuotaResponse(req, authorization) - - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 401 on failed WALLET_KEY auth', async () => { - // Request from one account, signed by another account - const req = { - account: ACCOUNT_ADDRESS2, - } - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await getCombinerQuotaResponse(req, authorization) - - expect(res.status).toBe(401) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.UNAUTHENTICATED_USER, - }) - }) - - it('Should respond with 401 on failed DEK auth', async () => { - const req = { - account: ACCOUNT_ADDRESS2, - AuthenticationMethod: AuthenticationMethod.ENCRYPTION_KEY, - } - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await getCombinerQuotaResponse(req, authorization) - - expect(res.status).toBe(401) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.UNAUTHENTICATED_USER, - }) - }) - - // This previously returned 502 instead of 500 - it('Should respond with 500 when insufficient signer responses', async () => { - await signerDB1?.destroy() - await signerDB2?.destroy() - signer1?.close() - signer2?.close() - - const req = { - account: ACCOUNT_ADDRESS1, - } - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await getCombinerQuotaResponse(req, authorization) - - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: ErrorMessage.THRESHOLD_PNP_QUOTA_STATUS_FAILURE, - }) - }) - - it('Should respond with 503 on disabled api', async () => { - const configWithApiDisabled: typeof combinerConfig = JSON.parse( - JSON.stringify(combinerConfig) - ) - configWithApiDisabled.phoneNumberPrivacy.enabled = false - const appWithApiDisabled = startCombiner(configWithApiDisabled, mockKit) - const req = { - account: ACCOUNT_ADDRESS1, - } - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await request(appWithApiDisabled) - .post(CombinerEndpoint.PNP_QUOTA) - .set('Authorization', authorization) - .send(req) - expect(res.status).toBe(503) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.API_UNAVAILABLE, - }) - }) - - describe('functionality in case of errors', () => { - it('Should respond with 200 on failure to fetch DEK when shouldFailOpen is true', async () => { - mockGetDataEncryptionKey.mockReset().mockImplementation(() => { - throw new Error() - }) - - const req = { - account: ACCOUNT_ADDRESS1, - authenticationMethod: AuthenticationMethod.ENCRYPTION_KEY, - } - - // NOT the dek private key, so authentication would fail if getDataEncryptionKey succeeded - const differentPk = '0x00000000000000000000000000000000000000000000000000000000ddddbbbb' - const authorization = getPnpRequestAuthorization(req, differentPk) - - const combinerConfigWithFailOpenEnabled: typeof combinerConfig = JSON.parse( - JSON.stringify(combinerConfig) - ) - combinerConfigWithFailOpenEnabled.phoneNumberPrivacy.shouldFailOpen = true - const appWithFailOpenEnabled = startCombiner(combinerConfigWithFailOpenEnabled, mockKit) - const res = await getCombinerQuotaResponse(req, authorization, appWithFailOpenEnabled) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - performedQueryCount: 0, - totalQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - }) - }) - }) - - describe(`${CombinerEndpoint.PNP_SIGN}`, () => { - let req: SignMessageRequest - - beforeEach(async () => { - mockOdisPaymentsTotalPaidCUSD.mockReturnValue(onChainPaymentsDefault) - req = getSignRequest(blindedMsgResult) - }) - - it('Should respond with 200 on valid request', async () => { - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendPnpSignRequest(req, authorization, app) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: 1, - totalQuota: expectedTotalQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - const unblindedSig = threshold_bls.unblind( - Buffer.from(res.body.signature, 'base64'), - blindedMsgResult.blindingFactor - ) - - expect(Buffer.from(unblindedSig).toString('base64')).toEqual(expectedUnblindedSig) - }) - - for (let i = 1; i <= 3; i++) { - it(`Should respond with 200 on valid request with key version header ${i}`, async () => { - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendPnpSignRequest(req, authorization, app, i.toString()) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignatures[i - 1], - performedQueryCount: 1, - totalQuota: expectedTotalQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - - const unblindedSig = threshold_bls.unblind( - Buffer.from(res.body.signature, 'base64'), - blindedMsgResult.blindingFactor - ) - - expect(Buffer.from(unblindedSig).toString('base64')).toEqual( - expectedUnblindedSigs[i - 1] - ) - }) - } - - it('Should respond with 200 on repeated valid requests', async () => { - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res1 = await sendPnpSignRequest(req, authorization, app) - const expectedResponse: SignMessageResponseSuccess = { - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: 1, - totalQuota: expectedTotalQuota, - blockNumber: testBlockNumber, - warnings: [], - } - - expect(res1.status).toBe(200) - expect(res1.body).toStrictEqual(expectedResponse) - - const res2 = await sendPnpSignRequest(req, authorization, app) - expect(res2.status).toBe(200) - // Do not expect performedQueryCount to increase since this is a duplicate request - expect(res2.body).toStrictEqual(expectedResponse) - }) - - it('Should increment performedQueryCount on request from the same account with a new message', async () => { - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res1 = await sendPnpSignRequest(req, authorization, app) - const expectedResponse: SignMessageResponseSuccess = { - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: 1, - totalQuota: expectedTotalQuota, - blockNumber: testBlockNumber, - warnings: [], - } - - expect(res1.status).toBe(200) - expect(res1.body).toStrictEqual(expectedResponse) - - // Second request for the same account but with new message - const message2 = Buffer.from('second test message', 'utf8') - const blindedMsg2 = threshold_bls.blind(message2, userSeed) - const req2 = getSignRequest(blindedMsg2) - const authorization2 = getPnpRequestAuthorization(req2, PRIVATE_KEY1) - - // Expect performedQueryCount to increase - expectedResponse.performedQueryCount++ - expectedResponse.signature = - 'PWvuSYIA249x1dx+qzgl6PKSkoulXXE/P4WHJvGmtw77pCRilEWTn3xSp+6JS9+A' - const res2 = await sendPnpSignRequest(req2, authorization2, app) - expect(res2.status).toBe(200) - expect(res2.body).toStrictEqual(expectedResponse) - }) - - it('Should respond with 200 on extra request fields', async () => { - // @ts-ignore Intentionally adding an extra field to the request type - req.extraField = 'dummyString' - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendPnpSignRequest(req, authorization, app) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: 1, - totalQuota: expectedTotalQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - }) - - it('Should respond with 200 when authenticated with DEK', async () => { - req.authenticationMethod = AuthenticationMethod.ENCRYPTION_KEY - const authorization = getPnpRequestAuthorization(req, DEK_PRIVATE_KEY) - const res = await sendPnpSignRequest(req, authorization, app) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: 1, - totalQuota: expectedTotalQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - }) - - it('Should respond with 200 on invalid key version', async () => { - req.authenticationMethod = AuthenticationMethod.ENCRYPTION_KEY - const authorization = getPnpRequestAuthorization(req, DEK_PRIVATE_KEY) - const res = await sendPnpSignRequest(req, authorization, app, 'invalid') - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: 1, - totalQuota: expectedTotalQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - }) - - it('Should get the same unblinded signatures from the same message (different seed)', async () => { - const authorization1 = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res1 = await sendPnpSignRequest(req, authorization1, app) - - expect(res1.status).toBe(200) - expect(res1.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: 1, - totalQuota: expectedTotalQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - - const secondUserSeed = new Uint8Array(userSeed) - secondUserSeed[0]++ - // Ensure request is identical except for blinded message - const req2 = { ...req } - const blindedMsgResult2 = threshold_bls.blind(message, secondUserSeed) - req2.blindedQueryPhoneNumber = Buffer.from(blindedMsgResult2.message).toString('base64') - - // Sanity check - expect(req2.blindedQueryPhoneNumber).not.toEqual(req.blindedQueryPhoneNumber) - - const authorization2 = getPnpRequestAuthorization(req2, PRIVATE_KEY1) - const res2 = await sendPnpSignRequest(req2, authorization2, app) - expect(res2.status).toBe(200) - const unblindedSig1 = threshold_bls.unblind( - Buffer.from(res1.body.signature, 'base64'), - blindedMsgResult.blindingFactor - ) - const unblindedSig2 = threshold_bls.unblind( - Buffer.from(res2.body.signature, 'base64'), - blindedMsgResult2.blindingFactor - ) - expect(Buffer.from(unblindedSig1).toString('base64')).toEqual(expectedUnblindedSig) - expect(unblindedSig1).toEqual(unblindedSig2) - }) - - it('Should respond with 400 on missing request fields', async () => { - // @ts-ignore Intentionally deleting required field - delete req.account - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendPnpSignRequest(req, authorization, app) - - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 on unsupported key version', async () => { - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendPnpSignRequest(req, authorization, app, '4') - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_KEY_VERSION_REQUEST, - }) - }) - - it('Should respond with 401 on failed WALLET_KEY auth', async () => { - req.account = mockAccount - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendPnpSignRequest(req, authorization, app) - - expect(res.status).toBe(401) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.UNAUTHENTICATED_USER, - }) - }) - - it('Should respond with 401 on failed DEK auth', async () => { - req.account = mockAccount - req.authenticationMethod = AuthenticationMethod.ENCRYPTION_KEY - const differentPk = '0x00000000000000000000000000000000000000000000000000000000ddddbbbb' - const authorization = getPnpRequestAuthorization(req, differentPk) - const res = await sendPnpSignRequest(req, authorization, app) - - expect(res.status).toBe(401) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.UNAUTHENTICATED_USER, - }) - }) - - it('Should respond with 403 on out of quota', async () => { - mockOdisPaymentsTotalPaidCUSD.mockReturnValue(new BigNumber(0)) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendPnpSignRequest(req, authorization, app) - - expect(res.status).toBe(403) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.EXCEEDED_QUOTA, - }) - }) - - it('Should respond with 503 on disabled api', async () => { - const configWithApiDisabled: typeof combinerConfig = JSON.parse( - JSON.stringify(combinerConfig) - ) - configWithApiDisabled.phoneNumberPrivacy.enabled = false - const appWithApiDisabled = startCombiner(configWithApiDisabled, mockKit) - - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendPnpSignRequest(req, authorization, appWithApiDisabled) - - expect(res.status).toBe(503) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.API_UNAVAILABLE, - }) - }) - - describe('functionality in case of errors', () => { - it('Should return 200 on failure to fetch DEK when shouldFailOpen is true', async () => { - mockGetDataEncryptionKey.mockImplementation(() => { - throw new Error() - }) - - req.authenticationMethod = AuthenticationMethod.ENCRYPTION_KEY - // NOT the dek private key, so authentication would fail if getDataEncryptionKey succeeded - const differentPk = '0x00000000000000000000000000000000000000000000000000000000ddddbbbb' - const authorization = getPnpRequestAuthorization(req, differentPk) - - const combinerConfigWithFailOpenEnabled: typeof combinerConfig = JSON.parse( - JSON.stringify(combinerConfig) - ) - combinerConfigWithFailOpenEnabled.phoneNumberPrivacy.shouldFailOpen = true - const appWithFailOpenEnabled = startCombiner(combinerConfigWithFailOpenEnabled, mockKit) - const res = await sendPnpSignRequest(req, authorization, appWithFailOpenEnabled) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: 1, - totalQuota: expectedTotalQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - const unblindedSig = threshold_bls.unblind( - Buffer.from(res.body.signature, 'base64'), - blindedMsgResult.blindingFactor - ) - expect(Buffer.from(unblindedSig).toString('base64')).toEqual(expectedUnblindedSig) - }) - - it('Should return 401 on failure to fetch DEK when shouldFailOpen is false', async () => { - mockGetDataEncryptionKey.mockImplementation(() => { - throw new Error() - }) - - req.authenticationMethod = AuthenticationMethod.ENCRYPTION_KEY - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - - const combinerConfigWithFailOpenDisabled: typeof combinerConfig = JSON.parse( - JSON.stringify(combinerConfig) - ) - combinerConfigWithFailOpenDisabled.phoneNumberPrivacy.shouldFailOpen = false - const appWithFailOpenDisabled = startCombiner( - combinerConfigWithFailOpenDisabled, - mockKit - ) - const res = await sendPnpSignRequest(req, authorization, appWithFailOpenDisabled) - - expect(res.status).toBe(401) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.UNAUTHENTICATED_USER, - }) - }) - }) - }) - }) - - // For testing combiner code paths when signers do not behave as expected - describe('when signers are not operating correctly', () => { - beforeEach(() => { - mockOdisPaymentsTotalPaidCUSD.mockReturnValue(onChainPaymentsDefault) - }) - - describe('when 2/3 signers return correct signatures', () => { - beforeEach(async () => { - const badBlsShare1 = - '000000002e50aa714ef6b865b5de89c56969ef9f8f27b6b0a6d157c9cc01c574ac9df604' - - const badKeyProvider1 = new MockKeyProvider( - new Map([[`${DefaultKeyName.PHONE_NUMBER_PRIVACY}-1`, badBlsShare1]]) - ) - signer1 = startSigner(signerConfig, signerDB1, badKeyProvider1, mockKit).listen(3001) - signer2 = startSigner(signerConfig, signerDB2, keyProvider2, mockKit).listen(3002) - signer3 = startSigner(signerConfig, signerDB3, keyProvider3, mockKit).listen(3003) - }) - - describe(`${CombinerEndpoint.PNP_SIGN}`, () => { - it('Should respond with 200 on valid request', async () => { - const req = getSignRequest(blindedMsgResult) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendPnpSignRequest(req, authorization, app) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: 1, - totalQuota: expectedTotalQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - const unblindedSig = threshold_bls.unblind( - Buffer.from(res.body.signature, 'base64'), - blindedMsgResult.blindingFactor - ) - expect(Buffer.from(unblindedSig).toString('base64')).toEqual(expectedUnblindedSig) - }) - }) - }) - - describe('when 1/3 signers return correct signatures', () => { - beforeEach(async () => { - const badBlsShare1 = - '000000002e50aa714ef6b865b5de89c56969ef9f8f27b6b0a6d157c9cc01c574ac9df604' - const badBlsShare2 = - '01000000b8f0ef841dcf8d7bd1da5e8025e47d729eb67f513335784183b8fa227a0b9a0b' - - const badKeyProvider1 = new MockKeyProvider( - new Map([[`${DefaultKeyName.PHONE_NUMBER_PRIVACY}-1`, badBlsShare1]]) - ) - - const badKeyProvider2 = new MockKeyProvider( - new Map([[`${DefaultKeyName.PHONE_NUMBER_PRIVACY}-1`, badBlsShare2]]) - ) - - signer1 = startSigner(signerConfig, signerDB1, keyProvider1, mockKit).listen(3001) - signer2 = startSigner(signerConfig, signerDB2, badKeyProvider1, mockKit).listen(3002) - signer3 = startSigner(signerConfig, signerDB3, badKeyProvider2, mockKit).listen(3003) - }) - - describe(`${CombinerEndpoint.PNP_SIGN}`, () => { - it('Should respond with 500 even if request is valid', async () => { - const req = getSignRequest(blindedMsgResult) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendPnpSignRequest(req, authorization, app) - - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: ErrorMessage.NOT_ENOUGH_PARTIAL_SIGNATURES, - }) - }) - }) - }) - - describe('when 2/3 of signers are disabled', () => { - beforeEach(async () => { - const configWithApiDisabled: SignerConfig = JSON.parse(JSON.stringify(signerConfig)) - configWithApiDisabled.api.phoneNumberPrivacy.enabled = false - signer1 = startSigner(signerConfig, signerDB1, keyProvider1, mockKit).listen(3001) - signer2 = startSigner(configWithApiDisabled, signerDB2, keyProvider2, mockKit).listen( - 3002 - ) - signer3 = startSigner(configWithApiDisabled, signerDB3, keyProvider3, mockKit).listen( - 3003 - ) - }) - - describe(`${CombinerEndpoint.PNP_QUOTA}`, () => { - it('Should fail to reach threshold of signers on valid request', async () => { - const req = { - account: ACCOUNT_ADDRESS1, - } - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await getCombinerQuotaResponse(req, authorization) - expect(res.status).toBe(503) // majority error code in this case - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: ErrorMessage.THRESHOLD_PNP_QUOTA_STATUS_FAILURE, - }) - }) - }) - - describe(`${CombinerEndpoint.PNP_SIGN}`, () => { - it('Should fail to reach threshold of signers on valid request', async () => { - const req = getSignRequest(blindedMsgResult) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendPnpSignRequest(req, authorization, app) - - expect(res.status).toBe(503) // majority error code in this case - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: ErrorMessage.NOT_ENOUGH_PARTIAL_SIGNATURES, - }) - }) - }) - }) - - describe('when 1/3 of signers are disabled', () => { - beforeEach(async () => { - const configWithApiDisabled: SignerConfig = JSON.parse(JSON.stringify(signerConfig)) - configWithApiDisabled.api.phoneNumberPrivacy.enabled = false - signer1 = startSigner(signerConfig, signerDB1, keyProvider1, mockKit).listen(3001) - signer2 = startSigner(signerConfig, signerDB2, keyProvider2, mockKit).listen(3002) - signer3 = startSigner(configWithApiDisabled, signerDB3, keyProvider3, mockKit).listen( - 3003 - ) - }) - - describe(`${CombinerEndpoint.PNP_QUOTA}`, () => { - it('Should respond with 200 on valid request', async () => { - const req = { - account: ACCOUNT_ADDRESS1, - } - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await getCombinerQuotaResponse(req, authorization) - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - performedQueryCount: 0, - totalQuota: expectedTotalQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - }) - }) - - describe(`${CombinerEndpoint.PNP_SIGN}`, () => { - it('Should respond with 200 on valid request', async () => { - const req = getSignRequest(blindedMsgResult) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendPnpSignRequest(req, authorization, app) - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: 1, - totalQuota: expectedTotalQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - }) - }) - }) - - describe('when signers timeout', () => { - beforeEach(async () => { - const testTimeoutMS = 0 - - const configWithShortTimeout: SignerConfig = JSON.parse(JSON.stringify(signerConfig)) - configWithShortTimeout.timeout = testTimeoutMS - // Test this with all signers timing out to decrease possibility of race conditions - signer1 = startSigner(configWithShortTimeout, signerDB1, keyProvider1, mockKit).listen( - 3001 - ) - signer2 = startSigner(configWithShortTimeout, signerDB2, keyProvider2, mockKit).listen( - 3002 - ) - signer3 = startSigner(configWithShortTimeout, signerDB3, keyProvider3, mockKit).listen( - 3003 - ) - }) - - describe(`${CombinerEndpoint.PNP_QUOTA}`, () => { - it('Should fail to reach threshold of signers on valid request', async () => { - const req = { - account: ACCOUNT_ADDRESS1, - } - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await getCombinerQuotaResponse(req, authorization) - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: ErrorMessage.THRESHOLD_PNP_QUOTA_STATUS_FAILURE, - }) - }) - }) - - describe(`${CombinerEndpoint.PNP_SIGN}`, () => { - it('Should fail to reach threshold of signers on valid request', async () => { - const req = getSignRequest(blindedMsgResult) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendPnpSignRequest(req, authorization, app) - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: ErrorMessage.NOT_ENOUGH_PARTIAL_SIGNATURES, - }) - }) - }) - }) - }) - }) - - // Ensure the same behavior when a minority of signers can block the threshold. - // On failure, the majority error code should not reflect the abort. - describe('with n=5, t=4', () => { - let keyProvider4: KeyProvider - let keyProvider5: KeyProvider - let signerDB4: Knex - let signerDB5: Knex - let signer4: Server | HttpsServer - let signer5: Server | HttpsServer - - const combinerConfigLargerN: typeof config = JSON.parse(JSON.stringify(combinerConfig)) - combinerConfigLargerN.phoneNumberPrivacy.odisServices.signers = JSON.stringify([ - { - url: 'http://localhost:3001', - fallbackUrl: 'http://localhost:3001/fallback', - }, - { - url: 'http://localhost:3002', - fallbackUrl: 'http://localhost:3002/fallback', - }, - { - url: 'http://localhost:3003', - fallbackUrl: 'http://localhost:3003/fallback', - }, - { - url: 'http://localhost:3004', - fallbackUrl: 'http://localhost:3004/fallback', - }, - { - url: 'http://localhost:3005', - fallbackUrl: 'http://localhost:3005/fallback', - }, - ]) - combinerConfigLargerN.phoneNumberPrivacy.keys.versions = JSON.stringify([ - { - keyVersion: 1, - threshold: 4, - polynomial: - '04000000000000007e196818fb4a5677ab97ef04a8b6e188e253d822c0689e37626fe9690d3a60283e74f2c38ec768f32870d73c7e11ff005ad65aa45707922dfc78d1fd54d64200da22a87d82b93783e2f9ee83ec290e25951c0dac2fb856871eba991731367a80b5f92e54b90901594c5e4d56beb15c44a437e78b90eb01bd4770b9c130feaf42c68d28d4e51415949d692936d3689e000f192e4fdcb03d45d1ffcd3615132046a3c8400e30cecaedf8d9bf275ead903e06ef0552a8326159f5361f8c8d16208197367a3115d3f15651082337e125005814a3f94c307e2205864803cc45dbb1b7e11738edec1d0630973830d0a74d0e0113c6ab677f087fb919728b8e1cb4f0004c6b59b4dcf28be7b4b9a5e9522e216b4d70278eff131717ff121b4203a2668093c54c6287cf9b09dd611627872f40f018f7e5a63eed5c94ead63fcd59515b1b8948482a5b7bdf07f91014d0097bba009316a8219c2074d16de09d557c2e7109112ade0d3f68248df7acfbbc4891acbb313d20021be70664d7a114a7fa6d9e01', - pubKey: - 'fhloGPtKVnerl+8EqLbhiOJT2CLAaJ43Ym/paQ06YCg+dPLDjsdo8yhw1zx+Ef8AWtZapFcHki38eNH9VNZCANoiqH2CuTeD4vnug+wpDiWVHA2sL7hWhx66mRcxNnqA', - }, - ]) - - beforeAll(async () => { - keyProvider1 = new MockKeyProvider( - new Map([ - [ - `${DefaultKeyName.PHONE_NUMBER_PRIVACY}-1`, - '00000000a49c8a293839ccb24bbcc7b833b0d57fe2f6087d33271750e7d6cf40897f520c', - ], - ]) - ) - keyProvider2 = new MockKeyProvider( - new Map([ - [ - `${DefaultKeyName.PHONE_NUMBER_PRIVACY}-1`, - '01000000c0866754b43a0e7c6f86c6732c1bc1bc1900f71a0ccab81fcd4048c5ff2edb02', - ], - ]) - ) - keyProvider3 = new MockKeyProvider( - new Map([ - [ - `${DefaultKeyName.PHONE_NUMBER_PRIVACY}-1`, - '02000000c24271a9dd0827e2939e5afbd5cd1c6705fa40d6e962fb288bbc7201921efa10', - ], - ]) - ) - keyProvider4 = new MockKeyProvider( - new Map([ - [ - `${DefaultKeyName.PHONE_NUMBER_PRIVACY}-1`, - '030000006320e2a99d4ce6a491a6354feda06051966a056dffbb0e7c8431b246d863ac09', - ], - ]) - ) - keyProvider5 = new MockKeyProvider( - new Map([ - [ - `${DefaultKeyName.PHONE_NUMBER_PRIVACY}-1`, - '04000000606ff4d6ddae61ac454009af2a49aeb4c297410ef9d3f3ab751c1c4fe5a99c0a', - ], - ]) - ) - app = startCombiner(combinerConfigLargerN, mockKit) - }) - - let req: SignMessageRequest - - beforeEach(async () => { - signerDB1 = await initSignerDatabase(signerConfig, signerMigrationsPath) - signerDB2 = await initSignerDatabase(signerConfig, signerMigrationsPath) - signerDB3 = await initSignerDatabase(signerConfig, signerMigrationsPath) - signerDB4 = await initSignerDatabase(signerConfig, signerMigrationsPath) - signerDB5 = await initSignerDatabase(signerConfig, signerMigrationsPath) - - signer1 = startSigner(signerConfig, signerDB1, keyProvider1).listen(3001) - signer2 = startSigner(signerConfig, signerDB2, keyProvider2).listen(3002) - signer3 = startSigner(signerConfig, signerDB3, keyProvider3).listen(3003) - signer4 = startSigner(signerConfig, signerDB4, keyProvider4).listen(3004) - signer5 = startSigner(signerConfig, signerDB5, keyProvider5).listen(3005) - - userSeed = new Uint8Array(32) - for (let i = 0; i < userSeed.length - 1; i++) { - userSeed[i] = i - } - - blindedMsgResult = threshold_bls.blind(message, userSeed) - req = getSignRequest(blindedMsgResult) - }) - - afterEach(async () => { - await signerDB1?.destroy() - await signerDB2?.destroy() - await signerDB3?.destroy() - await signerDB4?.destroy() - await signerDB5?.destroy() - signer1?.close() - signer2?.close() - signer3?.close() - signer4?.close() - signer5?.close() - }) - - it('Should respond with 200 on valid request', async () => { - mockOdisPaymentsTotalPaidCUSD.mockReturnValue(onChainPaymentsDefault) - - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendPnpSignRequest(req, authorization, app) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: res.body.signature, - performedQueryCount: 1, - totalQuota: expectedTotalQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - threshold_bls.unblind( - Buffer.from(res.body.signature, 'base64'), - blindedMsgResult.blindingFactor - ) - }) - - it('Should respond with 403 on out of quota', async () => { - mockOdisPaymentsTotalPaidCUSD.mockReturnValue(new BigNumber(0)) - - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendPnpSignRequest(req, authorization, app) - - expect(res.status).toBe(403) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.EXCEEDED_QUOTA, - }) - }) - }) -}) diff --git a/packages/phone-number-privacy/combiner/test/unit/bls-signature.test.ts b/packages/phone-number-privacy/combiner/test/unit/bls-signature.test.ts deleted file mode 100644 index 88a5cd3bb5d..00000000000 --- a/packages/phone-number-privacy/combiner/test/unit/bls-signature.test.ts +++ /dev/null @@ -1,295 +0,0 @@ -import { KeyVersionInfo, rootLogger } from '@celo/phone-number-privacy-common' -import threshold_bls from 'blind-threshold-bls' -import { BLSCryptographyClient } from '../../src/common/crypto-clients/bls-crypto-client' -import { ServicePartialSignature } from '../../src/common/crypto-clients/crypto-client' -import config from '../../src/config' - -const PUBLIC_KEY = - '8VZ0ZBPBjeRaH2nE+itNKiL/wKl38foK74MniCCIxvpA/9AfE1Uy7qbGGRyiKj8AAeiFpSaMzi7Flfe/Tj/qCWM8LMgQGR+eTvt7yiYsyKIVpGMJYVyzchEtPwFZyRyA' -const PUBLIC_POLYNOMIAL = - '0300000000000000f156746413c18de45a1f69c4fa2b4d2a22ffc0a977f1fa0aef8327882088c6fa40ffd01f135532eea6c6191ca22a3f0001e885a5268cce2ec595f7bf4e3fea09633c2cc810191f9e4efb7bca262cc8a215a46309615cb372112d3f0159c91c80ececfb0ecd57116e44c7580b57fe7c3f0f566d65f789f041b9febd83d3497e4c430af250cf8ac135f4782d283f3dd5009cf6e8de23a35be1cc21a8504ee2e3757a36f6c9813137d0f6b8aa75febc5ee77435cfd4280de80647670a60683e9481f091088a9940142b31d42ed3981dd548910fa41364f589c93c87bf62725468779a1442785600c08efbeff391f84e3200560dcd95d055998f4ef803a820356ef5b756cc75a98286bd21b5675cfe2db9bac0bcee64dc94c435d92aa5fbfa118680' -const SIGNATURES = [ - 'MAAAAAAAAADkHsKIX91BuKRjNgsJR81otwGGln4HuguYe4QkZoInFwNIiU9QglFZeLpJmNEysIAAAAAA', - 'MAAAAAAAAABqscf+GUMQD5I8SJW+zzZKuo83gyRZs/RUR7zePSDx4ZtewOGEc/VThpUpqgM5mAEBAAAA', - 'MAAAAAAAAABH006sJMay5D4OtOHDdQh3W8gX7yafeyMSGJzba7RhBAWatCEztthuQ6gSEOYTYQECAAAA', - 'MAAAAAAAAAAhzTl/S+mldhE+5F5rt+2XKJQsNtELZeo+aoHjhsVVdw8Ofk1ZRr9EUZbvVKetNYADAAAA', -] -const COMBINED_SIGNATURE = '16RcENpbLgq5pIkcPWdgnMofeLqSyuUVin9h4jof9/I8GRsmt5iRxjWAkpftKPWA' -const INVALID_SIGNATURE = - 'MAAAAAAAAACanrA73tApLu+j569ICcXrEBRLi4czWJtInJPSUpoZUOVDc1667hvMq1ESncFzlgEHAAAA' - -const keyVersionInfo: KeyVersionInfo = { - keyVersion: 1, - threshold: 3, - polynomial: PUBLIC_POLYNOMIAL, - pubKey: PUBLIC_KEY, -} - -config.phoneNumberPrivacy.keys = { - currentVersion: keyVersionInfo.keyVersion, - versions: JSON.stringify([keyVersionInfo]), -} - -describe(`BLS service computes signature`, () => { - it('provides blinded signature', async () => { - const signatures: ServicePartialSignature[] = [ - { - url: 'url1', - signature: SIGNATURES[0], - }, - { - url: 'url2', - signature: SIGNATURES[1], - }, - { - url: 'url3', - signature: SIGNATURES[2], - }, - { - url: 'url4', - signature: SIGNATURES[3], - }, - ] - - const message = Buffer.from('hello world') - const userSeed = new Uint8Array(32) - for (let i = 0; i < userSeed.length - 1; i++) { - userSeed[i] = i - } - - const blindedMsgResult = threshold_bls.blind(message, userSeed) - const blindedMsg = Buffer.from(blindedMsgResult.message).toString('base64') - - const blsCryptoClient = new BLSCryptographyClient(keyVersionInfo) - for (let i = 0; i < signatures.length; i++) { - blsCryptoClient.addSignature(signatures[i]) - if (i >= 2) { - expect(blsCryptoClient.hasSufficientSignatures()).toBeTruthy() - } else { - expect(blsCryptoClient.hasSufficientSignatures()).toBeFalsy() - } - } - - const actual = blsCryptoClient.combineBlindedSignatureShares( - blindedMsg, - rootLogger(config.serviceName) - ) - expect(actual).toEqual(COMBINED_SIGNATURE) - - const unblindedSignedMessage = threshold_bls.unblind( - Buffer.from(actual, 'base64'), - blindedMsgResult.blindingFactor - ) - const publicKey = Buffer.from(PUBLIC_KEY, 'base64') - expect(threshold_bls.verify(publicKey, message, unblindedSignedMessage)) - }) - it('provides blinded signature given one failure if still above threshold', async () => { - const signatures: ServicePartialSignature[] = [ - { - url: 'url1', - signature: SIGNATURES[0], - }, - { - url: 'url2', - signature: 'X', // This input causes signature combination to fail - }, - { - url: 'url3', - signature: SIGNATURES[2], - }, - { - url: 'url4', - signature: SIGNATURES[3], - }, - ] - - const message = Buffer.from('hello world') - const userSeed = new Uint8Array(32) - for (let i = 0; i < userSeed.length - 1; i++) { - userSeed[i] = i - } - - const blindedMsgResult = threshold_bls.blind(message, userSeed) - const blindedMsg = Buffer.from(blindedMsgResult.message).toString('base64') - - const blsCryptoClient = new BLSCryptographyClient(keyVersionInfo) - signatures.forEach(async (signature) => { - blsCryptoClient.addSignature(signature) - }) - const actual = blsCryptoClient.combineBlindedSignatureShares( - blindedMsg, - rootLogger(config.serviceName) - ) - expect(actual).toEqual(COMBINED_SIGNATURE) - - const unblindedSignedMessage = threshold_bls.unblind( - Buffer.from(actual, 'base64'), - blindedMsgResult.blindingFactor - ) - const publicKey = Buffer.from(PUBLIC_KEY, 'base64') - expect(threshold_bls.verify(publicKey, message, unblindedSignedMessage)) - }) - it('throws error if does not meet threshold signatures', async () => { - const signatures: ServicePartialSignature[] = [ - { - url: 'url1', - signature: SIGNATURES[0], - }, - { - url: 'url2', - signature: 'X', - }, - { - url: 'url3', - signature: 'X', - }, - { - url: 'url4', - signature: SIGNATURES[3], - }, - ] - - const message = Buffer.from('hello world') - const userSeed = new Uint8Array(32) - for (let i = 0; i < userSeed.length - 1; i++) { - userSeed[i] = i - } - - const blindedMsgResult = threshold_bls.blind(message, userSeed) - const blindedMsg = Buffer.from(blindedMsgResult.message).toString('base64') - - const blsCryptoClient = new BLSCryptographyClient(keyVersionInfo) - signatures.forEach(async (signature) => { - blsCryptoClient.addSignature(signature) - }) - try { - blsCryptoClient.combineBlindedSignatureShares(blindedMsg, rootLogger(config.serviceName)) - throw new Error('Expected failure with missing signatures') - } catch (e: any) { - expect(e.message.includes('Not enough partial signatures')).toBeTruthy() - } - }) - it('throws error if signature cannot be combined, but can recover from failure with sufficient signatures', async () => { - const signatures: ServicePartialSignature[] = [ - { - url: 'url1', - signature: SIGNATURES[0], - }, - { - url: 'url2', - signature: 'X', // This input causes signature combination to fail - }, - { - url: 'url3', - signature: SIGNATURES[2], - }, - { - url: 'url4', - signature: SIGNATURES[3], - }, - ] - - const message = Buffer.from('hello world') - const userSeed = new Uint8Array(32) - for (let i = 0; i < userSeed.length - 1; i++) { - userSeed[i] = i - } - - const blindedMsgResult = threshold_bls.blind(message, userSeed) - const blindedMsg = Buffer.from(blindedMsgResult.message).toString('base64') - - const blsCryptoClient = new BLSCryptographyClient(keyVersionInfo) - // Add sigs one-by-one and verify intermediary states - blsCryptoClient.addSignature(signatures[0]) - expect(blsCryptoClient.hasSufficientSignatures()).toBeFalsy() - blsCryptoClient.addSignature(signatures[1]) - expect(blsCryptoClient.hasSufficientSignatures()).toBeFalsy() - blsCryptoClient.addSignature(signatures[2]) - expect(blsCryptoClient.hasSufficientSignatures()).toBeTruthy() - // Should fail since 1/3 sigs are invalid - try { - blsCryptoClient.combineBlindedSignatureShares(blindedMsg, rootLogger(config.serviceName)) - } catch (e: any) { - expect(e.message.includes('Not enough partial signatures')).toBeTruthy() - } - // Should be false, now that the invalid signature has been removed - expect(blsCryptoClient.hasSufficientSignatures()).toBeFalsy() - - blsCryptoClient.addSignature(signatures[3]) - expect(blsCryptoClient.hasSufficientSignatures()).toBeTruthy() - const actual = blsCryptoClient.combineBlindedSignatureShares( - blindedMsg, - rootLogger(config.serviceName) - ) - expect(actual).toEqual(COMBINED_SIGNATURE) - - const unblindedSignedMessage = threshold_bls.unblind( - Buffer.from(actual, 'base64'), - blindedMsgResult.blindingFactor - ) - const publicKey = Buffer.from(PUBLIC_KEY, 'base64') - expect(threshold_bls.verify(publicKey, message, unblindedSignedMessage)) - }) - it('throws error if combined signature is invalid, and can recover from failure with sufficient valid partial signatures', async () => { - const signatures: ServicePartialSignature[] = [ - { - url: 'url1', - signature: SIGNATURES[0], - }, - { - url: 'url2', - signature: SIGNATURES[1], - }, - { - url: 'url3', - signature: INVALID_SIGNATURE, // Combination will succeed but verification will fail. - }, - { - url: 'url4', - signature: SIGNATURES[3], - }, - ] - - const message = Buffer.from('hello world') - const userSeed = new Uint8Array(32) - for (let i = 0; i < userSeed.length - 1; i++) { - userSeed[i] = i - } - - const blindedMsgResult = threshold_bls.blind(message, userSeed) - const blindedMsg = Buffer.from(blindedMsgResult.message).toString('base64') - - const blsCryptoClient = new BLSCryptographyClient(keyVersionInfo) - // Add sigs one-by-one and verify intermediary states - blsCryptoClient.addSignature(signatures[0]) - expect(blsCryptoClient.hasSufficientSignatures()).toBeFalsy() - blsCryptoClient.addSignature(signatures[1]) - expect(blsCryptoClient.hasSufficientSignatures()).toBeFalsy() - blsCryptoClient.addSignature(signatures[2]) - expect(blsCryptoClient.hasSufficientSignatures()).toBeTruthy() - // Should fail since signature from url3 was generated with the wrong key version - try { - blsCryptoClient.combineBlindedSignatureShares(blindedMsg, rootLogger(config.serviceName)) - } catch (e: any) { - expect(e.message.includes('Not enough partial signatures')).toBeTruthy() - } - - // Should be false, now that the invalid partial signature has been removed - expect(blsCryptoClient.hasSufficientSignatures()).toBeFalsy() - - blsCryptoClient.addSignature(signatures[3]) - expect(blsCryptoClient.hasSufficientSignatures()).toBeTruthy() - const actual = blsCryptoClient.combineBlindedSignatureShares( - blindedMsg, - rootLogger(config.serviceName) - ) - expect(actual).toEqual(COMBINED_SIGNATURE) - - const unblindedSignedMessage = threshold_bls.unblind( - Buffer.from(actual, 'base64'), - blindedMsgResult.blindingFactor - ) - const publicKey = Buffer.from(PUBLIC_KEY, 'base64') - expect(threshold_bls.verify(publicKey, message, unblindedSignedMessage)) - }) -}) diff --git a/packages/phone-number-privacy/combiner/test/unit/domain-response-logger.test.ts b/packages/phone-number-privacy/combiner/test/unit/domain-response-logger.test.ts deleted file mode 100644 index c97f661c38a..00000000000 --- a/packages/phone-number-privacy/combiner/test/unit/domain-response-logger.test.ts +++ /dev/null @@ -1,352 +0,0 @@ -import { - DisableDomainRequest, - DomainQuotaStatusRequest, - DomainRequest, - DomainRestrictedSignatureRequest, - KeyVersionInfo, - OdisResponse, - rootLogger, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import { getSignerVersion } from '@celo/phone-number-privacy-signer/src/config' -import { Request, Response } from 'express' -import { Session } from '../../src/common/session' -import config from '../../src/config' -import { DomainSignerResponseLogger } from '../../src/domain/services/log-responses' - -describe('domain response logger', () => { - const url = 'test signer url' - - const keyVersionInfo: KeyVersionInfo = { - keyVersion: 1, - threshold: 3, - polynomial: 'mock polynomial', - pubKey: 'mock pubKey', - } - - const getSession = (responses: OdisResponse[]) => { - const mockRequest = { - body: {}, - } as Request - - // @ts-ignore: missing some properties - const mockResponse = { - locals: { - logger: rootLogger(config.serviceName), - }, - } as Response - const session = new Session(mockRequest, mockResponse, keyVersionInfo) - responses.forEach((res) => { - session.responses.push({ url, res, status: 200 }) - }) - return session - } - - const version = getSignerVersion() - const counter = 1 - const disabled = false - const timer = 10000 - const domainResponseLogger = new DomainSignerResponseLogger() - - const testCases: { - it: string - responses: OdisResponse< - DomainRestrictedSignatureRequest | DomainQuotaStatusRequest | DisableDomainRequest - >[] - expectedLogs: { - params: string | any[] - level: 'info' | 'debug' | 'warn' | 'error' - }[] - }[] = [ - { - it: 'should log correctly when no responses provided', - responses: [], - expectedLogs: [ - { - params: ['No successful signer responses found!'], - level: 'warn', - }, - ], - }, - { - it: 'should log correctly when all the responses are the same (except for now field)', - responses: [ - { success: true, version, status: { counter, timer, disabled, now: Date.now() } }, - { success: true, version, status: { counter, timer, disabled, now: Date.now() } }, - { success: true, version, status: { counter, timer, disabled, now: Date.now() } }, - ], - expectedLogs: [], - }, - { - it: 'should log correctly when there is a discrepency in version field', - responses: [ - { success: true, version, status: { counter, timer, disabled, now: Date.now() } }, - { success: true, version, status: { counter, timer, disabled, now: Date.now() } }, - { - success: true, - version: 'differentVersion', - status: { counter, timer, disabled, now: Date.now() }, - }, - ], - expectedLogs: [ - { - params: [ - { - parsedResponses: [ - { - signerUrl: url, - values: { - counter, - disabled, - timer, - version, - }, - }, - { - signerUrl: url, - values: { - counter, - disabled, - timer, - version, - }, - }, - { - signerUrl: url, - values: { - counter, - disabled, - timer, - version: 'differentVersion', - }, - }, - ], - }, - WarningMessage.SIGNER_RESPONSE_DISCREPANCIES, - ], - level: 'warn', - }, - ], - }, - { - it: 'should log correctly when there is a discrepency in counter field', - responses: [ - { success: true, version, status: { counter, timer, disabled, now: Date.now() } }, - { success: true, version, status: { counter, timer, disabled, now: Date.now() } }, - { - success: true, - version, - status: { counter: counter + 1, timer, disabled, now: Date.now() }, - }, - ], - expectedLogs: [ - { - params: [ - { - parsedResponses: [ - { - signerUrl: url, - values: { - counter, - disabled, - timer, - version, - }, - }, - { - signerUrl: url, - values: { - counter, - disabled, - timer, - version, - }, - }, - { - signerUrl: url, - values: { - counter: counter + 1, - disabled, - timer, - version, - }, - }, - ], - }, - WarningMessage.SIGNER_RESPONSE_DISCREPANCIES, - ], - level: 'warn', - }, - ], - }, - { - it: 'should log correctly when there is a discrepency in disabled field', - responses: [ - { success: true, version, status: { counter, timer, disabled, now: Date.now() } }, - { success: true, version, status: { counter, timer, disabled, now: Date.now() } }, - { success: true, version, status: { counter, timer, disabled: true, now: Date.now() } }, - ], - expectedLogs: [ - { - params: [ - { - parsedResponses: [ - { - signerUrl: url, - values: { - counter, - disabled, - timer, - version, - }, - }, - { - signerUrl: url, - values: { - counter, - disabled, - timer, - version, - }, - }, - { - signerUrl: url, - values: { - counter, - disabled: true, - timer, - version, - }, - }, - ], - }, - WarningMessage.SIGNER_RESPONSE_DISCREPANCIES, - ], - level: 'warn', - }, - { - params: [ - { - parsedResponses: [ - { - signerUrl: url, - values: { - counter, - disabled, - timer, - version, - }, - }, - { - signerUrl: url, - values: { - counter, - disabled, - timer, - version, - }, - }, - { - signerUrl: url, - values: { - counter, - disabled: true, - timer, - version, - }, - }, - ], - }, - WarningMessage.INCONSISTENT_SIGNER_DOMAIN_DISABLED_STATES, - ], - level: 'error', - }, - ], - }, - { - it: 'should log correctly when there is a discrepency in timer field', - responses: [ - { success: true, version, status: { counter, timer, disabled, now: Date.now() } }, - { success: true, version, status: { counter, timer, disabled, now: Date.now() } }, - { - success: true, - version, - status: { counter, timer: timer + 1, disabled, now: Date.now() }, - }, - ], - expectedLogs: [ - { - params: [ - { - parsedResponses: [ - { - signerUrl: url, - values: { - counter, - disabled, - timer, - version, - }, - }, - { - signerUrl: url, - values: { - counter, - disabled, - timer, - version, - }, - }, - { - signerUrl: url, - values: { - counter, - disabled, - timer: timer + 1, - version, - }, - }, - ], - }, - WarningMessage.SIGNER_RESPONSE_DISCREPANCIES, - ], - level: 'warn', - }, - ], - }, - ] - testCases.forEach((testCase) => { - it(testCase.it, () => { - const session = getSession(testCase.responses) - const logSpys = { - info: { - spy: jest.spyOn(session.logger, 'info'), - callCount: 0, - }, - debug: { - spy: jest.spyOn(session.logger, 'debug'), - callCount: 0, - }, - warn: { - spy: jest.spyOn(session.logger, 'warn'), - callCount: 0, - }, - error: { - spy: jest.spyOn(session.logger, 'error'), - callCount: 0, - }, - } - domainResponseLogger.logResponseDiscrepancies(session) - testCase.expectedLogs.forEach((log) => { - expect(logSpys[log.level].spy).toHaveBeenNthCalledWith( - ++logSpys[log.level].callCount, - ...log.params - ) - }) - Object.values(logSpys).forEach((level) => { - level.spy.mockClear() - level.spy.mockRestore() - }) - }) - }) -}) diff --git a/packages/phone-number-privacy/combiner/test/unit/domain-threshold-state.test.ts b/packages/phone-number-privacy/combiner/test/unit/domain-threshold-state.test.ts deleted file mode 100644 index 9deec258d61..00000000000 --- a/packages/phone-number-privacy/combiner/test/unit/domain-threshold-state.test.ts +++ /dev/null @@ -1,177 +0,0 @@ -import { - DomainQuotaStatusResponseSuccess, - DomainRestrictedSignatureResponseSuccess, - KeyVersionInfo, - SequentialDelayDomainState, -} from '@celo/phone-number-privacy-common' -import { getSignerVersion } from '@celo/phone-number-privacy-signer/src/config' -import Logger from 'bunyan' -import { Request, Response } from 'express' -import { Session } from '../../src/common/session' -import config from '../../src/config' -import { DomainThresholdStateService } from '../../src/domain/services/threshold-state' - -describe('domain threshold state', () => { - // TODO add tests with failed signer responses, depending on - // result of https://github.com/celo-org/celo-monorepo/issues/9826 - - const keyVersionInfo: KeyVersionInfo = { - keyVersion: 1, - threshold: 3, - polynomial: 'mock polynomial', - pubKey: 'mock pubKey', - } - - const getSession = (domainStates: SequentialDelayDomainState[]) => { - const mockRequest = { - body: {}, - } as Request - - // @ts-ignore: missing some properties - const mockResponse = { - locals: { - logger: new Logger({ name: 'logger' }), - }, - } as Response - const session = new Session(mockRequest, mockResponse, keyVersionInfo) - domainStates.forEach((status) => { - const res: DomainRestrictedSignatureResponseSuccess | DomainQuotaStatusResponseSuccess = { - success: true, - version: expectedVersion, - status, - } - session.responses.push({ url: 'random url', res, status: 200 }) - }) - return session - } - - const domainConfig = config.domains - domainConfig.keys.currentVersion = keyVersionInfo.keyVersion - domainConfig.keys.versions = JSON.stringify([keyVersionInfo]) - domainConfig.odisServices.signers = JSON.stringify([ - { url: 'http://localhost:3001', fallbackUrl: 'http://localhost:3001/fallback' }, - { url: 'http://localhost:3002', fallbackUrl: 'http://localhost:3002/fallback' }, - { url: 'http://localhost:3003', fallbackUrl: 'http://localhost:3003/fallback' }, - { url: 'http://localhost:4004', fallbackUrl: 'http://localhost:4004/fallback' }, - ]) - - const domainThresholdStateService = new DomainThresholdStateService(domainConfig) - - const expectedVersion = getSignerVersion() - const now = Date.now() - const timer = now - 1 - const counter = 2 - - const varyingDomainStates = [ - { - statuses: [ - { timer, counter: 2, disabled: false, now }, - { timer, counter: 2, disabled: false, now }, - { timer, counter: 2, disabled: false, now }, - { timer, counter: 2, disabled: false, now }, - ], - expectedCounter: 2, - expectedTimer: timer, - }, - { - statuses: [ - { timer, counter: 1, disabled: false, now }, - { timer, counter: 2, disabled: false, now }, - { timer, counter: 2, disabled: false, now }, - { timer, counter: 2, disabled: false, now }, - ], - expectedCounter: 2, - expectedTimer: timer, - }, - { - statuses: [ - { timer, counter: 0, disabled: false, now }, - { timer, counter: 1, disabled: false, now }, - { timer, counter: 2, disabled: false, now }, - { timer, counter: 3, disabled: false, now }, - ], - expectedCounter: 2, - expectedTimer: timer, - }, - { - statuses: [ - { timer, counter: 0, disabled: true, now }, - { timer, counter: 1, disabled: false, now }, - { timer, counter: 2, disabled: false, now }, - { timer, counter: 3, disabled: false, now }, - ], - expectedCounter: 3, - expectedTimer: timer, - }, - { - statuses: [ - { timer: timer - 1, counter, disabled: false, now }, - { timer, counter, disabled: false, now }, - { timer, counter, disabled: false, now }, - { timer, counter, disabled: false, now }, - ], - expectedCounter: counter, - expectedTimer: timer, - }, - { - statuses: [ - { timer: timer - 1, counter, disabled: false, now }, - { timer: timer - 1, counter, disabled: false, now }, - { timer: timer - 1, counter, disabled: false, now }, - { timer, counter, disabled: false, now }, - ], - expectedCounter: counter, - expectedTimer: timer - 1, - }, - { - statuses: [ - { timer: timer - 1, counter: 1, disabled: false, now }, - { timer, counter: 1, disabled: false, now }, - { timer, counter: 2, disabled: false, now }, - { timer, counter: 3, disabled: false, now }, - ], - expectedCounter: 2, - expectedTimer: timer, - }, - ] - - varyingDomainStates.forEach(({ statuses, expectedCounter, expectedTimer }) => { - it(`should return counter:${expectedCounter} and timer:${expectedTimer} given the domain states: ${statuses}`, () => { - const session = getSession(statuses) - const thresholdResult = domainThresholdStateService.findThresholdDomainState(session) - - expect(thresholdResult).toStrictEqual({ - timer: expectedTimer, - counter: expectedCounter, - disabled: false, - now, - }) - }) - }) - - it('should return 0 values when too many disabled responses', () => { - const statuses = [ - { timer, counter: 0, disabled: true, now }, - { timer, counter: 1, disabled: true, now }, - { timer, counter: 2, disabled: false, now }, - { timer, counter: 2, disabled: false, now }, - ] - const session = getSession(statuses) - const thresholdResult = domainThresholdStateService.findThresholdDomainState(session) - - expect(thresholdResult).toStrictEqual({ timer: 0, counter: 0, disabled: true, now: 0 }) - }) - - it('should throw an error if not enough signer responses', () => { - const statuses = [ - { timer, counter: 1, disabled: true, now }, - { timer, counter: 2, disabled: false, now }, - { timer, counter: 2, disabled: false, now }, - ] - const session = getSession(statuses) - - expect(() => domainThresholdStateService.findThresholdDomainState(session)).toThrow( - 'Insufficient number of signer responses. Domain may be disabled' - ) - }) -}) diff --git a/packages/phone-number-privacy/combiner/test/unit/pnp-response-logger.test.ts b/packages/phone-number-privacy/combiner/test/unit/pnp-response-logger.test.ts deleted file mode 100644 index 51f17c32fc4..00000000000 --- a/packages/phone-number-privacy/combiner/test/unit/pnp-response-logger.test.ts +++ /dev/null @@ -1,715 +0,0 @@ -import { - ErrorMessage, - KeyVersionInfo, - OdisResponse, - PnpQuotaRequest, - rootLogger, - SignMessageRequest, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import { getSignerVersion } from '@celo/phone-number-privacy-signer/src/config' -import { Request, Response } from 'express' -import { Session } from '../../src/common/session' -import config, { - MAX_BLOCK_DISCREPANCY_THRESHOLD, - MAX_QUERY_COUNT_DISCREPANCY_THRESHOLD, - MAX_TOTAL_QUOTA_DISCREPANCY_THRESHOLD, -} from '../../src/config' -import { PnpSignerResponseLogger } from '../../src/pnp/services/log-responses' - -describe('pnp response logger', () => { - const url = 'test signer url' - - const keyVersionInfo: KeyVersionInfo = { - keyVersion: 1, - threshold: 3, - polynomial: 'mock polynomial', - pubKey: 'mock pubKey', - } - - const getSession = (responses: OdisResponse[]) => { - const mockRequest = { - body: {}, - } as Request - - // @ts-ignore: missing some properties - const mockResponse = { - locals: { - logger: rootLogger(config.serviceName), - }, - } as Response - const session = new Session( - mockRequest, - mockResponse, - keyVersionInfo - ) - responses.forEach((res) => { - session.responses.push({ url, res, status: 200 }) - }) - return session - } - - const pnpConfig = config.phoneNumberPrivacy - pnpConfig.keys.currentVersion = keyVersionInfo.keyVersion - pnpConfig.keys.versions = JSON.stringify([keyVersionInfo]) - const pnpResponseLogger = new PnpSignerResponseLogger() - - const version = getSignerVersion() - const blockNumber = 1000000 - const totalQuota = 10 - const performedQueryCount = 5 - const warnings = ['warning'] - - const testCases: { - it: string - responses: OdisResponse[] - expectedLogs: { - params: string | any[] - level: 'info' | 'debug' | 'warn' | 'error' - }[] - }[] = [ - { - it: 'should log correctly when no responses provided', - responses: [], - expectedLogs: [ - { - params: ['No successful signer responses found!'], - level: 'warn', - }, - ], - }, - { - it: 'should log correctly when all the responses are the same', - responses: [ - { success: true, performedQueryCount, totalQuota, version, blockNumber, warnings }, - { success: true, performedQueryCount, totalQuota, version, blockNumber, warnings }, - { success: true, performedQueryCount, totalQuota, version, blockNumber, warnings }, - ], - expectedLogs: [], - }, - { - it: 'should log correctly when there is a discrepency in version field', - responses: [ - { - success: true, - performedQueryCount, - totalQuota, - version: 'differentVersion', - blockNumber, - warnings, - }, - { success: true, performedQueryCount, totalQuota, version, blockNumber, warnings }, - { success: true, performedQueryCount, totalQuota, version, blockNumber, warnings }, - ], - expectedLogs: [ - { - params: [ - { - parsedResponses: [ - { - signerUrl: url, - values: { - blockNumber, - performedQueryCount, - totalQuota, - version: 'differentVersion', - warnings, - }, - }, - { - signerUrl: url, - values: { - blockNumber, - performedQueryCount, - totalQuota, - version, - warnings, - }, - }, - { - signerUrl: url, - values: { - blockNumber, - performedQueryCount, - totalQuota, - version, - warnings, - }, - }, - ], - }, - WarningMessage.SIGNER_RESPONSE_DISCREPANCIES, - ], - level: 'warn', - }, - ], - }, - { - it: 'should log correctly when there is a discrepency in performedQueryCount field', - responses: [ - { success: true, performedQueryCount: 1, totalQuota, version, blockNumber, warnings }, - { success: true, performedQueryCount, totalQuota, version, blockNumber, warnings }, - { success: true, performedQueryCount, totalQuota, version, blockNumber, warnings }, - ], - expectedLogs: [ - { - params: [ - { - parsedResponses: [ - { - signerUrl: url, - values: { - blockNumber, - performedQueryCount: 1, - totalQuota, - version, - warnings, - }, - }, - { - signerUrl: url, - values: { - blockNumber, - performedQueryCount, - totalQuota, - version, - warnings, - }, - }, - { - signerUrl: url, - values: { - blockNumber, - performedQueryCount, - totalQuota, - version, - warnings, - }, - }, - ], - }, - WarningMessage.SIGNER_RESPONSE_DISCREPANCIES, - ], - level: 'warn', - }, - ], - }, - { - it: 'should log correctly when there is a large discrepency in performedQueryCount field', - responses: [ - { - success: true, - performedQueryCount: performedQueryCount + MAX_QUERY_COUNT_DISCREPANCY_THRESHOLD, - totalQuota, - version, - blockNumber, - warnings, - }, - { success: true, performedQueryCount, totalQuota, version, blockNumber, warnings }, - { success: true, performedQueryCount, totalQuota, version, blockNumber, warnings }, - ], - expectedLogs: [ - { - params: [ - { - parsedResponses: [ - { - signerUrl: url, - values: { - blockNumber, - performedQueryCount, - totalQuota, - version, - warnings, - }, - }, - { - signerUrl: url, - values: { - blockNumber, - performedQueryCount, - totalQuota, - version, - warnings, - }, - }, - { - signerUrl: url, - values: { - blockNumber, - performedQueryCount: - performedQueryCount + MAX_QUERY_COUNT_DISCREPANCY_THRESHOLD, - totalQuota, - version, - warnings, - }, - }, - ], - }, - WarningMessage.SIGNER_RESPONSE_DISCREPANCIES, - ], - level: 'warn', - }, - { - params: [ - { - sortedByQueryCount: [ - { - signerUrl: url, - values: { - blockNumber, - performedQueryCount, - totalQuota, - version, - warnings, - }, - }, - { - signerUrl: url, - values: { - blockNumber, - performedQueryCount, - totalQuota, - version, - warnings, - }, - }, - { - signerUrl: url, - values: { - blockNumber, - performedQueryCount: - performedQueryCount + MAX_QUERY_COUNT_DISCREPANCY_THRESHOLD, - totalQuota, - version, - warnings, - }, - }, - ], - }, - WarningMessage.INCONSISTENT_SIGNER_QUERY_MEASUREMENTS, - ], - level: 'error', - }, - ], - }, - { - it: 'should log correctly when there is a discrepency in totalQuota field', - responses: [ - { success: true, performedQueryCount, totalQuota, version, blockNumber, warnings }, - { success: true, performedQueryCount, totalQuota, version, blockNumber, warnings }, - { success: true, performedQueryCount, totalQuota: 1, version, blockNumber, warnings }, - ], - expectedLogs: [ - { - params: [ - { - parsedResponses: [ - { - signerUrl: url, - values: { - blockNumber, - performedQueryCount, - totalQuota: 1, - version, - warnings, - }, - }, - { - signerUrl: url, - values: { - blockNumber, - performedQueryCount, - totalQuota, - version, - warnings, - }, - }, - { - signerUrl: url, - values: { - blockNumber, - performedQueryCount, - totalQuota, - version, - warnings, - }, - }, - ], - }, - WarningMessage.SIGNER_RESPONSE_DISCREPANCIES, - ], - level: 'warn', - }, - ], - }, - { - it: 'should log correctly when there is a large discrepency in totalQuota field', - responses: [ - { success: true, performedQueryCount, totalQuota, version, blockNumber, warnings }, - { success: true, performedQueryCount, totalQuota, version, blockNumber, warnings }, - { - success: true, - performedQueryCount, - totalQuota: totalQuota + MAX_TOTAL_QUOTA_DISCREPANCY_THRESHOLD, - version, - blockNumber, - warnings, - }, - ], - expectedLogs: [ - { - params: [ - { - sortedByTotalQuota: [ - { - signerUrl: url, - values: { - blockNumber, - performedQueryCount, - totalQuota, - version, - warnings, - }, - }, - { - signerUrl: url, - values: { - blockNumber, - performedQueryCount, - totalQuota, - version, - warnings, - }, - }, - { - signerUrl: url, - values: { - blockNumber, - performedQueryCount, - totalQuota: totalQuota + MAX_TOTAL_QUOTA_DISCREPANCY_THRESHOLD, - version, - warnings, - }, - }, - ], - }, - WarningMessage.INCONSISTENT_SIGNER_QUOTA_MEASUREMENTS, - ], - level: 'error', - }, - ], - }, - { - it: 'should log correctly when one signer returns an undefined blockNumber', - responses: [ - { success: true, performedQueryCount, totalQuota, version, blockNumber, warnings }, - { success: true, performedQueryCount, totalQuota, version, blockNumber, warnings }, - { - success: true, - performedQueryCount, - totalQuota, - version, - blockNumber: undefined, - warnings, - }, - ], - expectedLogs: [ - { - params: [ - { - parsedResponses: [ - { - signerUrl: url, - values: { - blockNumber, - performedQueryCount, - totalQuota, - version, - warnings, - }, - }, - { - signerUrl: url, - values: { - blockNumber, - performedQueryCount, - totalQuota, - version, - warnings, - }, - }, - { - signerUrl: url, - values: { - blockNumber: undefined, - performedQueryCount, - totalQuota, - version, - warnings, - }, - }, - ], - }, - WarningMessage.SIGNER_RESPONSE_DISCREPANCIES, - ], - level: 'warn', - }, - { - params: [{ signerUrl: url }, 'Signer responded with undefined blockNumber'], - level: 'warn', - }, - ], - }, - { - it: 'should log correctly when there is a large discrepency in blockNumber field', - responses: [ - { success: true, performedQueryCount, totalQuota, version, blockNumber, warnings }, - { success: true, performedQueryCount, totalQuota, version, blockNumber, warnings }, - { - success: true, - performedQueryCount, - totalQuota, - version, - blockNumber: blockNumber + MAX_BLOCK_DISCREPANCY_THRESHOLD, - warnings, - }, - ], - expectedLogs: [ - { - params: [ - { - sortedByBlockNumber: [ - { - signerUrl: url, - values: { - blockNumber, - performedQueryCount, - totalQuota, - version, - warnings, - }, - }, - { - signerUrl: url, - values: { - blockNumber, - performedQueryCount, - totalQuota, - version, - warnings, - }, - }, - { - signerUrl: url, - values: { - blockNumber: blockNumber + MAX_BLOCK_DISCREPANCY_THRESHOLD, - performedQueryCount, - totalQuota, - version, - warnings, - }, - }, - ], - }, - WarningMessage.INCONSISTENT_SIGNER_BLOCK_NUMBERS, - ], - level: 'error', - }, - ], - }, - { - it: 'should log correctly when there is a discrepency in warnings field', - responses: [ - { success: true, performedQueryCount, totalQuota, version, blockNumber, warnings }, - { success: true, performedQueryCount, totalQuota, version, blockNumber, warnings }, - { - success: true, - performedQueryCount, - totalQuota, - version, - blockNumber, - warnings: ['differentWarning'], - }, - ], - expectedLogs: [ - { - params: [ - { - parsedResponses: [ - { - signerUrl: url, - values: { - blockNumber, - performedQueryCount, - totalQuota, - version, - warnings, - }, - }, - { - signerUrl: url, - values: { - blockNumber, - performedQueryCount, - totalQuota, - version, - warnings, - }, - }, - { - signerUrl: url, - values: { - blockNumber, - performedQueryCount, - totalQuota, - version, - warnings: ['differentWarning'], - }, - }, - ], - }, - WarningMessage.SIGNER_RESPONSE_DISCREPANCIES, - ], - level: 'warn', - }, - ], - }, - { - it: 'should log correctly when signers respond with fail-open warnings', - responses: [ - { - success: true, - performedQueryCount, - totalQuota, - version, - blockNumber, - warnings: [ErrorMessage.FAILING_OPEN], - }, - { - success: true, - performedQueryCount, - totalQuota, - version, - blockNumber, - warnings: [ErrorMessage.FAILURE_TO_GET_TOTAL_QUOTA], - }, - { - success: true, - performedQueryCount, - totalQuota, - version, - blockNumber, - warnings: [ErrorMessage.FAILURE_TO_GET_DEK], - }, - ], - expectedLogs: [ - { - params: [ - { - parsedResponses: [ - { - signerUrl: url, - values: { - blockNumber, - performedQueryCount, - totalQuota, - version, - warnings: [ErrorMessage.FAILING_OPEN], - }, - }, - { - signerUrl: url, - values: { - blockNumber, - performedQueryCount, - totalQuota, - version, - warnings: [ErrorMessage.FAILURE_TO_GET_TOTAL_QUOTA], - }, - }, - { - signerUrl: url, - values: { - blockNumber, - performedQueryCount, - totalQuota, - version, - warnings: [ErrorMessage.FAILURE_TO_GET_DEK], - }, - }, - ], - }, - WarningMessage.SIGNER_RESPONSE_DISCREPANCIES, - ], - level: 'warn', - }, - { - params: [ - { - signerWarning: ErrorMessage.FAILING_OPEN, - service: url, - }, - WarningMessage.SIGNER_FAILED_OPEN, - ], - level: 'error', - }, - { - params: [ - { - signerWarning: ErrorMessage.FAILURE_TO_GET_TOTAL_QUOTA, - service: url, - }, - WarningMessage.SIGNER_FAILED_OPEN, - ], - level: 'error', - }, - { - params: [ - { - signerWarning: ErrorMessage.FAILURE_TO_GET_DEK, - service: url, - }, - WarningMessage.SIGNER_FAILED_OPEN, - ], - level: 'error', - }, - ], - }, - ] - testCases.forEach((testCase) => { - it(testCase.it, () => { - const session = getSession(testCase.responses) - const logSpys = { - info: { - spy: jest.spyOn(session.logger, 'info'), - callCount: 0, - }, - debug: { - spy: jest.spyOn(session.logger, 'debug'), - callCount: 0, - }, - warn: { - spy: jest.spyOn(session.logger, 'warn'), - callCount: 0, - }, - error: { - spy: jest.spyOn(session.logger, 'error'), - callCount: 0, - }, - } - pnpResponseLogger.logResponseDiscrepancies(session) - pnpResponseLogger.logFailOpenResponses(session) - testCase.expectedLogs.forEach((log) => { - expect(logSpys[log.level].spy).toHaveBeenNthCalledWith( - ++logSpys[log.level].callCount, - ...log.params - ) - }) - Object.values(logSpys).forEach((level) => { - level.spy.mockClear() - level.spy.mockRestore() - }) - }) - }) -}) diff --git a/packages/phone-number-privacy/combiner/test/unit/pnp-threshold-state.test.ts b/packages/phone-number-privacy/combiner/test/unit/pnp-threshold-state.test.ts deleted file mode 100644 index d42a6e6a8e6..00000000000 --- a/packages/phone-number-privacy/combiner/test/unit/pnp-threshold-state.test.ts +++ /dev/null @@ -1,227 +0,0 @@ -import { - KeyVersionInfo, - PnpQuotaRequest, - PnpQuotaResponseSuccess, - rootLogger, - SignMessageRequest, - SignMessageResponseSuccess, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import { getSignerVersion } from '@celo/phone-number-privacy-signer/src/config' -import { Request, Response } from 'express' -import { Session } from '../../src/common/session' -import config from '../../src/config' -import { PnpThresholdStateService } from '../../src/pnp/services/threshold-state' - -describe('pnp threshold state', () => { - // TODO add tests with failed signer responses, depending on - // result of https://github.com/celo-org/celo-monorepo/issues/9826 - - const keyVersionInfo: KeyVersionInfo = { - keyVersion: 1, - threshold: 3, - polynomial: 'mock polynomial', - pubKey: 'mock pubKey', - } - - const getSession = (quotaData: { totalQuota: number; performedQueryCount: number }[]) => { - const mockRequest = { - body: {}, - } as Request - - // @ts-ignore: missing some properties - const mockResponse = { - locals: { - logger: rootLogger, - }, - } as Response - const session = new Session( - mockRequest, - mockResponse, - keyVersionInfo - ) - quotaData.forEach((q) => { - const res: PnpQuotaResponseSuccess | SignMessageResponseSuccess = { - success: true, - version: expectedVersion, - ...q, - blockNumber: testBlockNumber, - } - session.responses.push({ url: 'random url', res, status: 200 }) - }) - return session - } - - const pnpConfig = config.phoneNumberPrivacy - pnpConfig.keys.currentVersion = keyVersionInfo.keyVersion - pnpConfig.keys.versions = JSON.stringify([keyVersionInfo]) - const pnpThresholdStateService = new PnpThresholdStateService() - - const expectedVersion = getSignerVersion() - const testBlockNumber = 1000000 - const totalQuota = 10 - const performedQueryCount = 5 - - const varyingQueryCount = [ - { - signerRes: [ - { performedQueryCount: 0, totalQuota }, - { performedQueryCount: 0, totalQuota }, - { performedQueryCount: 0, totalQuota }, - { performedQueryCount: 0, totalQuota }, - ], - expectedQueryCount: 0, - }, - { - signerRes: [ - { performedQueryCount: 0, totalQuota }, - { performedQueryCount: 0, totalQuota }, - { performedQueryCount: 0, totalQuota }, - { performedQueryCount: 1, totalQuota }, - ], - expectedQueryCount: 0, - }, // does not reach threshold - { - signerRes: [ - { performedQueryCount: 1, totalQuota }, - { performedQueryCount: 1, totalQuota }, - { performedQueryCount: 0, totalQuota }, - { performedQueryCount: 1, totalQuota }, - ], - expectedQueryCount: 1, - }, // threshold reached - { - signerRes: [ - { performedQueryCount: 0, totalQuota }, - { performedQueryCount: 1, totalQuota }, - { performedQueryCount: 1, totalQuota }, - { performedQueryCount: 1, totalQuota }, - ], - expectedQueryCount: 1, - }, // order of signers shouldn't matter - { - signerRes: [ - { performedQueryCount: 1, totalQuota }, - { performedQueryCount: 4, totalQuota }, - { performedQueryCount: 9, totalQuota }, - { performedQueryCount: 11, totalQuota }, - ], - expectedQueryCount: 9, - }, - ] - varyingQueryCount.forEach(({ signerRes, expectedQueryCount }) => { - it(`should return ${expectedQueryCount} performedQueryCount given signer responses of ${signerRes}`, () => { - const session = getSession(signerRes) - const thresholdResult = pnpThresholdStateService.findCombinerQuotaState(session) - expect(thresholdResult).toStrictEqual({ - performedQueryCount: expectedQueryCount, - totalQuota, - blockNumber: testBlockNumber, - }) - }) - }) - - const varyingTotalQuota = [ - { - signerRes: [ - { performedQueryCount, totalQuota }, - { performedQueryCount, totalQuota }, - { performedQueryCount, totalQuota }, - { performedQueryCount, totalQuota }, - ], - expectedTotalQuota: totalQuota, - warning: false, - }, - { - signerRes: [ - { performedQueryCount, totalQuota: 7 }, - { performedQueryCount, totalQuota: 8 }, - { performedQueryCount, totalQuota: 9 }, - { performedQueryCount, totalQuota: 10 }, - ], - expectedTotalQuota: 8, - warning: true, - }, - { - signerRes: [ - { performedQueryCount, totalQuota: 8 }, - { performedQueryCount, totalQuota: 9 }, - { performedQueryCount, totalQuota: 10 }, - { performedQueryCount, totalQuota: 7 }, - ], - expectedTotalQuota: 8, - warning: true, - }, - ] - varyingTotalQuota.forEach(({ signerRes, expectedTotalQuota, warning }) => { - it(`should return ${expectedTotalQuota} totalQuota given signer responses of ${signerRes}`, () => { - const session = getSession(signerRes) - const thresholdResult = pnpThresholdStateService.findCombinerQuotaState(session) - expect(thresholdResult).toStrictEqual({ - performedQueryCount, - totalQuota: expectedTotalQuota, - blockNumber: testBlockNumber, - }) - if (warning) { - expect(session.warnings).toContain( - WarningMessage.INCONSISTENT_SIGNER_QUOTA_MEASUREMENTS + - ', using threshold signer as best guess' - ) - } - }) - }) - - const varyingQuotaAndQuery = [ - { - signerRes: [ - { performedQueryCount: 1, totalQuota: 10 }, - { performedQueryCount: 2, totalQuota: 9 }, - { performedQueryCount: 3, totalQuota: 8 }, - { performedQueryCount: 4, totalQuota: 7 }, - ], - expectedQueryCount: 3, - expectedTotalQuota: 8, - warning: true, - }, - { - signerRes: [ - { performedQueryCount: 1, totalQuota: 7 }, - { performedQueryCount: 2, totalQuota: 8 }, - { performedQueryCount: 5, totalQuota: 9 }, - { performedQueryCount: 6, totalQuota: 10 }, - ], - expectedQueryCount: 5, - expectedTotalQuota: 9, - warning: true, - }, - ] - varyingQuotaAndQuery.forEach(({ signerRes, expectedQueryCount, expectedTotalQuota, warning }) => { - it(`should return ${expectedTotalQuota} totalQuota and ${expectedQueryCount} performedQueryCount given signer responses of ${signerRes}`, () => { - const session = getSession(signerRes) - const thresholdResult = pnpThresholdStateService.findCombinerQuotaState(session) - expect(thresholdResult).toStrictEqual({ - performedQueryCount: expectedQueryCount, - totalQuota: expectedTotalQuota, - blockNumber: testBlockNumber, - }) - if (warning) { - expect(session.warnings).toContain( - WarningMessage.INCONSISTENT_SIGNER_QUOTA_MEASUREMENTS + - ', using threshold signer as best guess' - ) - } - }) - }) - - it('should throw an error if the total quota varies too much between signers', () => { - const session = getSession([ - { performedQueryCount, totalQuota: 1 }, - { performedQueryCount, totalQuota: 9 }, - { performedQueryCount, totalQuota: 15 }, - { performedQueryCount, totalQuota: 14 }, - ]) - expect(() => pnpThresholdStateService.findCombinerQuotaState(session)).toThrow( - WarningMessage.INCONSISTENT_SIGNER_QUOTA_MEASUREMENTS - ) - }) -}) diff --git a/packages/phone-number-privacy/combiner/test/utils.ts b/packages/phone-number-privacy/combiner/test/utils.ts deleted file mode 100644 index 6dbc470c7d1..00000000000 --- a/packages/phone-number-privacy/combiner/test/utils.ts +++ /dev/null @@ -1,6 +0,0 @@ -import threshold_bls from 'blind-threshold-bls' - -export function getBlindedPhoneNumber(phoneNumber: string, blindingFactor: Buffer): string { - const blindedPhoneNumber = threshold_bls.blind(Buffer.from(phoneNumber), blindingFactor).message - return Buffer.from(blindedPhoneNumber).toString('base64') -} diff --git a/packages/phone-number-privacy/combiner/tsconfig.json b/packages/phone-number-privacy/combiner/tsconfig.json deleted file mode 100644 index c9ee1249ba9..00000000000 --- a/packages/phone-number-privacy/combiner/tsconfig.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "compilerOptions": { - "plugins": [ - { - "name": "typescript-tslint-plugin" - } - ], - "lib": ["es2017"], - "module": "commonjs", - "strict": true, - "allowJs": false, - "allowSyntheticDefaultImports": true, - "esModuleInterop": true, - "sourceMap": true, - "declaration": true, - "target": "es2017", - "rootDir": "src", - "outDir": "./dist", - "skipLibCheck": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "preserveConstEnums": true, - "composite": true - }, - "include": ["src"], - "compileOnSave": true -} diff --git a/packages/phone-number-privacy/combiner/tslint.json b/packages/phone-number-privacy/combiner/tslint.json deleted file mode 100644 index 5fc86ecb716..00000000000 --- a/packages/phone-number-privacy/combiner/tslint.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": ["@celo/typescript/tslint.json"], - "rules": { - "no-global-arrow-functions": false, - "no-console": true - } -} diff --git a/packages/phone-number-privacy/common/.gitignore b/packages/phone-number-privacy/common/.gitignore deleted file mode 100644 index 3709fe818de..00000000000 --- a/packages/phone-number-privacy/common/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -lib/ - -package-lock.json \ No newline at end of file diff --git a/packages/phone-number-privacy/common/README.md b/packages/phone-number-privacy/common/README.md deleted file mode 100644 index 369c3daaa9c..00000000000 --- a/packages/phone-number-privacy/common/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# ODIS Common Package - -This package contains common code used across ODIS. It is depended on by the Combiner, Signer and Monitor services as well as the @celo/identity and @celo/encrypted-backup SDKS. In most cases where code will be re-used by multiple parts of ODIS, it probably belongs here. - -## Notable Contents - -- The request and response type schemas for all ODIS APIs. -- Error and Warning types used for monitoring and alerting in both the Combiner and Signer. -- The PEAR Sequential Delay rate limiting algorithm. - -## Release Process - -When updating the ODIS common package, it is important to remember that all changes must be published before they can be used in production ODIS services or SDKS. If your changes are needed in the SDKS, then you will need to also publish all the Celo SDKs. The instructions below detail this entire SDK release process, but if your changes are only needed in ODIS services you only need to do step 7 (remember to run `yarn && yarn build` before publishing, and consider reading the rest of the steps anyway for context) - -These instructions assume the following scenario for readability: - -- The latest released sdk version is `3.1.0` -- The SDK versions in the monorepo are all set to `3.1.1-dev` -- You are releasing version `3.2.0` of the SDKs -- The latest released ODIS common package version is `2.0.2` -- You are releasing version `2.0.3` of the ODIS common package - -1. Checkout a new branch for the SDK release. Name it something like `/release3.2.0` -2. Note that you should release version `3.2.0-beta.1` and `2.0.3-beta.1` and test that everything is working correctly before publishing them as `latest`. If everything is not working correctly, try again with `-beta.2` -3. Search and replace all instances of the current sdk version in the monorepo with the new sdk version you are releasing (check the search and replace changes do what you intend them to before hitting replace!) - - i.e. search and replace `3.1.1-dev` with `3.2.0-beta.1` (note that we’ve removed the `-dev`) -4. Same idea as above -- ensure the version of the `@celo/phone-number-privacy-common` package is set to the version you are trying to release (i.e. `2.0.3-beta.1`) and that all other packages are importing this version. -5. From the monorepo root directory, run `yarn reset && yarn && yarn build` (expect this to take at least 10 mins) -6. Commit your changes with the message `3.2.0-beta.1` -7. Publish the ODIS common package by navigating to the `phone-number-privacy/common` directory and running `npm publish —-tag beta --version 3.2.0-beta.1 —-otp ` - - When publishing as `latest`, omit the `--tag beta` -8. Publish the sdks by running `npm run deploy-sdks` from the monorepo root directory - - You will be prompted to enter a version number that you wish to publish. i.e. `3.2.0-beta.1` - - You will be repeatedly asked to enter your OTP, which will be automatically supplied if you hit ‘enter’ (you do not have to paste it to the command line each time) - - When your OTP expires, you will see an error and will have to re-enter the new one - - Note the `deploy-sdks` script will automatically append `-dev` to all the sdk versions after they're published. You may need to search and replace to undue this if you were publishing a beta release. -9. Depending on what you're releasing, you may want to test that the newly published SDKs work as intended. This may be as simple as checking that CI runs successfully on your `3.2.0-beta.1` commit. -10. Once you are confident in the beta release, repeat steps 3 through 9 with versions `3.2.0` and `2.0.3`. The SDKs will be published with the `latest` tag. -11. The `deploy-sdks` script will automatically append `-dev` to all the sdk versions after they're published. For `latest` releases, it will also increment to the next patch version. Please ensure this happened correctly and commit the result with the message `3.2.1-dev` -12. Get your PR for the release branch reviewed and merged - - If CI fails with output like below, it means that some packages outside of the SDK did not get incremented to `3.2.1-dev`. Please go through and make sure these are all incremented correctly and CI should pass. - - ``` - ./sdk/utils/src/address.ts(1,46): error TS2307: Cannot find module '@celo/base/lib/address' or its corresponding type declarations. - ../sdk/utils/src/address.ts(27,8): error TS2307: Cannot find module '@celo/base/lib/address' or its corresponding type declarations. - ../sdk/utils/src/async.ts(10,8): error TS2307: Cannot find module '@celo/base/lib/async' or its corresponding type declarations - ``` - -13. Don’t forget to tag the PR commit as a release in GitHub and add Release Notes diff --git a/packages/phone-number-privacy/common/index.d.ts b/packages/phone-number-privacy/common/index.d.ts deleted file mode 100644 index 102dc17cf17..00000000000 --- a/packages/phone-number-privacy/common/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare module 'bunyan-debug-stream' diff --git a/packages/phone-number-privacy/common/jest.config.js b/packages/phone-number-privacy/common/jest.config.js deleted file mode 100644 index ac1faeacbb9..00000000000 --- a/packages/phone-number-privacy/common/jest.config.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - preset: 'ts-jest', - coverageReporters: [['lcov', { projectRoot: '../../../' }], 'text'], - collectCoverageFrom: ['./src/**'], - coverageThreshold: { - global: { - lines: 80, - }, - }, -} diff --git a/packages/phone-number-privacy/common/package.json b/packages/phone-number-privacy/common/package.json deleted file mode 100644 index 7b465991f74..00000000000 --- a/packages/phone-number-privacy/common/package.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "@celo/phone-number-privacy-common", - "version": "3.0.0-dev", - "description": "Common library for the combiner and signer libraries", - "author": "Celo", - "license": "Apache-2.0", - "main": "./lib/index.js", - "types": "./lib/index.d.ts", - "scripts": { - "prepublishOnly": "yarn build", - "build": "tsc -b .", - "clean": "tsc -b . --clean", - "test": "jest --testPathIgnorePatterns test/end-to-end", - "test:coverage": "yarn test --coverage", - "lint": "tslint -c tslint.json --project ." - }, - "files": [ - "lib/**/*" - ], - "dependencies": { - "@celo/base": "^4.1.1-dev", - "@celo/contractkit": "^4.1.1-dev", - "@celo/utils": "^4.1.1-dev", - "@celo/phone-utils": "^4.1.1-dev", - "@types/bunyan": "1.8.8", - "bignumber.js": "^9.0.0", - "bunyan": "1.8.12", - "bunyan-debug-stream": "2.0.0", - "bunyan-gke-stackdriver": "0.1.2", - "dotenv": "^8.2.0", - "elliptic": "^6.5.4", - "io-ts": "2.0.1", - "is-base64": "^1.1.0" - }, - "devDependencies": { - "@celo/poprf": "^0.1.9", - "@celo/wallet-local": "^4.1.1-dev", - "@types/elliptic": "^6.4.12", - "@types/express": "^4.17.6", - "@types/is-base64": "^1.1.0", - "@types/node-fetch": "^2.5.7", - "web3": "1.10.0" - }, - "engines": { - "node": ">=10" - } -} diff --git a/packages/phone-number-privacy/common/src/domains/constants.ts b/packages/phone-number-privacy/common/src/domains/constants.ts deleted file mode 100644 index 78179ce6f0a..00000000000 --- a/packages/phone-number-privacy/common/src/domains/constants.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Enum of identifiers (i.e. names) for all standardized domains - * - * @privateRemarks DomainIdentifiers is defined in a separate file to avoid issues with circular - * dependencies causing undefined errors. - */ -export enum DomainIdentifiers { - SequentialDelay = 'ODIS Sequential Delay Domain', -} diff --git a/packages/phone-number-privacy/common/src/domains/domains.ts b/packages/phone-number-privacy/common/src/domains/domains.ts deleted file mode 100644 index 95fd41d3a22..00000000000 --- a/packages/phone-number-privacy/common/src/domains/domains.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { - EIP712TypedData, - EIP712TypesWithPrimary, - generateTypedDataHash, -} from '@celo/utils/lib/sign-typed-data-utils' -import * as t from 'io-ts' -import { - isSequentialDelayDomain, - SequentialDelayDomain, - sequentialDelayDomainEIP712Types, - SequentialDelayDomainOptions, - sequentialDelayDomainOptionsEIP712Types, - SequentialDelayDomainSchema, - SequentialDelayDomainState, -} from './sequential-delay' - -/** - * Union type of domains which are currently implmented and standardized for use with ODIS. - * Domains should be added to the union type as they are implemented. - * - * @remarks Additional domain types should be added to this type union as they are standardized. - * - * All new Domain types must contain the fields { name: string, version: string }. Domain types - * may have additional fields, which must be assignable to EIP712Value. See CIP-40 for more details: - * - * https://github.com/celo-org/celo-proposals/blob/master/CIPs/cip-0040.md#extension-for-domain-restricted-requests - */ -export type Domain = SequentialDelayDomain - -/** - * Parameterized union type of standardized domain options. - * - * @remarks If the type parameter is specified to be a concrete Domain subtype, then only its - * associated DomainOptions is selected and assignable to the parameterized type. - * - * Additional domain options types should be added to this type union along with the new domain type - * definition, if the new domain type has associated options. If a domain type has no associated - * options, it's corresponding options type should be an empty struct. - * - * Domain options must be assignable to EIP712Object. - */ -export type DomainOptions = D extends SequentialDelayDomain - ? SequentialDelayDomainOptions - : never - -/** - * Parameterized union type of currently implemented and standarized domain state structs. If the - * type parameter is specified to be a concrete Domain subtype, then only its associated - * Domain state type is selected and assignable to the parameterized type. - */ -export type DomainState = D extends SequentialDelayDomain - ? SequentialDelayDomainState - : never - -/** io-ts schema for encoding and decoding domains of any standardized type */ -export const DomainSchema: t.Type = SequentialDelayDomainSchema - -export function domainEIP712Types(domain: Domain): EIP712TypesWithPrimary { - if (isSequentialDelayDomain(domain)) { - return sequentialDelayDomainEIP712Types - } - - // canary provides a compile-time check that all subtypes of Domain have branches. If a case - // was missed, then an error will report that domain cannot be assigned to type `never`. - const canary = (x: never) => x - canary(domain) - throw new Error('Implementation error. Input of type Domain was not recognized') -} - -export function domainOptionsEIP712Types(domain: Domain): EIP712TypesWithPrimary { - if (isSequentialDelayDomain(domain)) { - return sequentialDelayDomainOptionsEIP712Types - } - - // canary provides a compile-time check that all subtypes of Domain have branches. If a case - // was missed, then an error will report that domain cannot be assigned to type `never`. - const canary = (x: never) => x - canary(domain) - throw new Error('Implementation error. Input of type Domain was not recognized') -} - -/** - * Wraps a domain instance of a standardized type into an EIP-712 typed data structure, including - * the EIP-712 type signature specififed by the mapping from TypeScript types in CIP-40. - * https://github.com/celo-org/celo-proposals/blob/master/CIPs/cip-0040.md#mapping-typescript-to-eip-712-types - */ -export const domainEIP712 = (domain: Domain): EIP712TypedData => ({ - types: { - ...domainEIP712Types(domain).types, - EIP712Domain: [ - { name: 'name', type: 'string' }, - { name: 'version', type: 'string' }, - ], - }, - primaryType: domainEIP712Types(domain).primaryType, - domain: { - name: domain.name, - version: domain.version, - }, - message: domain, -}) - -/** - * Produces the canonical 256-bit EIP-712 typed hash of the given domain. - * - * @remarks Note that this is a simple wrapper to get the EIP-712 hash after encoding it to an - * EIP-712 typed data format. If a signature over the domain is needed, encode to EIP-712 format - * and pass that into a signTypedData function. - */ -export function domainHash(domain: Domain): Buffer { - return generateTypedDataHash(domainEIP712(domain)) -} diff --git a/packages/phone-number-privacy/common/src/domains/index.ts b/packages/phone-number-privacy/common/src/domains/index.ts deleted file mode 100644 index 4befe2657cb..00000000000 --- a/packages/phone-number-privacy/common/src/domains/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './constants' -export * from './domains' -export * from './sequential-delay' diff --git a/packages/phone-number-privacy/common/src/domains/sequential-delay.ts b/packages/phone-number-privacy/common/src/domains/sequential-delay.ts deleted file mode 100644 index 91de2c00644..00000000000 --- a/packages/phone-number-privacy/common/src/domains/sequential-delay.ts +++ /dev/null @@ -1,284 +0,0 @@ -import { - EIP712Optional, - eip712OptionalSchema, - eip712OptionalType, - EIP712TypesWithPrimary, -} from '@celo/utils/lib/sign-typed-data-utils' -import * as t from 'io-ts' -import { DomainIdentifiers } from './constants' -import { Domain } from './domains' - -// Concrete Domain subtypes are only assignable to Domain and EIP712Object when using type instead -// of interface. Otherwise the compiler complains about a missing index signature. -// tslint:disable:interface-over-type-literal - -export type SequentialDelayStage = { - /** - * How many seconds each batch of attempts in this stage is delayed with - * respect to the timer. - */ - delay: number - /** - * Whether the timer should be reset between attempts during this stage. - * Defaults to true. - */ - resetTimer: EIP712Optional - /** - * The number of continuous attempts a user gets before the next delay - * in each repetition of this stage. Defaults to 1. - */ - batchSize: EIP712Optional - /** - * The number of times this stage repeats before continuing to the next stage - * in the RateLimit array. Defaults to 1. - */ - repetitions: EIP712Optional -} - -export type SequentialDelayDomain = { - name: DomainIdentifiers.SequentialDelay - version: '1' - stages: SequentialDelayStage[] - /** - * Optional Celo address against which signed requests must be authenticated. - * In the case of Cloud Backup, this will be derived from a one-time key stored with the ciphertext. - * Encoded as a checksummed address with leading "0x". - */ - address: EIP712Optional - /** - * Optional string to distinguish the output of this domain instance from - * other SequentialDelayDomain instances - */ - salt: EIP712Optional -} - -export type SequentialDelayDomainOptions = { - /** - * EIP-712 signature over the entire request by the address specified in the domain. - * Required if `address` is defined in the domain instance. If `address` is - * not defined in the domain instance, then a signature must not be provided. - * Encoded as a hex string with leading 0x. - */ - signature: EIP712Optional - /** - * Used to prevent replay attacks. Required if a signature is provided. - * Code verifying the signature for rate limiting should check this nonce against a counter of - * applied requests. E.g. Ensure the nonce is 0 on the first request and 2 on the third. - */ - nonce: EIP712Optional -} - -export interface SequentialDelayDomainState { - /** - * Timestamp in seconds since the Unix Epoch to which the next delay should be applied - * to calculate when a new request will be accepted. - */ - timer: number - /** Number of queries that have been accepted for the SequentialDelayDomain instance. */ - counter: number - /** Whether or not the domain has been disabled. If disabled, no more queries will be served. */ - disabled: boolean - /** Server timestamp in seconds since the Unix Epoch. */ - now: number -} - -export const INITIAL_SEQUENTIAL_DELAY_DOMAIN_STATE: SequentialDelayDomainState = { - timer: 0, - counter: 0, - disabled: false, - now: 0, -} - -/** io-ts schema for encoding and decoding SequentialDelayStage structs */ -export const SequentialDelayStageSchema: t.Type = t.strict({ - delay: t.number, - resetTimer: eip712OptionalSchema(t.boolean), - batchSize: eip712OptionalSchema(t.number), - repetitions: eip712OptionalSchema(t.number), -}) - -/** io-ts schema for encoding and decoding SequentialDelayDomain structs */ -export const SequentialDelayDomainSchema: t.Type = t.strict({ - name: t.literal(DomainIdentifiers.SequentialDelay), - version: t.literal('1'), - stages: t.array(SequentialDelayStageSchema), - address: eip712OptionalSchema(t.string), - salt: eip712OptionalSchema(t.string), -}) - -/** io-ts schema for encoding and decoding SequentialDelayDomainOptions structs */ -export const SequentialDelayDomainOptionsSchema: t.Type = t.strict({ - signature: eip712OptionalSchema(t.string), - nonce: eip712OptionalSchema(t.number), -}) - -/** io-ts schema for encoding and decoding SequentialDelayDomainState structs */ -export const SequentialDelayDomainStateSchema: t.Type = t.strict({ - timer: t.number, - counter: t.number, - disabled: t.boolean, - now: t.number, -}) - -export const isSequentialDelayDomain = (domain: Domain): domain is SequentialDelayDomain => - domain.name === DomainIdentifiers.SequentialDelay && domain.version === '1' - -export const sequentialDelayDomainEIP712Types: EIP712TypesWithPrimary = { - types: { - SequentialDelayDomain: [ - { name: 'address', type: 'Optional
' }, - { name: 'name', type: 'string' }, - { name: 'salt', type: 'Optional' }, - { name: 'stages', type: 'SequentialDelayStage[]' }, - { name: 'version', type: 'string' }, - ], - SequentialDelayStage: [ - { name: 'batchSize', type: 'Optional' }, - { name: 'delay', type: 'uint256' }, - { name: 'repetitions', type: 'Optional' }, - { name: 'resetTimer', type: 'Optional' }, - ], - ...eip712OptionalType('address'), - ...eip712OptionalType('string'), - ...eip712OptionalType('uint256'), - ...eip712OptionalType('bool'), - }, - primaryType: 'SequentialDelayDomain', -} - -export const sequentialDelayDomainOptionsEIP712Types: EIP712TypesWithPrimary = { - types: { - SequentialDelayDomainOptions: [ - { name: 'nonce', type: 'Optional' }, - { name: 'signature', type: 'Optional' }, - ], - ...eip712OptionalType('string'), - ...eip712OptionalType('uint256'), - }, - primaryType: 'SequentialDelayDomainOptions', -} - -/** Result values of the sequential delay domain rate limiting function */ -export interface SequentialDelayResultAccepted { - /** Whether or not a request will be accepted at the given time */ - accepted: true - /** State after applying an additional query against the quota */ - state: SequentialDelayDomainState -} - -export interface SequentialDelayResultRejected { - /** Whether or not a request will be accepted at the given time */ - accepted: false - /** State after rejecting the request. Should be unchanged. */ - state: SequentialDelayDomainState - /** - * Earliest time a request will be accepted at the current stage. - * Undefined if a request will never be accepted. - */ - notBefore?: number -} - -export type SequentialDelayResult = SequentialDelayResultAccepted | SequentialDelayResultRejected - -interface IndexedSequentialDelayStage extends SequentialDelayStage { - // The attempt number at which the stage begins - start: number -} - -/** - * Rate limiting predicate for the sequential delay domain - * - * @param domain SequentialDelayDomain instance against which the rate limit is being calculated, - * and which supplied the rate limiting parameters. - * @param attemptTime The Unix timestamp in seconds when the request was received. - * @param state The current state of the domain, including the used quota counter and timer values. - * Defaults to initial state if no state is available (i.e. for first request against the domain). - */ -export const checkSequentialDelayRateLimit = ( - domain: SequentialDelayDomain, - attemptTime: number, - state: SequentialDelayDomainState = INITIAL_SEQUENTIAL_DELAY_DOMAIN_STATE -): SequentialDelayResult => { - // If the domain has been disabled, all queries are to be rejected. - if (state.disabled) { - return { accepted: false, state: { ...state, now: attemptTime } } - } - - const stage = getIndexedStage(domain, state.counter) - - // If the counter is past the last stage (i.e. the domain is permanently out of quota) return early. - if (!stage) { - return { accepted: false, state: { ...state, now: attemptTime } } - } - - const resetTimer = stage.resetTimer.defined ? stage.resetTimer.value : true - const delay = getDelay(stage, state.counter) - const notBefore = state.timer + delay - - if (attemptTime < notBefore) { - return { accepted: false, notBefore, state: { ...state, now: attemptTime } } - } - - // Request is accepted. Update the state. - return { - accepted: true, - state: { - counter: state.counter + 1, - timer: resetTimer ? attemptTime : notBefore, - disabled: state.disabled, - now: attemptTime, - }, - } -} - -/** - * Finds the current stage of the SequentialDelayDomain rate limit for a given attempt number - * - * @param domain SequentialDelayDomain instance against which the rate limit is being calculated, - * and which supplied the rate limiting parameters. - * @param counter The current attempt number - */ -const getIndexedStage = ( - domain: SequentialDelayDomain, - counter: number -): IndexedSequentialDelayStage | undefined => { - // The attempt index marking the beginning of the current stage - let start = 0 - // The index of the current stage in domain.stages[] - let index = 0 - // The number of attempts in the current stage - let attemptsInStage = 0 - while (start <= counter) { - if (index >= domain.stages.length) { - // Counter is past the last stage (i.e. the domain is permanently out of quota) - return undefined - } - const stage = domain.stages[index] - const repetitions = stage.repetitions.defined ? stage.repetitions.value : 1 - const batchSize = stage.batchSize.defined ? stage.batchSize.value : 1 - attemptsInStage = repetitions * batchSize - start += attemptsInStage - index++ - } - - start -= attemptsInStage - index-- - - return { ...domain.stages[index], start } -} - -/** - * Finds the delay to enforce for an attempt given its counter (attempt number) and - * the corresponding stage in the SequentialDelayDomain rate limit. - * - * @param stage IndexedSequentialDelayStage The given stage of the SequentialDelayDomain rate limit, - * extended to include the index of the first attempt in that stage. - * @param counter The current attempt number - */ -const getDelay = (stage: IndexedSequentialDelayStage, counter: number): number => { - const batchSize = stage.batchSize.defined ? stage.batchSize.value : 1 - if ((counter - stage.start) % batchSize === 0) { - return stage.delay - } - return 0 -} diff --git a/packages/phone-number-privacy/common/src/index.ts b/packages/phone-number-privacy/common/src/index.ts deleted file mode 100644 index 8e216f2d5cf..00000000000 --- a/packages/phone-number-privacy/common/src/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -export * from './domains' -export * from './interfaces' -export { ErrorMessage, WarningMessage } from './interfaces/errors' -export { - PoprfClient, - PoprfCombiner, - PoprfServer, - ThresholdPoprfClient, - ThresholdPoprfServer, -} from './poprf' -export { TestUtils } from './test/index' -export * from './utils/authentication' -export { fetchEnv, fetchEnvOrDefault, toBool, toNum } from './utils/config.utils' -export * from './utils/constants' -export { BlockchainConfig, getContractKit } from './utils/contracts' -export * from './utils/input-validation' -export * from './utils/key-version' -export { genSessionID, loggerMiddleware, rootLogger } from './utils/logger' -export * from './utils/responses.utils' diff --git a/packages/phone-number-privacy/common/src/interfaces/endpoints.ts b/packages/phone-number-privacy/common/src/interfaces/endpoints.ts deleted file mode 100644 index d7c7d998e0d..00000000000 --- a/packages/phone-number-privacy/common/src/interfaces/endpoints.ts +++ /dev/null @@ -1,79 +0,0 @@ -export enum SignerEndpointCommon { - METRICS = '/metrics', - STATUS = '/status', -} - -export enum SignerEndpointPNP { - LEGACY_PNP_SIGN = '/getBlindedMessagePartialSig', - LEGACY_PNP_QUOTA = '/getQuota', - PNP_QUOTA = '/quotaStatus', - PNP_SIGN = '/sign', -} - -export enum CombinerEndpointCommon { - STATUS = '/status', -} - -export enum CombinerEndpointPNP { - LEGACY_PNP_SIGN = '/getBlindedMessageSig', - PNP_QUOTA = '/quotaStatus', - PNP_SIGN = '/sign', - STATUS = '/status', -} - -export enum DomainEndpoint { - DOMAIN_SIGN = '/domain/sign', - DISABLE_DOMAIN = '/domain/disable', - DOMAIN_QUOTA_STATUS = '/domain/quotaStatus', -} - -export type SignerEndpoint = SignerEndpointCommon | SignerEndpointPNP | DomainEndpoint -export const SignerEndpoint = { ...SignerEndpointCommon, ...SignerEndpointPNP, ...DomainEndpoint } - -export type CombinerEndpoint = CombinerEndpointCommon | CombinerEndpointPNP | DomainEndpoint -export const CombinerEndpoint = { - ...CombinerEndpointCommon, - ...CombinerEndpointPNP, - ...DomainEndpoint, -} - -export type Endpoint = SignerEndpoint | CombinerEndpoint -export const Endpoint = { ...SignerEndpoint, ...CombinerEndpoint } - -export function getSignerEndpoint(endpoint: CombinerEndpoint): SignerEndpoint { - switch (endpoint) { - case CombinerEndpoint.DISABLE_DOMAIN: - return SignerEndpoint.DISABLE_DOMAIN - case CombinerEndpoint.DOMAIN_QUOTA_STATUS: - return SignerEndpoint.DOMAIN_QUOTA_STATUS - case CombinerEndpoint.DOMAIN_SIGN: - return SignerEndpoint.DOMAIN_SIGN - case CombinerEndpoint.PNP_QUOTA: - return SignerEndpoint.PNP_QUOTA - case CombinerEndpoint.PNP_SIGN: - return SignerEndpoint.PNP_SIGN - case CombinerEndpoint.LEGACY_PNP_SIGN: - return SignerEndpoint.LEGACY_PNP_SIGN - default: - throw new Error(`No corresponding signer endpoint exists for combiner endpoint ${endpoint}`) - } -} - -export function getCombinerEndpoint(endpoint: SignerEndpoint): CombinerEndpoint { - switch (endpoint) { - case SignerEndpoint.DISABLE_DOMAIN: - return CombinerEndpoint.DISABLE_DOMAIN - case SignerEndpoint.DOMAIN_QUOTA_STATUS: - return CombinerEndpoint.DOMAIN_QUOTA_STATUS - case SignerEndpoint.DOMAIN_SIGN: - return CombinerEndpoint.DOMAIN_SIGN - case SignerEndpoint.PNP_QUOTA: - return CombinerEndpoint.PNP_QUOTA - case SignerEndpoint.PNP_SIGN: - return CombinerEndpoint.PNP_SIGN - case SignerEndpoint.LEGACY_PNP_SIGN: - return CombinerEndpoint.LEGACY_PNP_SIGN - default: - throw new Error(`No corresponding combiner endpoint exists for signer endpoint ${endpoint}`) - } -} diff --git a/packages/phone-number-privacy/common/src/interfaces/errors.ts b/packages/phone-number-privacy/common/src/interfaces/errors.ts deleted file mode 100644 index f1021e209a4..00000000000 --- a/packages/phone-number-privacy/common/src/interfaces/errors.ts +++ /dev/null @@ -1,60 +0,0 @@ -// ERR_# is used for logging so ensure that this is unique when adding a new message -export enum ErrorMessage { - UNKNOWN_ERROR = `CELO_ODIS_ERR_00 Something went wrong`, - DATABASE_UPDATE_FAILURE = `CELO_ODIS_ERR_01 DB_ERR Failed to update database entry`, - DATABASE_INSERT_FAILURE = `CELO_ODIS_ERR_02 DB_ERR Failed to insert database entry`, - DATABASE_GET_FAILURE = `CELO_ODIS_ERR_03 DB_ERR Failed to get database entry`, - KEY_FETCH_ERROR = `CELO_ODIS_ERR_04 INIT_ERR Failed to retrieve key from keystore`, - SIGNATURE_COMPUTATION_FAILURE = `CELO_ODIS_ERR_05 SIG_ERR Failed to compute BLS signature`, - VERIFY_PARITAL_SIGNATURE_ERROR = `CELO_ODIS_ERR_06 SIG_ERR BLS partial signature verification Failure`, - NOT_ENOUGH_PARTIAL_SIGNATURES = `CELO_ODIS_ERR_07 SIG_ERR Not enough partial signatures`, - INCONSISTENT_SIGNER_RESPONSES = `CELO_ODIS_ERR_08 SIG_ERR Inconsistent responses from signers`, - SIGNER_REQUEST_ERROR = `CELO_ODIS_ERR_09 SIG_ERR Failure in signer request`, - TIMEOUT_FROM_SIGNER = `CELO_ODIS_ERR_10 SIG_ERR Timeout from signer`, - FULL_NODE_ERROR = `CELO_ODIS_ERR_11 NODE_ERR Failed to read on-chain state`, - FAILURE_TO_STORE_REQUEST = `CELO_ODIS_ERR_12 DB_ERR Failed to store partial sig request`, - FAILURE_TO_INCREMENT_QUERY_COUNT = `CELO_ODIS_ERR_13 DB_ERR Failed to increment user query count`, - DOMAIN_ALREADY_DISABLED_FAILURE = `CELO_ODIS_ERR_14 DB_ERR Domain is already disabled`, - UNSUPPORTED_DOMAIN = `CELO_ODIS_ERR_15 DOMAIN Domain type is not supported`, - SIGNER_DISABLE_DOMAIN_FAILURE = `CELO_ODIS_ERR_16 DOMAIN Failed to disable domain on a signer`, - THRESHOLD_DISABLE_DOMAIN_FAILURE = `CELO_ODIS_ERR_17 DOMAIN Failed to disable domain on a threshold of signers`, - SIGNER_DOMAIN_QUOTA_STATUS_FAILURE = `CELO_ODIS_ERR_18 DOMAIN Failed to get domain status from signer`, - THRESHOLD_DOMAIN_QUOTA_STATUS_FAILURE = `CELO_ODIS_ERR_19 DOMAIN Failed to get domain quota status from a threshold of signers`, - INVALID_KEY_VERSION_RESPONSE = `CELO_ODIS_ERR_20 SIG_ERR Signer response key version header is invalid`, - INVALID_SIGNER_RESPONSE = `CELO_ODIS_ERR_21 SIG_ERR Signer response body is invalid`, - SIGNER_RESPONSE_FAILED_WITH_OK_STATUS = `CELO_ODIS_ERR_22 SIG_ERR Signer response failed with 200 status`, - THRESHOLD_PNP_QUOTA_STATUS_FAILURE = `CELO_ODIS_ERR_23 SIG_ERR Failed to get PNP quota status from a threshold of signers`, - FAILURE_TO_GET_PERFORMED_QUERY_COUNT = `CELO_ODIS_ERR_24 DB_ERR Failed to read performedQueryCount from signer db`, - FAILURE_TO_GET_TOTAL_QUOTA = `CELO_ODIS_ERR_25 NODE_ERR Failed to read on-chain state to calculate total quota`, - FAILURE_TO_GET_BLOCK_NUMBER = `CELO_ODIS_ERR_26 NODE_ERR Failed to read block number from full node`, - FAILURE_TO_GET_DEK = `CELO_ODIS_ERR_27 NODE_ERR Failed to read user's DEK from full-node`, - FAILING_OPEN = `CELO_ODIS_ERR_28 NODE_ERR Failing open on full-node error`, - FAILING_CLOSED = `CELO_ODIS_ERR_29 NODE_ERR Failing closed on full-node error`, - CAUGHT_ERROR_IN_ENDPOINT_HANDLER = `CELO_ODIS_ERR_30 Caught error in outer endpoint handler`, - ERROR_AFTER_RESPONSE_SENT = `CELO_ODIS_ERR_31 Error in endpoint thrown after response was already sent`, - SIGNATURE_AGGREGATION_FAILURE = 'CELO_ODIS_ERR_32 SIG_ERR Failed to blind aggregate signature shares', -} - -export enum WarningMessage { - INVALID_INPUT = `CELO_ODIS_WARN_01 BAD_INPUT Invalid input parameters`, - UNAUTHENTICATED_USER = `CELO_ODIS_WARN_02 BAD_INPUT Missing or invalid authentication`, - EXCEEDED_QUOTA = `CELO_ODIS_WARN_03 QUOTA Requester exceeded service query quota`, - DUPLICATE_REQUEST_TO_GET_PARTIAL_SIG = `CELO_ODIS_WARN_04 BAD_INPUT Attempt to replay partial signature request`, - INCONSISTENT_SIGNER_BLOCK_NUMBERS = `CELO_ODIS_WARN_05 SIGNER Discrepancy found in signers latest block number that exceeds threshold`, - INCONSISTENT_SIGNER_QUOTA_MEASUREMENTS = `CELO_ODIS_WARN_06 SIGNER Discrepancy found in signers quota measurements`, - MISSING_SESSION_ID = `CELO_ODIS_WARN_07 BAD_INPUT Client did not provide sessionID in request`, - CANCELLED_REQUEST_TO_SIGNER = `CELO_ODIS_WARN_08 SIGNER Cancelled request to signer`, - INVALID_USER_PHONE_NUMBER_SIGNATURE = `CELO_ODIS_WARN_09 BAD_INPUT User phone number signature is invalid`, - UNKNOWN_DOMAIN = `CELO_ODIS_WARN_10 BAD_INPUT Provided domain name and version is not recognized`, - DISABLED_DOMAIN = `CELO_ODIS_WARN_11 BAD_INPUT Provided domain is disabled`, - INVALID_KEY_VERSION_REQUEST = `CELO_ODIS_WARN_12 BAD_INPUT Request key version header is invalid`, - API_UNAVAILABLE = `CELO_ODIS_WARN_13 BAD_INPUT API is unavailable`, - INCONSISTENT_SIGNER_DOMAIN_DISABLED_STATES = `CELO_ODIS_WARN_14 SIGNER Discrepency found in signer domain disabled states`, - INVALID_AUTH_SIGNATURE = `CELO_ODIS_WARN_15 BAD_INPUT Authorization signature was incorrectly generated. Request will be rejected in a future version.`, - INVALID_NONCE = `CELO_ODIS_WARN_16 BAD_INPUT SequentialDelayDomain nonce check failed on Signer request`, - SIGNER_RESPONSE_DISCREPANCIES = `CELO_ODIS_WARN_17 SIGNER Discrepancies detected in signer responses`, - INCONSISTENT_SIGNER_QUERY_MEASUREMENTS = `CELO_ODIS_WARN_18 SIGNER Discrepancy found in signers performed query count measurements`, - SIGNER_FAILED_OPEN = `CELO_ODIS_WARN_19 SIGNER Signer failed open on request`, -} - -export type ErrorType = ErrorMessage | WarningMessage diff --git a/packages/phone-number-privacy/common/src/interfaces/index.ts b/packages/phone-number-privacy/common/src/interfaces/index.ts deleted file mode 100644 index 4a68aa366a9..00000000000 --- a/packages/phone-number-privacy/common/src/interfaces/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './endpoints' -export * from './errors' -export * from './requests' -export * from './responses' diff --git a/packages/phone-number-privacy/common/src/interfaces/requests.ts b/packages/phone-number-privacy/common/src/interfaces/requests.ts deleted file mode 100644 index 164a4e97ec6..00000000000 --- a/packages/phone-number-privacy/common/src/interfaces/requests.ts +++ /dev/null @@ -1,540 +0,0 @@ -import { - EIP712Optional, - eip712OptionalSchema, - eip712OptionalType, - EIP712TypedData, - noString, -} from '@celo/utils/lib/sign-typed-data-utils' -import { verifyEIP712TypedDataSigner } from '@celo/utils/lib/signatureUtils' -import { chain, isRight } from 'fp-ts/lib/Either' -import { pipe } from 'fp-ts/lib/pipeable' -import * as t from 'io-ts' -import { KEY_VERSION_HEADER } from '..' -import { - Domain, - domainEIP712Types, - DomainOptions, - domainOptionsEIP712Types, - isSequentialDelayDomain, - SequentialDelayDomain, - SequentialDelayDomainOptionsSchema, -} from '../domains' - -// Domain request types are only assignable to EIP712Object when using type instead -// of interface. Otherwise the compiler complains about a missing index signature. -// tslint:disable:interface-over-type-literal - -export enum AuthenticationMethod { - WALLET_KEY = 'wallet_key', - ENCRYPTION_KEY = 'encryption_key', -} - -export interface SignMessageRequest { - /** Celo account address. Query is charged against this account's quota. */ - account: string - /** Query message. A blinded elliptic curve point encoded in base64. */ - blindedQueryPhoneNumber: string - /** Authentication method to use for verifying the signature in the Authorization header */ - authenticationMethod?: string - /** Client-specified session ID for the request. */ - sessionID?: string - /** Client-specified version string */ - version?: string -} - -/** previously known as GetBlindedMessageSigRequest */ -export interface LegacySignMessageRequest extends SignMessageRequest { - /** Optional on-chain identifier. Unlocks additional quota if the account is verified as an owner of the identifier. */ - hashedPhoneNumber?: string -} - -export const SignMessageRequestSchema: t.Type = t.intersection([ - t.type({ - account: t.string, - blindedQueryPhoneNumber: t.string, - }), - t.partial({ - authenticationMethod: t.union([t.string, t.undefined]), - sessionID: t.union([t.string, t.undefined]), - version: t.union([t.string, t.undefined]), - }), -]) - -export const LegacySignMessageRequestSchema: t.Type = t.intersection([ - SignMessageRequestSchema, - t.partial({ - hashedPhoneNumber: t.union([t.string, t.undefined]), - }), -]) - -export interface PnpQuotaRequest { - account: string - /** Authentication method to use for verifying the signature in the Authorization header */ - authenticationMethod?: string - /** Client-specified session ID for the request. */ - sessionID?: string - /** Client-specified version string */ - version?: string -} -export interface LegacyPnpQuotaRequest extends PnpQuotaRequest { - /** User's ODIS generated on-chain identifier */ - hashedPhoneNumber?: string -} - -// Backwards compatibility -export declare type GetQuotaRequest = LegacyPnpQuotaRequest - -export const PnpQuotaRequestSchema: t.Type = t.intersection([ - t.type({ - account: t.string, - }), - t.partial({ - authenticationMethod: t.union([t.string, t.undefined]), - sessionID: t.union([t.string, t.undefined]), - version: t.union([t.string, t.undefined]), - }), -]) - -export const LegacyPnpQuotaRequestSchema: t.Type = t.intersection([ - PnpQuotaRequestSchema, - t.partial({ - hashedPhoneNumber: t.union([t.string, t.undefined]), - }), -]) - -export type PhoneNumberPrivacyRequest = - | SignMessageRequest - | LegacySignMessageRequest - | PnpQuotaRequest - | LegacyPnpQuotaRequest - -export enum DomainRequestTypeTag { - SIGN = 'DomainRestrictedSignatureRequest', - QUOTA = 'DomainQuotaStatusRequest', - DISABLE = 'DisableDomainRequest', -} - -/** - * Domain restricted signature request to get a pOPRF evaluation on the given message in a given - * domain, as specified by CIP-40. - * - * @remarks Concrete request types are created by specifying the type parameter for Domain. If a - * domain has no options, an empty struct should be used. - */ -export type DomainRestrictedSignatureRequest = { - /** Request type tag to ensure this type can be distinguished from other request objects. */ - type: DomainRequestTypeTag.SIGN - /** Domain specification. Selects the PRF domain and rate limiting rules. */ - domain: D - /** - * Domain-specific options. - * Used for inputs relevant to the domain, but not part of the domain string. - * Example: { "authorization": } for an account-restricted domain. - */ - options: DomainOptions - /** Query message. A blinded elliptic curve point encoded in base64. */ - blindedMessage: string - /** Client-specified session ID. */ - sessionID: EIP712Optional -} - -/** - * Request to get the quota status of the given domain. ODIS will respond with the current state - * relevant to calculating quota under the associated rate limiting rules. - * - * Options may be provided for authentication in case the quota state is non-public information. - * E.g. Quota state may reveal whether or not a user has attempted to recover a given account. - * - * @remarks Concrete request types are created by specifying the type parameter for Domain. If a - * domain has no options, an empty struct should be used. - */ -export type DomainQuotaStatusRequest = { - /** Request type tag to ensure this type can be distinguished from other request objects. */ - type: DomainRequestTypeTag.QUOTA - /** Domain specification. Selects the PRF domain and rate limiting rules. */ - domain: D - /** Domain-specific options. */ - options: DomainOptions - /** Client-specified session ID. */ - sessionID: EIP712Optional -} - -/** - * Request to disable a domain such that not further requests for signatures in the given domain - * will be served. Available for domains which need to option to prevent further requests for - * security. - * - * Options may be provided for authentication to prevent unintended parties from disabling a domain. - * - * @remarks Concrete request types are created by specifying the type parameter for Domain. If a - * domain has no options, an empty struct should be used. - */ -export type DisableDomainRequest = { - /** Request type tag to ensure this type can be distinguished from other request objects. */ - type: DomainRequestTypeTag.DISABLE - /** Domain specification. Selects the PRF domain and rate limiting rules. */ - domain: D - /** Domain-specific options. */ - options: DomainOptions - /** Client-specified session ID. */ - sessionID: EIP712Optional -} - -/** Union type of Domain API requests */ -export type DomainRequest = - | DomainRestrictedSignatureRequest - | DomainQuotaStatusRequest - | DisableDomainRequest - -export type OdisRequest = DomainRequest | PhoneNumberPrivacyRequest - -// NOTE: Next three functions are a bit repetitive. An attempt was made to combine them, but the -// type signature got quite complicated. Feel free to attempt it if you are motivated. - -/** Parameterized schema for checking unknown input against DomainRestrictedSignatureRequest */ -export function domainRestrictedSignatureRequestSchema( - domain: t.Type -): t.Type> { - // The schema defined here does most of the work, but does not guarantee consistency between the - // domain and options fields. We wrap the schema below to add a consistency check. - const schema = t.strict({ - domain, - type: t.literal(DomainRequestTypeTag.SIGN), - options: t.unknown, - blindedMessage: t.string, - sessionID: eip712OptionalSchema(t.string), - }) - - const validation = ( - unk: unknown, - ctx: t.Context - ): t.Validation> => - pipe( - schema.validate(unk, ctx), - chain((value: t.TypeOf) => { - if (isSequentialDelayDomain(value.domain)) { - const either = SequentialDelayDomainOptionsSchema.validate(value.options, ctx) - if (isRight(either)) { - return t.success(value as DomainRestrictedSignatureRequest) - } - - return t.failure(unk, ctx, 'options type does not match domain type') - } - - // canary provides a compile-time check that all subtypes of Domain have branches. If a case - // was missed, then an error will report that domain cannot be assigned to type `never`. - const canary = (x: never) => x - canary(value.domain) - throw new Error('Implementation error: validated domain is not of any known type') - }) - ) - - return new t.Type, DomainRestrictedSignatureRequest>( - `DomainRestrictedSignatureRequestSchema<${domain.name}>`, - (unk: unknown): unk is DomainRestrictedSignatureRequest => isRight(validation(unk, [])), - validation, - (req: DomainRestrictedSignatureRequest) => req - ) -} - -/** Parameterized schema for checking unknown input against DomainQuotaStatusRequest */ -export function domainQuotaStatusRequestSchema( - domain: t.Type -): t.Type> { - // The schema defined here does most of the work, but does not guarantee consistency between the - // domain and options fields. We wrap the schema below to add a consistency check. - const schema = t.strict({ - domain, - type: t.literal(DomainRequestTypeTag.QUOTA), - options: t.unknown, - sessionID: eip712OptionalSchema(t.string), - }) - - const validation = (unk: unknown, ctx: t.Context): t.Validation> => - pipe( - schema.validate(unk, ctx), - chain((value: t.TypeOf) => { - if (isSequentialDelayDomain(value.domain)) { - const either = SequentialDelayDomainOptionsSchema.validate(value.options, ctx) - if (isRight(either)) { - return t.success(value as DomainQuotaStatusRequest) - } - - return t.failure(unk, ctx, 'options type does not match domain type') - } - - // canary provides a compile-time check that all subtypes of Domain have branches. If a case - // was missed, then an error will report that domain cannot be assigned to type `never`. - const canary = (x: never) => x - canary(value.domain) - throw new Error('Implementation error: validated domain is not of any known type') - }) - ) - - return new t.Type, DomainQuotaStatusRequest>( - `DomainQuotaStatusRequestSchema<${domain.name}>`, - (unk: unknown): unk is DomainQuotaStatusRequest => isRight(validation(unk, [])), - validation, - (req: DomainQuotaStatusRequest) => req - ) -} - -/** Parameterized schema for checking unknown input against DisableDomainRequest */ -export function disableDomainRequestSchema( - domain: t.Type -): t.Type> { - // The schema defined here does most of the work, but does not guarantee consistency between the - // domain and options fields. We wrap the schema below to add a consistency check. - const schema = t.strict({ - domain, - type: t.literal(DomainRequestTypeTag.DISABLE), - options: t.unknown, - sessionID: eip712OptionalSchema(t.string), - }) - - const validation = (unk: unknown, ctx: t.Context): t.Validation> => - pipe( - schema.validate(unk, ctx), - chain((value: t.TypeOf) => { - if (isSequentialDelayDomain(value.domain)) { - const either = SequentialDelayDomainOptionsSchema.validate(value.options, ctx) - if (isRight(either)) { - return t.success(value as DisableDomainRequest) - } - - return t.failure(unk, ctx, 'options type does not match domain type') - } - - // canary provides a compile-time check that all subtypes of Domain have branches. If a case - // was missed, then an error will report that domain cannot be assigned to type `never`. - const canary = (x: never) => x - canary(value.domain) - throw new Error('Implementation error: validated domain is not of any known type') - }) - ) - - return new t.Type, DisableDomainRequest>( - `DisableDomainRequestSchema<${domain.name}>`, - (unk: unknown): unk is DisableDomainRequest => isRight(validation(unk, [])), - validation, - (req: DisableDomainRequest) => req - ) -} - -/** Wraps the signature request as an EIP-712 typed data structure for hashing and signing */ -export function domainRestrictedSignatureRequestEIP712( - request: DomainRestrictedSignatureRequest -): EIP712TypedData { - const domainTypes = domainEIP712Types(request.domain) - const optionsTypes = domainOptionsEIP712Types(request.domain) - return { - types: { - DomainRestrictedSignatureRequest: [ - { name: 'type', type: 'string' }, - { name: 'blindedMessage', type: 'string' }, - { name: 'domain', type: domainTypes.primaryType }, - { name: 'options', type: optionsTypes.primaryType }, - { name: 'sessionID', type: 'Optional' }, - ], - ...domainTypes.types, - ...optionsTypes.types, - ...eip712OptionalType('string'), - EIP712Domain: [ - { name: 'name', type: 'string' }, - { name: 'version', type: 'string' }, - ], - }, - primaryType: 'DomainRestrictedSignatureRequest', - domain: { - name: 'ODIS Domain Restricted Signature Request', - version: '1', - }, - message: request, - } -} - -/** Wraps the domain quota request as an EIP-712 typed data structure for hashing and signing */ -export function domainQuotaStatusRequestEIP712( - request: DomainQuotaStatusRequest -): EIP712TypedData { - const domainTypes = domainEIP712Types(request.domain) - const optionsTypes = domainOptionsEIP712Types(request.domain) - return { - types: { - DomainQuotaStatusRequest: [ - { name: 'type', type: 'string' }, - { name: 'domain', type: domainTypes.primaryType }, - { name: 'options', type: optionsTypes.primaryType }, - { name: 'sessionID', type: 'Optional' }, - ], - ...domainTypes.types, - ...optionsTypes.types, - ...eip712OptionalType('string'), - EIP712Domain: [ - { name: 'name', type: 'string' }, - { name: 'version', type: 'string' }, - ], - }, - primaryType: 'DomainQuotaStatusRequest', - domain: { - name: 'ODIS Domain Quota Status', - version: '1', - }, - message: request, - } -} - -/** Wraps the disable domain request as an EIP-712 typed data structure for hashing and signing */ -export function disableDomainRequestEIP712( - request: DisableDomainRequest -): EIP712TypedData { - const domainTypes = domainEIP712Types(request.domain) - const optionsTypes = domainOptionsEIP712Types(request.domain) - return { - types: { - DisableDomainRequest: [ - { name: 'type', type: 'string' }, - { name: 'domain', type: domainTypes.primaryType }, - { name: 'options', type: optionsTypes.primaryType }, - { name: 'sessionID', type: 'Optional' }, - ], - ...domainTypes.types, - ...optionsTypes.types, - ...eip712OptionalType('string'), - EIP712Domain: [ - { name: 'name', type: 'string' }, - { name: 'version', type: 'string' }, - ], - }, - primaryType: 'DisableDomainRequest', - domain: { - name: 'ODIS Disable Domain Request', - version: '1', - }, - message: request, - } -} - -/** - * Generic function to verify the signature on a Domain API request. - * - * @remarks Passing in the builder allows the caller to handle the differences of EIP-712 types - * between request types. Requests cannot be fully differentiated at runtime. In particular, - * DomainQuotaStatusRequest and DisableDomainRequest are indistinguishable at runtime. - * - * @privateRemarks Function is currently defined explicitly in terms of SequentialDelayDomain. It - * should be generalized to other authenticated domain types as they are standardized. - */ -function verifyRequestSignature>( - typedDataBuilder: (request: R) => EIP712TypedData, - request: R -): boolean { - // If the address field is undefined, then this domain is unauthenticated. - // Return false as the signature cannot be checked. - if (!request.domain.address.defined) { - return false - } - const signer = request.domain.address.value - - // If not signature is provided, return false. - if (!request.options.signature.defined) { - return false - } - const signature = request.options.signature.value - - // Requests are signed over the message excluding the signature. CIP-40 specifies that the - // signature in the signed message should be the zero value. When the signature type is - // EIP712Optional, this is { defined: false, value: "" } (i.e. `noString`) - const message: R = { - ...request, - options: { - ...request.options, - signature: noString, - }, - } - - // Build the typed data then return the result of signature verification. - const typedData = typedDataBuilder(message) - return verifyEIP712TypedDataSigner(typedData, signature, signer) -} - -/** - * Verifies the authentication (e.g. client signature) over a domain signature request. - * If the domain is unauthenticated, this function returns false. - * - * @remarks As specified in CIP-40, the signed message is the full request interpreted as EIP-712 - * typed data with the signature field in the domain options set to its zero value (i.e. It is set - * to the undefined value for type EIP712Optional). - */ -export function verifyDomainRestrictedSignatureRequestAuthenticity( - request: DomainRestrictedSignatureRequest -): boolean { - return verifyRequestSignature(domainRestrictedSignatureRequestEIP712, request) -} - -/** - * Verifies the authentication (e.g. client signature) over a domain status request. - * If the domain is unauthenticated, this function returns false. - * - * @remarks As specified in CIP-40, the signed message is the full request interpreted as EIP-712 - * typed data with the signature field in the domain options set to its zero value (i.e. It is set - * to the undefined value for type EIP712Optional). - */ -export function verifyDomainQuotaStatusRequestAuthenticity( - request: DomainQuotaStatusRequest -): boolean { - return verifyRequestSignature(domainQuotaStatusRequestEIP712, request) -} - -/** - * Verifies the authentication (e.g. client signature) over a disable domain request. - * If the domain is unauthenticated, this function returns false. - * - * @remarks As specified in CIP-40, the signed message is the full request interpreted as EIP-712 - * typed data with the signature field in the domain options set to its zero value (i.e. It is set - * to the undefined value for type EIP712Optional). - */ -export function verifyDisableDomainRequestAuthenticity( - request: DisableDomainRequest -): boolean { - return verifyRequestSignature(disableDomainRequestEIP712, request) -} - -interface PnpAuthHeader { - Authorization: string -} - -interface KeyVersionHeader { - [KEY_VERSION_HEADER]?: string -} - -export type DomainRestrictedSignatureRequestHeader = KeyVersionHeader -export type DisableDomainRequestHeader = undefined -export type DomainQuotaStatusRequestHeader = undefined - -export type DomainRequestHeader = - R extends DomainRestrictedSignatureRequest - ? DomainRestrictedSignatureRequestHeader - : never | R extends DisableDomainRequest - ? DisableDomainRequestHeader - : never | R extends DomainQuotaStatusRequest - ? DomainQuotaStatusRequestHeader - : never - -export type SignMessageRequestHeader = KeyVersionHeader & PnpAuthHeader - -export type PnpQuotaRequestHeader = PnpAuthHeader - -export type PhoneNumberPrivacyRequestHeader = R extends - | SignMessageRequest - | LegacySignMessageRequest - ? SignMessageRequestHeader - : never | R extends PnpQuotaRequest - ? PnpQuotaRequestHeader - : never - -export type OdisRequestHeader = R extends DomainRequest - ? DomainRequestHeader - : never | R extends PhoneNumberPrivacyRequest - ? PhoneNumberPrivacyRequestHeader - : never diff --git a/packages/phone-number-privacy/common/src/interfaces/responses.ts b/packages/phone-number-privacy/common/src/interfaces/responses.ts deleted file mode 100644 index 70e60ad867d..00000000000 --- a/packages/phone-number-privacy/common/src/interfaces/responses.ts +++ /dev/null @@ -1,251 +0,0 @@ -import * as t from 'io-ts' -import { - DisableDomainRequest, - DomainQuotaStatusRequest, - DomainRequest, - DomainRestrictedSignatureRequest, - LegacyPnpQuotaRequest, - LegacySignMessageRequest, - OdisRequest, - PhoneNumberPrivacyRequest, - PnpQuotaRequest, - SignMessageRequest, -} from '.' -import { Domain, DomainState } from '../domains' - -// Phone Number Privacy -export interface PnpQuotaStatus { - performedQueryCount: number - // all time total quota - totalQuota: number - blockNumber?: number -} - -const PnpQuotaStatusSchema: t.Type = t.intersection([ - t.type({ - performedQueryCount: t.number, - totalQuota: t.number, - }), - t.partial({ - blockNumber: t.union([t.number, t.undefined]), - }), -]) - -export interface SignMessageResponseSuccess extends PnpQuotaStatus { - success: true - version: string - signature: string - warnings?: string[] -} - -export interface SignMessageResponseFailure { - success: false - version: string - error: string - // These fields are occasionally provided by the signer but not the combiner - // because the combiner separates failure/success responses before processing states. - // => If the signer response fails, then it's irrelevant if that signer returned quota values, - // since these won't be used in the quota calculation anyways. - // Changing this is more involved; TODO(future) https://github.com/celo-org/celo-monorepo/issues/9826 - performedQueryCount?: number - totalQuota?: number - blockNumber?: number -} - -export type SignMessageResponse = SignMessageResponseSuccess | SignMessageResponseFailure - -export const SignMessageResponseSchema: t.Type = t.union([ - t.intersection([ - t.type({ - success: t.literal(true), - version: t.string, - signature: t.string, - }), - t.partial({ - warnings: t.union([t.array(t.string), t.undefined]), - }), - PnpQuotaStatusSchema, - ]), - t.intersection([ - t.type({ - success: t.literal(false), - version: t.string, - error: t.string, - }), - t.partial({ - performedQueryCount: t.union([t.number, t.undefined]), - totalQuota: t.union([t.number, t.undefined]), - blockNumber: t.union([t.number, t.undefined]), - }), - ]), -]) - -export interface PnpQuotaResponseSuccess extends PnpQuotaStatus { - success: true - version: string - warnings?: string[] -} - -export interface PnpQuotaResponseFailure { - success: false - version: string - error: string -} - -export type PnpQuotaResponse = PnpQuotaResponseSuccess | PnpQuotaResponseFailure - -export const PnpQuotaResponseSchema: t.Type = t.union([ - t.intersection([ - t.type({ - success: t.literal(true), - version: t.string, - }), - t.partial({ - warnings: t.union([t.array(t.string), t.undefined]), - }), - PnpQuotaStatusSchema, - ]), - t.type({ - success: t.literal(false), - version: t.string, - error: t.string, - }), -]) - -// prettier-ignore -export type PhoneNumberPrivacyResponse< - R extends PhoneNumberPrivacyRequest = PhoneNumberPrivacyRequest -> = - | R extends SignMessageRequest | LegacySignMessageRequest ? SignMessageResponse : never - | R extends PnpQuotaRequest | LegacyPnpQuotaRequest ? PnpQuotaResponse : never - -// Domains - -export interface DomainRestrictedSignatureResponseSuccess { - success: true - version: string - signature: string - status: DomainState -} - -export interface DomainRestrictedSignatureResponseFailure { - success: false - version: string - error: string - status?: DomainState -} - -export type DomainRestrictedSignatureResponse = - | DomainRestrictedSignatureResponseSuccess - | DomainRestrictedSignatureResponseFailure - -export interface DomainQuotaStatusResponseSuccess { - success: true - version: string - status: DomainState -} - -export interface DomainQuotaStatusResponseFailure { - success: false - version: string - error: string -} - -export type DomainQuotaStatusResponse = - | DomainQuotaStatusResponseSuccess - | DomainQuotaStatusResponseFailure - -export interface DisableDomainResponseSuccess { - success: true - version: string - status: DomainState -} - -export interface DisableDomainResponseFailure { - success: false - version: string - error: string -} - -export type DisableDomainResponse = - | DisableDomainResponseSuccess - | DisableDomainResponseFailure - -// prettier-ignore -export type DomainResponse< - R extends DomainRequest = DomainRequest -> = - | R extends DomainRestrictedSignatureRequest ? DomainRestrictedSignatureResponse : never - | R extends DomainQuotaStatusRequest ? DomainQuotaStatusResponse : never - | R extends DisableDomainRequest ? DisableDomainResponse : never - -export function domainRestrictedSignatureResponseSchema( - state: t.Type> -): t.Type> { - return t.union([ - t.type({ - success: t.literal(true), - version: t.string, - signature: t.string, - status: state, - }), - t.intersection([ - t.type({ - success: t.literal(false), - version: t.string, - error: t.string, - }), - t.partial({ - status: t.union([state, t.undefined]), - }), - ]), - ]) -} - -export function domainQuotaStatusResponseSchema( - state: t.Type> -): t.Type> { - return t.union([ - t.type({ - success: t.literal(true), - version: t.string, - status: state, - }), - t.type({ - success: t.literal(false), - version: t.string, - error: t.string, - }), - ]) -} - -export function disableDomainResponseSchema( - state: t.Type> -): t.Type> { - return t.union([ - t.type({ - success: t.literal(true), - version: t.string, - status: state, - }), - t.type({ - success: t.literal(false), - version: t.string, - error: t.string, - }), - ]) -} - -// General - -// prettier-ignore -export type OdisResponse = - | R extends DomainRequest ? DomainResponse : never - | R extends PhoneNumberPrivacyRequest ? PhoneNumberPrivacyResponse : never - -export type SuccessResponse = OdisResponse & { - success: true -} -export type FailureResponse = OdisResponse & { - success: false -} diff --git a/packages/phone-number-privacy/common/src/poprf.ts b/packages/phone-number-privacy/common/src/poprf.ts deleted file mode 100644 index 633b469af51..00000000000 --- a/packages/phone-number-privacy/common/src/poprf.ts +++ /dev/null @@ -1,246 +0,0 @@ -// Note that this import is only ever used for its type information. As a result, it will not be -// included in the compiled JavaScript or result in an import at runtime. -// https://www.typescriptlang.org/docs/handbook/modules.html#optional-module-loading-and-other-advanced-loading-scenarios -import * as POPRF from '@celo/poprf' -import { randomBytes } from 'crypto' - -/** - * @module - * This module provides interfaces for the Pith POPRF. It allows the construction of client, server, - * and combiner objects that wrap that package the required functionality. - * - * A partially-oblivious PRF (POPRF) is a protocol between a client and a server to evaluate the - * keyed pseudo-random function F(k, t, m). The client provides the tag input, t, and message input, - * m. The server provides the secret key input k. During the exchange the server learns the - * client-provided tag, but gains no other information. In particular, they learn nothing about the - * message. The client learns the output of the PRF function F, but no other information about the - * secret key held by the server. - * - * Implementation of the POPRF can be found in the following repository: - * https://github.com/celo-org/celo-poprf-rs - */ - -// tslint:disable: max-classes-per-file - -let _poprf: typeof POPRF | undefined - -/** - * Lazy loading function for the POPRF WASM implementation dependency. - * - * @remarks @celo/poprf version 0.1.x is compiled to a WASM is around 320 kB. Note that this must be - * loaded into memory at runtime. In order to avoid that cost, both on-disk and in memory, for users - * that do not need the POPRF functionality, it is loaded lazily, and included only as a dev - * dependency for clients that do not need it. If a package wants to utilize the POPRF - * functionality, it should add @celo/poprf to its dependencies (i.e. package.json). - */ -function poprf(): typeof POPRF { - // TODO: This will only initially work in Node environments. If we want to have this work in - // ReactNative and browser environments, some work will need to be done in @celo/poprf or here. - if (_poprf === undefined) { - try { - _poprf = require('@celo/poprf') - } catch { - throw new Error('@celo/poprf not available. Add it to the package.json') - } - } - return _poprf! -} - -/** Client for an instance of the POPRF protocol interacting with a POPRF service. */ -export class PoprfClient { - /** Blinded message to be sent to the POPRF service for evaluation */ - public readonly blindedMessage: Buffer - - /** Secret blinding factor used for blinding and response verification. */ - protected readonly blindingFactor: Buffer - - /** - * Constructs POPRF client state, blinding the given message and saving the public key, blinding - * factor, and tag for use in verification and unbinding of the response. - * - * @remarks Note that this client represents the client-side of a single protocol exchange. - * - * @param publicKey Public key for the POPRF service for use in verification. - * @param tag A plaintext tag which will be sent to the service along with the message. - * @param message A plaintext message which you want to blind and send to the POPRF service. - * @param seed Seed for the blinding factor. Provided if deterministic blinding is needed. - * Note that, by design, if the same seed and message is used twice, the blinded message will be - * the same. This allows for linking between the two blinded messages and so only should be used - * if this is intended (e.g. to provide for retries of requests without consuming quota). - */ - constructor( - readonly publicKey: Uint8Array, - readonly tag: Uint8Array, - readonly message: Uint8Array, - readonly seed?: Uint8Array - ) { - const blinded = poprf().blindMsg(message, seed ?? randomBytes(32)) - - // Save the blinding factor to the class state so that unblind can use it. - this.blindingFactor = Buffer.from(blinded.blindingFactor) - this.blindedMessage = Buffer.from(blinded.blindedMessage) - } - - /** - * Given a blinded evaluation response, unblind and verify the evaluation, returning the result. - * - * @remarks Note that this function expects a complete/aggregated response, and not a partial - * response as is returned by an individual server in a threshold service implementation. If the - * client wishes to unblind and verify partial responses, they will need to use the - * ThresholdPoprfClient. - * - * @param response A blinded evaluation response. - * @returns a buffer with the final POPRF output. - * - * @throws If the given response is invalid or cannot be verified against the public key, tag, and - * blinding state present in this client. - */ - public unblindResponse(response: Uint8Array): Buffer { - return Buffer.from(poprf().unblindResp(this.publicKey, this.blindingFactor, this.tag, response)) - } -} - -/** - * Combiner for an instance of the POPRF protocol acting as a relayer between the client and - * threshold service operators. A combiner effectively combines a set of service operators to appear - * as a single service. - * - * @remarks In the Pith POPRF protocol, verification occurs as part of the unblinding process and - * therefore only the client and determine is a given response is valid. As a result, the combiner - * cannot determine whether the responses from the service as correct, as long as they are - * well-formed. - */ -export class PoprfCombiner { - constructor(readonly threshold: number) { - if (threshold % 1 !== 0) { - throw new Error('POPRF threshold must be an integer') - } - } - - /** - * If there are enough responses provided, aggregates the collection of blind partial evaluations - * to a single blind threshold evaluation. - * - * @param response An array of blinded partial evaluation responses. - * @remarks Does not verify any of the responses. Verification only occurs during unblinding. - * - * @returns A buffer with a blind aggregated POPRF evaluation response, or undefined if there are - * less than the threshold number of responses provided. - */ - public blindAggregate(blindedResponses: Uint8Array[]): Buffer | undefined { - if (blindedResponses.length < this.threshold) { - return undefined - } - - return Buffer.from( - poprf().blindAggregate(this.threshold, Buffer.concat(blindedResponses.map(Buffer.from))) - ) - } - - /** - * If there are enough responses provided, aggregates the collection of partial evaluations - * to a single POPRF evaluation. - * - * @param response An array of partial evaluation responses. - * @returns A buffer with a POPRF evaluation, or undefined if there are less than the threshold - * number of responses provided. - */ - public aggregate(responses: Uint8Array[]): Buffer | undefined { - if (responses.length < this.threshold) { - return undefined - } - - return Buffer.from(poprf().aggregate(this.threshold, Buffer.concat(responses.map(Buffer.from)))) - } -} - -/** - * Client for interacting with a threshold implementation of the POPRF service without a combiner. - * - * @privateRemarks - * TODO Combine this class with the functionality from the combiner to create a POPRF client - * that can handle expunging bad partial evaluations from a set of responses. - */ -export class ThresholdPoprfClient extends PoprfClient { - /** - * Constructs POPRF client state, blinding the given message and saving the public keys, blinding - * factor, and tag for use in verification and unbinding of the response. - * - * Note that this client represents the client-side of a single protocol exchange. - * - * @param publicKey Public key for the POPRF service for use in verification. - * @param polynomial Public key polynomial for the individual POPRF servers for use in verification. - * @param tag A plaintext tag which will be sent to the service along with the message. - * @param message A plaintext message which you want to blind and send to the POPRF service. - * @param seed Seed for the blinding factor. Provided if deterministic blinding is needed. - * Note that, by design, if the same seed and message is used twice, the blinded message will be - * the same. This allows for linking between the two blinded messages and so only should be used - * if this is intended (e.g. to provide for retries of requests without consuming quota). - */ - constructor( - readonly publicKey: Uint8Array, - readonly polynomial: Uint8Array, - readonly tag: Uint8Array, - readonly message: Uint8Array, - readonly seed?: Uint8Array - ) { - super(publicKey, tag, message, seed) - } - - /** - * Given a blinded partial evaluation response, unblind and verify the evaluation share, returning the result. - * - * @param response A blinded partial evaluation response. - * @returns a buffer with unblinded partial evaluation. - * - * @throws If the given response is invalid or cannot be verified against the public key, tag, and - * blinding state present in this client. - */ - public unblindPartialResponse(response: Uint8Array): Buffer { - return Buffer.from( - poprf().unblindPartialResp(this.polynomial, this.blindingFactor, this.tag, response) - ) - } -} - -/** - * Server for the POPRF protocol for answering queries from clients. - * - * @remarks Note that, unlike the client, the server object is stateless and may be used for - * multiple protocol exchanges, including being used concurrently. - */ -export class PoprfServer { - constructor(readonly privateKey: Uint8Array) {} - - /** - * Evaluates the POPRF function over the tag and blinded message with the (complete) private key - * - * @param tag plaintext tag buffer to be combined with the blinded message in the POPRF. - * - * @returns a serialized blinded evaluation response. - */ - public blindEval(tag: Uint8Array, blindedMessage: Uint8Array): Buffer { - return Buffer.from(poprf().blindEval(this.privateKey, tag, blindedMessage)) - } -} - -/** - * Server for a threshold implementation of the POPRF protocol for answering queries from clients. - * - * @remarks Note that, unlike the client, the server object is stateless and may be used for - * multiple protocol exchanges, including being used concurrently. - */ -export class ThresholdPoprfServer { - constructor(readonly privateKeyShare: Uint8Array) {} - - /** - * Evaluates the POPRF function over the tag and blinded message with the private key share. - * - * @param tag plaintext tag buffer to be combined with the blinded message in the POPRF. - * - * @returns a serialized blinded partial evaluation response. - */ - public blindPartialEval(tag: Uint8Array, blindedMessage: Uint8Array): Buffer { - return Buffer.from(poprf().blindPartialEval(this.privateKeyShare, tag, blindedMessage)) - } -} diff --git a/packages/phone-number-privacy/common/src/test/index.ts b/packages/phone-number-privacy/common/src/test/index.ts deleted file mode 100644 index 63c1c20597d..00000000000 --- a/packages/phone-number-privacy/common/src/test/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -import * as Utils from './utils' -import * as Values from './values' - -export const TestUtils = { - Utils, - Values, -} diff --git a/packages/phone-number-privacy/common/src/test/utils.ts b/packages/phone-number-privacy/common/src/test/utils.ts deleted file mode 100644 index a2e57365ec3..00000000000 --- a/packages/phone-number-privacy/common/src/test/utils.ts +++ /dev/null @@ -1,180 +0,0 @@ -import { privateKeyToAddress } from '@celo/utils/lib/address' -import { serializeSignature, Signature, signMessage } from '@celo/utils/lib/signatureUtils' -import BigNumber from 'bignumber.js' -import Web3 from 'web3' -import { - AuthenticationMethod, - LegacyPnpQuotaRequest, - LegacySignMessageRequest, - PhoneNumberPrivacyRequest, - PnpQuotaRequest, - SignMessageRequest, -} from '../interfaces' -import { signWithRawKey } from '../utils/authentication' -import { genSessionID } from '../utils/logger' - -export interface AttestationsStatus { - isVerified: boolean - numAttestationsRemaining: number - total: number - completed: number -} - -export function createMockAttestation(getVerifiedStatus: jest.Mock) { - return { - getVerifiedStatus, - } -} - -export function createMockToken(balanceOf: jest.Mock) { - return { - balanceOf, - } -} - -export function createMockAccounts( - getWalletAddress: jest.Mock, - getDataEncryptionKey: jest.Mock -) { - return { - getWalletAddress, - getDataEncryptionKey, - } -} - -// Take in jest.Mock to enable individual tests to spy on function calls -// and more easily set return values -export function createMockOdisPayments(totalPaidCUSDFunc: jest.Mock) { - return { - totalPaidCUSD: totalPaidCUSDFunc, - } -} - -export function createMockContractKit( - c: { [contractName in ContractRetrieval]?: any }, - mockWeb3?: any -) { - const contracts: any = {} - for (const t of Object.keys(c)) { - contracts[t] = jest.fn(() => c[t as ContractRetrieval]) - } - - return { - contracts, - registry: { - addressFor: async () => 1000, - }, - connection: createMockConnection(mockWeb3), - } -} - -export function createMockConnection(mockWeb3?: any) { - mockWeb3 = mockWeb3 ?? new Web3() - return { - web3: mockWeb3, - getTransactionCount: jest.fn(() => mockWeb3.eth.getTransactionCount()), - getBlockNumber: jest.fn(() => { - return mockWeb3.eth.getBlockNumber() - }), - } -} - -export enum ContractRetrieval { - getAttestations = 'getAttestations', - getStableToken = 'getStableToken', - getGoldToken = 'getGoldToken', - getAccounts = 'getAccounts', - getOdisPayments = 'getOdisPayments', -} - -export function createMockWeb3(txCount: number, blockNumber: number) { - return { - eth: { - getTransactionCount: jest.fn(() => txCount), - getBlockNumber: jest.fn(() => blockNumber), - }, - } -} - -export async function replenishQuota(account: string, contractKit: any) { - const goldToken = await contractKit.contracts.getGoldToken() - const selfTransferTx = goldToken.transfer(account, 1) - await selfTransferTx.sendAndWaitForReceipt({ from: account }) -} - -export async function registerWalletAddress( - accountAddress: string, - walletAddress: string, - walletAddressPk: string, - contractKit: any -) { - const accounts = await contractKit.contracts.getAccounts() - const pop = await accounts.generateProofOfKeyPossessionLocally( - accountAddress, - walletAddress, - walletAddressPk - ) - await accounts - .setWalletAddress(walletAddress, pop as Signature) - .sendAndWaitForReceipt({ from: accountAddress } as any) -} - -export function getPnpQuotaRequest( - account: string, - authenticationMethod?: string -): PnpQuotaRequest { - return { - account, - authenticationMethod, - sessionID: genSessionID(), - } -} -export function getLegacyPnpQuotaRequest( - account: string, - authenticationMethod?: string, - hashedPhoneNumber?: string -): LegacyPnpQuotaRequest { - return { - account, - authenticationMethod, - hashedPhoneNumber, - sessionID: genSessionID(), - } -} - -export function getLegacyPnpSignRequest( - account: string, - blindedQueryPhoneNumber: string, - authenticationMethod?: string, - hashedPhoneNumber?: string -): LegacySignMessageRequest { - return { - account, - blindedQueryPhoneNumber, - authenticationMethod, - hashedPhoneNumber, - sessionID: genSessionID(), - } -} - -export function getPnpSignRequest( - account: string, - blindedQueryPhoneNumber: string, - authenticationMethod?: string -): SignMessageRequest { - return { - account, - blindedQueryPhoneNumber, - authenticationMethod, - sessionID: genSessionID(), - } -} - -export function getPnpRequestAuthorization(req: PhoneNumberPrivacyRequest, pk: string) { - const msg = JSON.stringify(req) - if (req.authenticationMethod === AuthenticationMethod.ENCRYPTION_KEY) { - return signWithRawKey(JSON.stringify(req), pk) - } - const account = privateKeyToAddress(pk) - return serializeSignature(signMessage(msg, pk, account)) -} diff --git a/packages/phone-number-privacy/common/src/test/values.ts b/packages/phone-number-privacy/common/src/test/values.ts deleted file mode 100644 index e92546f0592..00000000000 --- a/packages/phone-number-privacy/common/src/test/values.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { PhoneNumberUtils } from '@celo/phone-utils' -import { normalizeAddressWith0x, privateKeyToAddress } from '@celo/utils/lib/address' - -export const mockAccount = '0x0000000000000000000000000000000000007E57' -export const mockPhoneNumber = '+14155556666' -export const mockContractAddress = '0x000000000000000000000000000000000000CE10' - -export const PRIVATE_KEY1 = '535029bfb19fe5440dbd549b88fbf5ee847b059485e4eafc2a3e3bdfbf9b31ac' -export const ACCOUNT_ADDRESS1 = normalizeAddressWith0x(privateKeyToAddress(PRIVATE_KEY1)) -export const PRIVATE_KEY2 = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890fdeccc' -export const ACCOUNT_ADDRESS2 = privateKeyToAddress(PRIVATE_KEY2) -export const PRIVATE_KEY3 = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890fffff1d' -export const ACCOUNT_ADDRESS3 = normalizeAddressWith0x(privateKeyToAddress(PRIVATE_KEY3)) -export const PHONE_NUMBER = '+15555555555' -export const IDENTIFIER = PhoneNumberUtils.getPhoneHash(PHONE_NUMBER) -export const BLINDING_FACTOR = Buffer.from('0IsBvRfkBrkKCIW6HV0/T1zrzjQSe8wRyU3PKojCnww=', 'base64') -// BLINDED_PHONE_NUMBER value dependent on PHONE_NUMBER AND BLINDING_FACTOR -// hardcoding to avoid importing blind_threshols_bls library -export const BLINDED_PHONE_NUMBER = - 'n/I9srniwEHm5o6t3y0tTUB5fn7xjxRrLP1F/i8ORCdqV++WWiaAzUo3GA2UNHiB' -export const DEK_PUBLIC_KEY = '0x026063780c81991c032fb4fa7485c6607b7542e048ef85d08516fe5c4482360e4b' -export const DEK_PRIVATE_KEY = '0xc2bbdabb440141efed205497a41d5fb6114e0435fd541e368dc628a8e086bfee' - -// Public keys are expected to be in base64 -export const PNP_DEV_ODIS_PUBLIC_KEY = - 'HzMTasAppwLrBBCWvZ7wncnDaN3lKpcoZr3q/wiW+FlrdKt639cxi7o4UnWZdoQA30S8q2a884Q8F6LOg4vNWouhY0wYMU/wVlp8dpkFuKj7onqGv0xssi34nhut/iuB' -export const PNP_DEV_SIGNER_PRIVATE_KEY = - '00000000dd0005bf4de5f2f052174f5cf58dae1af1d556c7f7f85d6fb3656e1d0f10720f' -export const PNP_DEV_ODIS_POLYNOMIAL = - '01000000000000001f33136ac029a702eb041096bd9ef09dc9c368dde52a972866bdeaff0896f8596b74ab7adfd7318bba38527599768400df44bcab66bcf3843c17a2ce838bcd5a8ba1634c18314ff0565a7c769905b8a8fba27a86bf4c6cb22df89e1badfe2b81' - -// Public keys are expected to be in base64 -export const DOMAINS_DEV_ODIS_PUBLIC_KEY = - 'CyJK6fkM0ZRILiW0h85LFev4BbMcLH1RBX5I9BNDgwX5jM74kv8+FjFZuJ1C4P0ADU1fuPGXXQg+wAGCclUD+BCza6ItIxSYmwsZ4ie1Iw1/pdTcwPJJlXwYwcDo+LKA' -export const DOMAINS_DEV_SIGNER_PRIVATE_KEY = - '01000000f0c2d6231c9ed833da9478cbfd6e4970fcd893e156973862f6d286e7e1f6d904' -export const DOMAINS_DEV_ODIS_POLYNOMIAL = - '01000000000000000b224ae9f90cd194482e25b487ce4b15ebf805b31c2c7d51057e48f413438305f98ccef892ff3e163159b89d42e0fd000d4d5fb8f1975d083ec00182725503f810b36ba22d2314989b0b19e227b5230d7fa5d4dcc0f249957c18c1c0e8f8b280' - -// Generated with 2/3 ratio - -export const PNP_THRESHOLD_DEV_PUBKEY_V1 = - '61aeuHAdgxoKn/5d8yXu0qx/VpPHWMAqrVgEAJ/MpC7Oc/f1YLPiN7YKaw9eDWUBUWs4sPn6IN2UTGbt95jP6nO8IymD4IhbBONjLcElsq1jwTZ2cjuTHV9obSyDFl2B' -export const PNP_THRESHOLD_DEV_PK_SHARE_1_V1 = - '000000000e7e1a2fad3b54deb2b1b32cf4c7b084842d50bbb5c6143b9d9577d16e050f03' -export const PNP_THRESHOLD_DEV_PK_SHARE_2_V1 = - '01000000e43f10f7778e238e1ed58d5fad9363d7439d2b5a8eeda6073d68ba87c0b10011' -export const PNP_THRESHOLD_DEV_PK_SHARE_3_V1 = - '02000000b90106bf4261e13389f867c267e86bd0015dcf9c48c784738695d0a3b3f8460c' -export const PNP_THRESHOLD_DEV_POLYNOMIAL_V1 = - '0200000000000000eb569eb8701d831a0a9ffe5df325eed2ac7f5693c758c02aad5804009fcca42ece73f7f560b3e237b60a6b0f5e0d6501516b38b0f9fa20dd944c66edf798cfea73bc232983e0885b04e3632dc125b2ad63c13676723b931d5f686d2c83165d817aaff1f84d0b008ad218eff19db698f343168cf931ba8347640123a2f826f62b66ff084273f494d4647758e9a9f889009d573705824a0e74e1f49ed234462058e53bbb4fef370b55f78da89df070c661782a84239b8c7623d09e34b9f91f7781' - -// Note: The pubkey doesn't change with a resharing, so normally the different key versions would have the same pubkey. -// We generated these key versions independently (not through resharing), since that is sufficient to test the key rotation logic - -export const PNP_THRESHOLD_DEV_PUBKEY_V2 = - '2ckOWP3qphyao1R4s8VHbVRdenGcFsgskQh5eCMqAwAziJzQAZ6Wo9CFD30YhhoA6B91QFIQaqfDvdblNeOtMDsmIKTDFtxZjg+cZZtQzrCTLU2owWEEb8RPJc8F3ekA' -export const PNP_THRESHOLD_DEV_PK_SHARE_1_V2 = - '0000000087c722e1338395b942d8332328795a46c718baeb8fef9e5c63111d495469c50e' -export const PNP_THRESHOLD_DEV_PK_SHARE_2_V2 = - '01000000e4efa9b60743f8188a68d35663d877143ad1726931eaa9af168fc86472eafd0d' -export const PNP_THRESHOLD_DEV_PK_SHARE_3_V2 = - '020000004118318cdb025b78d1f8728a9e3795e2ac892be7d2e4b402ca0c7480906b360d' -export const PNP_THRESHOLD_DEV_POLYNOMIAL_V2 = - '0200000000000000d9c90e58fdeaa61c9aa35478b3c5476d545d7a719c16c82c91087978232a030033889cd0019e96a3d0850f7d18861a00e81f754052106aa7c3bdd6e535e3ad303b2620a4c316dc598e0f9c659b50ceb0932d4da8c161046fc44f25cf05dde900ebf6f83c5cb94288347ebf437e99fbb7a7eaf0c9873467352c1a9113f5fc0974d96cbf25462def50c39224da757ed300ce12e0fa8c6e73387cb43c69764bed41d0a0c55981642650b07fad1107a27b27fc8c552da3edd64494e8acc4de9a2600' - -export const PNP_THRESHOLD_DEV_PUBKEY_V3 = - '5o9Y516dvzZLy7E/SfOSm2kVh02t1rU1tkJrk55/HjhRSZtyHRgAOnbnvKJvQjAA1OE70LsYlrKK8PGNVOp7cVdrFbm9xbkew+BU6hdO473qierDOF4SjKQNToyh5UOB' -export const PNP_THRESHOLD_DEV_PK_SHARE_1_V3 = - '000000005b2c8089ead28a08233b6b16b2341542453523445950cfbd9bd2f1d09c8eee0c' -export const PNP_THRESHOLD_DEV_PK_SHARE_2_V3 = - '01000000f6c10aa979a0c33a3af5b03c37ffdf1d4a8517a5f9e6058e1d863337eeb59904' -export const PNP_THRESHOLD_DEV_PK_SHARE_3_V3 = - '02000000925795c808ee0d7752aff632bb40555350854362b8caf0bef5dea1379e42f00e' -export const PNP_THRESHOLD_DEV_POLYNOMIAL_V3 = - '0200000000000000e68f58e75e9dbf364bcbb13f49f3929b6915874dadd6b535b6426b939e7f1e3851499b721d18003a76e7bca26f423000d4e13bd0bb1896b28af0f18d54ea7b71576b15b9bdc5b91ec3e054ea174ee3bdea89eac3385e128ca40d4e8ca1e543813fae8439a057f8c17d4538afecf038624e552a8c226c9f82bfb7a072cff28fb7d26ab45801b67db270cec8037b8d7e016b1b78f7997160bd4ed1b54ab5d6be7663935992cd9c59ceb17010eccd708a9762df616c1fe45a220be634e21ba87581' - -export const PNP_THRESHOLD_DEV_POLYNOMIALS = [ - PNP_THRESHOLD_DEV_POLYNOMIAL_V1, - PNP_THRESHOLD_DEV_POLYNOMIAL_V2, - PNP_THRESHOLD_DEV_POLYNOMIAL_V3, -] - -export const PNP_THRESHOLD_DEV_PUBKEYS = [ - PNP_THRESHOLD_DEV_PUBKEY_V1, - PNP_THRESHOLD_DEV_PUBKEY_V2, - PNP_THRESHOLD_DEV_PUBKEY_V3, -] - -export const DOMAINS_THRESHOLD_DEV_PUBKEY_V1 = - 'zaetF6aXkBAkVwoUosuyQ8xiK2tKM9/zKrTPKbxoDoO7p6DSwbetk5uEICK+PjcAG4pGGY81jaUPSsPqlwIDfOy+RxJ2O+5ZPDM4I+b70MSYZYrsZ6qPxg+xtqLb9AOA' -export const DOMAINS_THRESHOLD_DEV_PK_SHARE_1_V1 = - '010000000c63d9615b2ff0746562c0b438286544f029698a4205cd8b8f93afaa5b793211' -export const DOMAINS_THRESHOLD_DEV_PK_SHARE_2_V1 = - '020000001b63b2c531070b176f56042da68923c5859b9f82181559646b58445976de5f08' -export const DOMAINS_THRESHOLD_DEV_PK_SHARE_3_V1 = - '030000002b638b29085f37c3794a487512628c9f1cbd0dd70c72999d9dc205a2efa83812' -export const DOMAINS_THRESHOLD_DEV_POLYNOMIAL_V1 = - '0200000000000000cda7ad17a697901024570a14a2cbb243cc622b6b4a33dff32ab4cf29bc680e83bba7a0d2c1b7ad939b842022be3e37001b8a46198f358da50f4ac3ea9702037cecbe4712763bee593c333823e6fbd0c498658aec67aa8fc60fb1b6a2dbf403804e71a1bb1f51f2186f579048cb224f8993295e699ea14552506418df6fcf019ffe6f89253d6122cc97b8f8c5785674006c821ca2d596e4c0d75aba2b03e8ba082e002d24ebe5c48956ef96b8ac85f96c9c7929e8facac50b74b3aac792ad5d00' - -export const DOMAINS_THRESHOLD_DEV_PUBKEY_V2 = - 'rc9WQhFQn64w9FzlbVgyZi8Cd/bep+l3MtzPOWMInRQ3XoJMDSJ15SzBgE6M6JEAr58f9m2zZi6TMEcogbg3hHp37MUoybowzbGeed9jWqCWGQ0VBMFMaJLR8exNdtkA' -export const DOMAINS_THRESHOLD_DEV_PK_SHARE_1_V2 = - '01000000a8070976747d9bb1fe56d822a57252ce3ddd5a8acef7e3ed94aeb52a16da4d04' -export const DOMAINS_THRESHOLD_DEV_PK_SHARE_2_V2 = - '020000003d8495ff96deb0109216d575bc0f6ff364466e830ecb4d0e860d869ef8b82e0b' -export const DOMAINS_THRESHOLD_DEV_PK_SHARE_3_V2 = - '03000000d2002289b93fc66f25d6d1c8d3ac8b188caf817c4e9eb72e776c5612db970f12' -export const DOMAINS_THRESHOLD_DEV_POLYNOMIAL_V2 = - '0200000000000000adcf564211509fae30f45ce56d5832662f0277f6dea7e97732dccf3963089d14375e824c0d2275e52cc1804e8ce89100af9f1ff66db3662e9330472881b837847a77ecc528c9ba30cdb19e79df635aa096190d1504c14c6892d1f1ec4d76d900c32eadf29b938d0466e566b527c798434931c6c2afd84fdd34aa5d620b15d19b6b1d59f9fa0c81150bf62d316a1b8f000708a46bd4c807cab0a60e9692e1efe74084ae1503172377e39600b8fd88b4885ee55adae7bb21993909da127d3c0c81' - -export const DOMAINS_THRESHOLD_DEV_PUBKEY_V3 = - 'OGHVPM0uXSduGBKQNyyGBr7IHXZQbnG9WopBhw5m0nddsmcoQP30/IBGCB0JGOsAemcw/mP43ueJxw7PPo/m+7JhFyu8cX7F61ULbmHAFd84wneZJf42U42rWSoC+IeB' -export const DOMAINS_THRESHOLD_DEV_PK_SHARE_1_V3 = - '0100000030c5c6ae0959c96ccc3a31c73b1b603b9b448ccfc80dba2e496d31295caee40e' -export const DOMAINS_THRESHOLD_DEV_PK_SHARE_2_V3 = - '02000000768eb3c42a126abaa80eb8cae14006f2a98cdd175d40984ad84e881c7bf7da0d' -export const DOMAINS_THRESHOLD_DEV_PK_SHARE_3_V3 = - '03000000bc57a0da4bcb0a0885e23ece8766aca8b8d42e60f17276666730df0f9a40d10c' -export const DOMAINS_THRESHOLD_DEV_POLYNOMIAL_V3 = - '02000000000000003861d53ccd2e5d276e181290372c8606bec81d76506e71bd5a8a41870e66d2775db2672840fdf4fc8046081d0918eb007a6730fe63f8dee789c70ecf3e8fe6fbb261172bbc717ec5eb550b6e61c015df38c2779925fe36538dab592a02f887817f62a8f350751888132fca7dd7ff06731102483340145ff1571229884b06bfbfb25636e4bc6ad5dc294a09e45f7171012d042d5be90537c3f0eb70d51c7f7a6c09cee7af8c4af1750afde124a47a98330073af8c9011ab8a1571bc8ee958e200' - -export const DOMAINS_THRESHOLD_DEV_PUBKEY_V4 = - 'iRyLg54DDNq2c1TUAbsnc2VB5BwjBjBjJCysj6NO/Fmuki3LHjaSOscbNTQtZkIBTjBTALBDPzJAr1hDFebQTFHfg7oNaFUiEKC7P7Mhd0X9BJWNV8MEm+ZG4DymrAgA' -export const DOMAINS_THRESHOLD_DEV_PK_SHARE_1_V4 = - '0100000044d1155eb821064919ef3b35625aa3595e2f0285d23181997836c6b18c661901' -export const DOMAINS_THRESHOLD_DEV_PK_SHARE_2_V4 = - '02000000b02c15e2769896f6c8b9bd2e3be1ddcac753683e2b991ac7c12531334122ee00' -export const DOMAINS_THRESHOLD_DEV_PK_SHARE_3_V4 = - '030000001c881466350f27a478843f281468183c3178cef78300b4f40a159cb4f5ddc200' -export const DOMAINS_THRESHOLD_DEV_POLYNOMIAL_V4 = - '0200000000000000891c8b839e030cdab67354d401bb27736541e41c23063063242cac8fa34efc59ae922dcb1e36923ac71b35342d6642014e305300b0433f3240af584315e6d04c51df83ba0d68552210a0bb3fb3217745fd04958d57c3049be646e03ca6ac08004a8cd44dd5f648a0f3cba05024829e25c79e603193fd7cdedce1cf400bf828bea0aee6b6b792c8efb6771713e6a30e01c8f8f981445a4455ee425a676133f8a095850245d32ce4765d83fc672a87c7116295c4b4927c51aec38b944260ea0200' - -export const DOMAINS_THRESHOLD_DEV_POLYNOMIALS = [ - DOMAINS_THRESHOLD_DEV_POLYNOMIAL_V1, - DOMAINS_THRESHOLD_DEV_POLYNOMIAL_V2, - DOMAINS_THRESHOLD_DEV_POLYNOMIAL_V3, - DOMAINS_THRESHOLD_DEV_POLYNOMIAL_V4, -] - -export const DOMAINS_THRESHOLD_DEV_PUBKEYS = [ - DOMAINS_THRESHOLD_DEV_PUBKEY_V1, - DOMAINS_THRESHOLD_DEV_PUBKEY_V2, - DOMAINS_THRESHOLD_DEV_PUBKEY_V3, - DOMAINS_THRESHOLD_DEV_PUBKEY_V4, -] diff --git a/packages/phone-number-privacy/common/src/utils/authentication.ts b/packages/phone-number-privacy/common/src/utils/authentication.ts deleted file mode 100644 index 782e23bc575..00000000000 --- a/packages/phone-number-privacy/common/src/utils/authentication.ts +++ /dev/null @@ -1,194 +0,0 @@ -import { hexToBuffer, retryAsyncWithBackOffAndTimeout } from '@celo/base' -import { ContractKit } from '@celo/contractkit' -import { AccountsWrapper } from '@celo/contractkit/lib/wrappers/Accounts' -import { AttestationsWrapper } from '@celo/contractkit/lib/wrappers/Attestations' -import { trimLeading0x } from '@celo/utils/lib/address' -import { verifySignature } from '@celo/utils/lib/signatureUtils' -import Logger from 'bunyan' -import crypto from 'crypto' -import { Request } from 'express' -import { fetchEnv, rootLogger } from '..' -import { - AuthenticationMethod, - ErrorMessage, - ErrorType, - PhoneNumberPrivacyRequest, -} from '../interfaces' -import { FULL_NODE_TIMEOUT_IN_MS, RETRY_COUNT, RETRY_DELAY_IN_MS } from './constants' - -/* - * Confirms that user is who they say they are and throws error on failure to confirm. - * Authorization header should contain the EC signed body - */ -export async function authenticateUser( - request: Request<{}, {}, R>, - contractKit: ContractKit, - logger: Logger, - shouldFailOpen: boolean = false, - warnings: ErrorType[] = [], - timeoutMs: number = FULL_NODE_TIMEOUT_IN_MS -): Promise { - logger.debug('Authenticating user') - - // https://tools.ietf.org/html/rfc7235#section-4.2 - const messageSignature = request.get('Authorization') - const message = JSON.stringify(request.body) - const signer = request.body.account - const authMethod = request.body.authenticationMethod - - if (!messageSignature || !signer) { - return false - } - - if (authMethod && authMethod === AuthenticationMethod.ENCRYPTION_KEY) { - let registeredEncryptionKey - try { - registeredEncryptionKey = await getDataEncryptionKey(signer, contractKit, logger, timeoutMs) - } catch (err) { - // getDataEncryptionKey should only throw if there is a full-node connection issue. - // That is, it does not throw if the DEK is undefined or invalid - const failureStatus = shouldFailOpen ? ErrorMessage.FAILING_OPEN : ErrorMessage.FAILING_CLOSED - logger.error({ - err, - warning: ErrorMessage.FAILURE_TO_GET_DEK, - failureStatus, - }) - warnings.push(ErrorMessage.FAILURE_TO_GET_DEK, failureStatus) - return shouldFailOpen - } - if (!registeredEncryptionKey) { - logger.warn({ account: signer }, 'Account does not have registered encryption key') - return false - } else { - logger.info({ dek: registeredEncryptionKey, account: signer }, 'Found DEK for account') - if (verifyDEKSignature(message, messageSignature, registeredEncryptionKey, logger)) { - return true - } - } - } - - // Fallback to previous signing pattern - logger.info( - { account: signer, message, messageSignature }, - 'Message was not authenticated with DEK, attempting to authenticate using wallet key' - ) - // TODO This uses signature utils, why doesn't DEK authentication? - // (https://github.com/celo-org/celo-monorepo/issues/9803) - return verifySignature(message, messageSignature, signer) -} - -export function getMessageDigest(message: string) { - // NOTE: Elliptic will truncate the raw msg to 64 bytes before signing, - // so make sure to always pass the hex encoded msgDigest instead. - return crypto.createHash('sha256').update(JSON.stringify(message)).digest('hex') -} - -// Used primarily for signing requests with a DEK, counterpart of verifyDEKSignature -// For general signing, use SignatureUtils in @celo/utils -export function signWithRawKey(msg: string, rawKey: string) { - // NOTE: elliptic is disabled elsewhere in this library to prevent - // accidental signing of truncated messages. - // tslint:disable-next-line:import-blacklist - const EC = require('elliptic').ec - const ec = new EC('secp256k1') - - // Sign - const key = ec.keyFromPrivate(hexToBuffer(rawKey)) - return JSON.stringify(key.sign(getMessageDigest(msg)).toDER()) -} - -export function verifyDEKSignature( - message: string, - messageSignature: string, - registeredEncryptionKey: string, - logger?: Logger -) { - logger = logger ?? rootLogger(fetchEnv('SERVICE_NAME')) - try { - // NOTE: elliptic is disabled elsewhere in this library to prevent - // accidental signing of truncated messages. - // tslint:disable-next-line:import-blacklist - const EC = require('elliptic').ec - const ec = new EC('secp256k1') - const key = ec.keyFromPublic(trimLeading0x(registeredEncryptionKey), 'hex') - const parsedSig = JSON.parse(messageSignature) - // TODO why do we use a different signing method instead of SignatureUtils? - // (https://github.com/celo-org/celo-monorepo/issues/9803) - if (key.verify(getMessageDigest(message), parsedSig)) { - return true - } - return false - } catch (err) { - logger.error('Failed to verify signature with DEK') - logger.error({ err, dek: registeredEncryptionKey }) - return false - } -} - -export async function getDataEncryptionKey( - address: string, - contractKit: ContractKit, - logger: Logger, - timeoutMs: number -): Promise { - try { - const res = await retryAsyncWithBackOffAndTimeout( - async () => { - const accountWrapper: AccountsWrapper = await contractKit.contracts.getAccounts() - return accountWrapper.getDataEncryptionKey(address) - }, - RETRY_COUNT, - [], - RETRY_DELAY_IN_MS, - 1.5, - timeoutMs - ) - return res - } catch (error) { - logger.error('Failed to retrieve DEK: ' + error) - logger.error(ErrorMessage.FULL_NODE_ERROR) - throw error - } -} - -export async function isVerified( - account: string, - hashedPhoneNumber: string, - contractKit: ContractKit, - logger: Logger -): Promise { - try { - const res = await retryAsyncWithBackOffAndTimeout( - async () => { - const attestationsWrapper: AttestationsWrapper = - await contractKit.contracts.getAttestations() - const { - isVerified: _isVerified, - completed, - numAttestationsRemaining, - total, - } = await attestationsWrapper.getVerifiedStatus(hashedPhoneNumber, account) - - logger.debug({ - account, - isVerified: _isVerified, - completedAttestations: completed, - remainingAttestations: numAttestationsRemaining, - totalAttestationsRequested: total, - }) - return _isVerified - }, - RETRY_COUNT, - [], - RETRY_DELAY_IN_MS, - 1.5, - FULL_NODE_TIMEOUT_IN_MS - ) - return res - } catch (error) { - logger.error('Failed to get verification status: ' + error) - logger.error(ErrorMessage.FULL_NODE_ERROR) - logger.warn('Assuming user is verified') - return true - } -} diff --git a/packages/phone-number-privacy/common/src/utils/config.utils.ts b/packages/phone-number-privacy/common/src/utils/config.utils.ts deleted file mode 100644 index 8b4abc33a66..00000000000 --- a/packages/phone-number-privacy/common/src/utils/config.utils.ts +++ /dev/null @@ -1,21 +0,0 @@ -import BigNumber from 'bignumber.js' -import * as dotenv from 'dotenv' - -dotenv.config() - -export const toNum = (value: BigNumber.Value) => new BigNumber(value).toNumber() -export const toBool = (value: string | undefined, fallback: boolean) => - value ? value.toLowerCase() === 'true' : fallback - -export function fetchEnv(name: string): string { - if (process.env[name] === undefined || process.env[name] === '') { - throw new Error(`ENV var '${name}' was not defined`) - } - return process.env[name] as string -} - -export function fetchEnvOrDefault(name: string, defaultValue: string): string { - return process.env[name] === undefined || process.env[name] === '' - ? defaultValue - : (process.env[name] as string) -} diff --git a/packages/phone-number-privacy/common/src/utils/constants.ts b/packages/phone-number-privacy/common/src/utils/constants.ts deleted file mode 100644 index c50f375e84d..00000000000 --- a/packages/phone-number-privacy/common/src/utils/constants.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const REASONABLE_BODY_CHAR_LIMIT: number = 16_000 -export const DB_TIMEOUT = 1000 -export const FULL_NODE_TIMEOUT_IN_MS = 1000 -export const RETRY_COUNT = 5 -export const RETRY_DELAY_IN_MS = 100 -export const KEY_VERSION_HEADER = 'odis-key-version' // headers must be all lower case diff --git a/packages/phone-number-privacy/common/src/utils/contracts.ts b/packages/phone-number-privacy/common/src/utils/contracts.ts deleted file mode 100644 index f4952231f1f..00000000000 --- a/packages/phone-number-privacy/common/src/utils/contracts.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ContractKit, newKit, newKitWithApiKey } from '@celo/contractkit' - -export interface BlockchainConfig { - provider: string - apiKey?: string -} - -export function getContractKit(config: BlockchainConfig): ContractKit { - return config.apiKey ? newKitWithApiKey(config.provider, config.apiKey) : newKit(config.provider) -} diff --git a/packages/phone-number-privacy/common/src/utils/input-validation.ts b/packages/phone-number-privacy/common/src/utils/input-validation.ts deleted file mode 100644 index 6ce052d0304..00000000000 --- a/packages/phone-number-privacy/common/src/utils/input-validation.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { isValidAddress, trimLeading0x } from '@celo/utils/lib/address' -import isBase64 from 'is-base64' -import { - GetQuotaRequest, - LegacySignMessageRequest, - PnpQuotaRequest, - SignMessageRequest, -} from '../interfaces' -import { REASONABLE_BODY_CHAR_LIMIT } from './constants' - -export function hasValidAccountParam(requestBody: { account: string }): boolean { - return !!requestBody.account && isValidAddress(requestBody.account) -} - -// Legacy message signing & quota requests extend the new types -export function isBodyReasonablySized(requestBody: SignMessageRequest | PnpQuotaRequest): boolean { - return JSON.stringify(requestBody).length <= REASONABLE_BODY_CHAR_LIMIT -} - -export function hasValidBlindedPhoneNumberParam(requestBody: SignMessageRequest): boolean { - return ( - !!requestBody.blindedQueryPhoneNumber && - requestBody.blindedQueryPhoneNumber.length === 64 && - isBase64(requestBody.blindedQueryPhoneNumber) - ) -} - -export function identifierIsValidIfExists( - requestBody: GetQuotaRequest | LegacySignMessageRequest -): boolean { - return !requestBody.hashedPhoneNumber || isByte32(requestBody.hashedPhoneNumber) -} - -const hexString = new RegExp(/[0-9A-Fa-f]{32}/, 'i') - -function isByte32(hashedData: string): boolean { - return hexString.test(trimLeading0x(hashedData)) -} diff --git a/packages/phone-number-privacy/common/src/utils/key-version.ts b/packages/phone-number-privacy/common/src/utils/key-version.ts deleted file mode 100644 index 07657663793..00000000000 --- a/packages/phone-number-privacy/common/src/utils/key-version.ts +++ /dev/null @@ -1,105 +0,0 @@ -import Logger from 'bunyan' -import { Request } from 'express' -import { Response as FetchResponse } from 'node-fetch' -import { ErrorMessage, KEY_VERSION_HEADER, OdisRequest, WarningMessage } from '..' - -export interface KeyVersionInfo { - keyVersion: number - threshold: number - polynomial: string - pubKey: string -} - -export function requestHasValidKeyVersion( - request: Request<{}, {}, OdisRequest>, - logger: Logger -): boolean { - try { - getRequestKeyVersion(request, logger) - return true - } catch (err) { - logger.debug('Error caught in requestHasValidKeyVersion') - logger.debug(err) - return false - } -} - -export function getRequestKeyVersion( - request: Request<{}, {}, OdisRequest>, - logger: Logger -): number | undefined { - const keyVersionHeader = request.headers[KEY_VERSION_HEADER] - const keyVersion = parseKeyVersionFromHeader(keyVersionHeader) - - if (keyVersion === undefined) { - return undefined - } - if (!isValidKeyVersion(keyVersion)) { - logger.error({ keyVersionHeader }, WarningMessage.INVALID_KEY_VERSION_REQUEST) - throw new Error(WarningMessage.INVALID_KEY_VERSION_REQUEST) - } - - logger.info({ keyVersion }, 'Request has valid key version') - return keyVersion -} - -export function responseHasExpectedKeyVersion( - response: FetchResponse, - expectedKeyVersion: number, - logger: Logger -): boolean { - let responseKeyVersion - try { - responseKeyVersion = getResponseKeyVersion(response, logger) - } catch (err) { - logger.debug('Error caught in responseHasExpectedKeyVersion') - logger.debug(err) - return false - } - - if (responseKeyVersion !== expectedKeyVersion) { - logger.error( - { expectedKeyVersion, responseKeyVersion }, - ErrorMessage.INVALID_KEY_VERSION_RESPONSE - ) - return false - } - - return true -} - -export function getResponseKeyVersion(response: FetchResponse, logger: Logger): number | undefined { - const keyVersionHeader = response.headers.get(KEY_VERSION_HEADER) - const keyVersion = parseKeyVersionFromHeader(keyVersionHeader) - - if (keyVersion === undefined) { - return undefined - } - if (!isValidKeyVersion(keyVersion)) { - logger.error({ keyVersionHeader }, ErrorMessage.INVALID_KEY_VERSION_RESPONSE) - throw new Error(ErrorMessage.INVALID_KEY_VERSION_RESPONSE) - } - - logger.info({ keyVersion }, 'Response has valid key version') - return keyVersion -} - -function parseKeyVersionFromHeader( - keyVersionHeader: string | string[] | undefined | null -): number | undefined { - if (keyVersionHeader === undefined || keyVersionHeader === null) { - return undefined - } - - const keyVersionHeaderString = keyVersionHeader.toString().trim() - - if (!keyVersionHeaderString.length) { - return undefined - } - - return Number(keyVersionHeaderString) -} - -function isValidKeyVersion(keyVersion: number): boolean { - return Number.isInteger(keyVersion) && keyVersion >= 0 -} diff --git a/packages/phone-number-privacy/common/src/utils/logger.ts b/packages/phone-number-privacy/common/src/utils/logger.ts deleted file mode 100644 index 0114aebb649..00000000000 --- a/packages/phone-number-privacy/common/src/utils/logger.ts +++ /dev/null @@ -1,62 +0,0 @@ -import Logger, { createLogger, levelFromName, LogLevelString, stdSerializers } from 'bunyan' -import bunyanDebugStream from 'bunyan-debug-stream' -import { createStream } from 'bunyan-gke-stackdriver' -import { NextFunction, Request, Response } from 'express' -import { WarningMessage } from '../interfaces/errors' -import { fetchEnvOrDefault } from './config.utils' - -let _rootLogger: Logger | undefined - -export function rootLogger(serviceName: string): Logger { - if (_rootLogger !== undefined) { - return _rootLogger - } - - const logLevel = fetchEnvOrDefault('LOG_LEVEL', 'info') as LogLevelString - const logFormat = fetchEnvOrDefault('LOG_FORMAT', 'human') - - let stream: any - switch (logFormat) { - case 'stackdriver': - stream = createStream(levelFromName[logLevel]) - break - case 'json': - stream = { stream: process.stdout, level: logLevel } - break - default: - stream = { level: logLevel, stream: bunyanDebugStream() } - break - } - - _rootLogger = createLogger({ - name: serviceName ?? '', - serializers: stdSerializers, - streams: [stream], - }) - return _rootLogger -} - -export function loggerMiddleware( - serviceName: string -): (req: Request, res: Response, next?: NextFunction) => Logger { - return (req, res, next) => { - const requestLogger = rootLogger(serviceName).child({ - endpoint: req.path, - sessionID: req.body.sessionID, // May be undefined - }) - res.locals.logger = requestLogger - - if (!req.body.sessionID && req.path !== '/metrics' && req.path !== '/status') { - requestLogger.info(WarningMessage.MISSING_SESSION_ID) - } - - if (next) { - next() - } - return requestLogger - } -} - -export function genSessionID() { - return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15) -} diff --git a/packages/phone-number-privacy/common/src/utils/responses.utils.ts b/packages/phone-number-privacy/common/src/utils/responses.utils.ts deleted file mode 100644 index 467acb1b42c..00000000000 --- a/packages/phone-number-privacy/common/src/utils/responses.utils.ts +++ /dev/null @@ -1,20 +0,0 @@ -import Logger from 'bunyan' -import { Response } from 'express' -import { OdisRequest, OdisResponse, WarningMessage } from '..' - -export function send< - I extends OdisRequest = OdisRequest, - O extends OdisResponse = OdisResponse ->(response: Response, body: O, status: number, logger: Logger) { - if (!body.success) { - if (body.error in WarningMessage) { - logger.warn({ error: body.error, status, body }, 'Responding with warning') - } else { - logger.error({ error: body.error, status, body }, 'Responding with error') - } - } else { - logger.info({ status, body }, 'Responding with success') - } - response.status(status).json(body) - logger.info('Completed send') -} diff --git a/packages/phone-number-privacy/common/test/domains.test.ts b/packages/phone-number-privacy/common/test/domains.test.ts deleted file mode 100644 index 814e0ced3dc..00000000000 --- a/packages/phone-number-privacy/common/test/domains.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { - defined, - EIP712Object, - generateTypedDataHash, - noBool, - noNumber, - noString, -} from '@celo/utils/lib/sign-typed-data-utils' -import { bufferToHex } from '@ethereumjs/util' -import { DomainIdentifiers } from '../src/domains/constants' -import { Domain, domainEIP712, DomainOptions } from '../src/domains/domains' -import { SequentialDelayDomain } from '../src/domains/sequential-delay' - -// Compile-time check that Domain can be cast to type EIP712Object -export const TEST_DOMAIN_IS_EIP712: EIP712Object = {} as unknown as Domain - -// Compile-time check that DomainOptions can be cast to type EIP712Object -export const TEST_DOMAIN_OPTIONS_ARE_EIP712: EIP712Object = {} as unknown as DomainOptions - -describe('domainEIP712()', () => { - it('should generate the correct type data for SequentialDelayDomain instance', () => { - const domain: SequentialDelayDomain = { - name: DomainIdentifiers.SequentialDelay, - version: '1', - stages: [ - { delay: 0, resetTimer: noBool, batchSize: defined(2), repetitions: noNumber }, - { delay: 1, resetTimer: defined(false), batchSize: noNumber, repetitions: noNumber }, - { delay: 1, resetTimer: defined(true), batchSize: noNumber, repetitions: noNumber }, - { delay: 2, resetTimer: defined(false), batchSize: noNumber, repetitions: defined(1) }, - { delay: 4, resetTimer: noBool, batchSize: defined(2), repetitions: defined(2) }, - ], - address: defined('0x0000000000000000000000000000000000000b0b'), - salt: noString, - } - const expectedHash = '0x966edacc6cdf76b4536da958e82e360213b957508767a393ccf5c6b73db241d1' - const typedData = domainEIP712(domain) - // console.debug(JSON.stringify(typedData, null, 2)) - expect(bufferToHex(generateTypedDataHash(typedData))).toEqual(expectedHash) - }) -}) diff --git a/packages/phone-number-privacy/common/test/interfaces/requests.test.ts b/packages/phone-number-privacy/common/test/interfaces/requests.test.ts deleted file mode 100644 index 6feb23ae586..00000000000 --- a/packages/phone-number-privacy/common/test/interfaces/requests.test.ts +++ /dev/null @@ -1,306 +0,0 @@ -import { - defined, - EIP712Object, - generateTypedDataHash, - noBool, - noNumber, - noString, -} from '@celo/utils/lib/sign-typed-data-utils' -import { LocalWallet } from '@celo/wallet-local' -import { bufferToHex } from '@ethereumjs/util' -import { - Domain, - DomainIdentifiers, - SequentialDelayDomain, - SequentialDelayDomainSchema, -} from '../../src/domains' -import { - DisableDomainRequest, - disableDomainRequestEIP712, - disableDomainRequestSchema, - DomainQuotaStatusRequest, - domainQuotaStatusRequestEIP712, - domainQuotaStatusRequestSchema, - DomainRequestTypeTag, - DomainRestrictedSignatureRequest, - domainRestrictedSignatureRequestEIP712, - domainRestrictedSignatureRequestSchema, - verifyDisableDomainRequestAuthenticity, - verifyDomainQuotaStatusRequestAuthenticity, - verifyDomainRestrictedSignatureRequestAuthenticity, -} from '../../src/interfaces/requests' - -// Compile-time check that DomainRestrictedSignatureRequest can be cast to type EIP712Object. -export let TEST_DOMAIN_RESTRICTED_SIGNATURE_REQUEST_IS_EIP712: EIP712Object -TEST_DOMAIN_RESTRICTED_SIGNATURE_REQUEST_IS_EIP712 = - {} as unknown as DomainRestrictedSignatureRequest -TEST_DOMAIN_RESTRICTED_SIGNATURE_REQUEST_IS_EIP712 = - {} as unknown as DomainRestrictedSignatureRequest - -// Compile-time check that DomainQuotaStatusRequest can be cast to type EIP712Object. -export let TEST_DOMAIN_QUOTA_STATUS_REQUEST_IS_EIP712: EIP712Object -TEST_DOMAIN_QUOTA_STATUS_REQUEST_IS_EIP712 = {} as unknown as DomainQuotaStatusRequest -TEST_DOMAIN_QUOTA_STATUS_REQUEST_IS_EIP712 = {} as unknown as DomainQuotaStatusRequest - -// Compile-time check that DomainQuotaStatusRequest can be cast to type EIP712Object. -export let TEST_DISABLE_DOMAIN_REQUEST_IS_EIP712: EIP712Object -TEST_DISABLE_DOMAIN_REQUEST_IS_EIP712 = {} as unknown as DisableDomainRequest -TEST_DISABLE_DOMAIN_REQUEST_IS_EIP712 = {} as unknown as DisableDomainRequest - -describe('domainRestrictedSignatureRequestEIP712()', () => { - it('should generate the correct type data for request with SequentialDelayDomain', () => { - const request: DomainRestrictedSignatureRequest = { - type: DomainRequestTypeTag.SIGN, - domain: { - name: DomainIdentifiers.SequentialDelay, - version: '1', - stages: [{ delay: 0, resetTimer: noBool, batchSize: defined(2), repetitions: defined(10) }], - address: defined('0x0000000000000000000000000000000000000b0b'), - salt: noString, - }, - options: { - signature: defined(''), - nonce: defined(1), - }, - blindedMessage: '', - sessionID: noString, - } - const expectedHash = '0x9914e6bc3bd0d63727eeae4008654920b9879654f7159b1d5ab33768e61f56df' - const typedData = domainRestrictedSignatureRequestEIP712(request) - // console.debug(JSON.stringify(typedData, null, 2)) - expect(bufferToHex(generateTypedDataHash(typedData))).toEqual(expectedHash) - }) -}) - -describe('domainQuotaStatusRequestEIP712()', () => { - it('should generate the correct type data for request with SequentialDelayDomain', () => { - const request: DomainQuotaStatusRequest = { - type: DomainRequestTypeTag.QUOTA, - domain: { - name: DomainIdentifiers.SequentialDelay, - version: '1', - stages: [{ delay: 0, resetTimer: noBool, batchSize: defined(2), repetitions: defined(10) }], - address: defined('0x0000000000000000000000000000000000000b0b'), - salt: noString, - }, - options: { - signature: defined(''), - nonce: defined(2), - }, - sessionID: noString, - } - const expectedHash = '0x0c1545b83f28d8d0f24886fa0d21ac540af706dd6f9ee6d045bac17780a2656e' - const typedData = domainQuotaStatusRequestEIP712(request) - //console.debug(JSON.stringify(typedData, null, 2)) - expect(bufferToHex(generateTypedDataHash(typedData))).toEqual(expectedHash) - }) -}) - -describe('disableDomainRequestEIP712()', () => { - it('should generate the correct type data for request with SequentialDelayDomain', () => { - const request: DisableDomainRequest = { - type: DomainRequestTypeTag.DISABLE, - domain: { - name: DomainIdentifiers.SequentialDelay, - version: '1', - stages: [{ delay: 0, resetTimer: noBool, batchSize: defined(2), repetitions: defined(10) }], - address: defined('0x0000000000000000000000000000000000000b0b'), - salt: noString, - }, - options: { - signature: defined(''), - nonce: defined(2), - }, - sessionID: noString, - } - const expectedHash = '0xd30be7d1b1bb3a9a0b2b2148d9ea3fcae7775dc31ce984d658f90295887a323a' - const typedData = disableDomainRequestEIP712(request) - console.debug(JSON.stringify(typedData, null, 2)) - expect(bufferToHex(generateTypedDataHash(typedData))).toEqual(expectedHash) - }) -}) - -const wallet = new LocalWallet() -wallet.addAccount('0x00000000000000000000000000000000000000000000000000000000deadbeef') -wallet.addAccount('0x00000000000000000000000000000000000000000000000000000000bad516e9') -const walletAddress = wallet.getAccounts()[0]! -const badAddress = wallet.getAccounts()[1]! - -const authenticatedDomain: SequentialDelayDomain = { - name: DomainIdentifiers.SequentialDelay, - version: '1', - stages: [{ delay: 0, resetTimer: noBool, batchSize: defined(2), repetitions: defined(10) }], - address: defined(walletAddress), - salt: noString, -} - -const unauthenticatedDomain: SequentialDelayDomain = { - name: DomainIdentifiers.SequentialDelay, - version: '1', - stages: [{ delay: 0, resetTimer: noBool, batchSize: defined(2), repetitions: defined(10) }], - address: noString, - salt: noString, -} - -const manipulatedDomain: SequentialDelayDomain = { - name: DomainIdentifiers.SequentialDelay, - version: '1', - stages: [{ delay: 0, resetTimer: noBool, batchSize: defined(100), repetitions: defined(10) }], - address: defined(walletAddress), - salt: noString, -} - -const signatureRequest: DomainRestrictedSignatureRequest = { - type: DomainRequestTypeTag.SIGN, - domain: authenticatedDomain, - options: { - signature: noString, - nonce: defined(0), - }, - blindedMessage: '', - sessionID: noString, -} - -const quotaRequest: DomainQuotaStatusRequest = { - type: DomainRequestTypeTag.QUOTA, - domain: authenticatedDomain, - options: { - signature: noString, - nonce: defined(0), - }, - sessionID: noString, -} - -const disableRequest: DisableDomainRequest = { - type: DomainRequestTypeTag.DISABLE, - domain: authenticatedDomain, - options: { - signature: noString, - nonce: defined(0), - }, - sessionID: noString, -} - -const verifyCases = [ - { - request: signatureRequest, - typedDataBuilder: domainRestrictedSignatureRequestEIP712, - verifier: verifyDomainRestrictedSignatureRequestAuthenticity, - name: 'verifyDomainRestrictedSignatureRequestAuthenticity()', - }, - { - request: quotaRequest, - typedDataBuilder: domainQuotaStatusRequestEIP712, - verifier: verifyDomainQuotaStatusRequestAuthenticity, - name: 'verifyDomainQuotaStatusRequestAuthenticity()', - }, - { - request: disableRequest, - typedDataBuilder: disableDomainRequestEIP712, - verifier: verifyDisableDomainRequestAuthenticity, - name: 'verifyDisableDomainRequestAuthenticity()', - }, -] - -for (const { request, verifier, typedDataBuilder, name } of verifyCases) { - describe(name, () => { - it('should report a correctly signed request as verified', async () => { - //@ts-ignore type checking does not correctly infer types. - const typedData = typedDataBuilder(request) - const signature = await wallet.signTypedData(walletAddress, typedData) - const signed = { - ...request, - options: { - ...request.options, - signature: defined(signature), - }, - } - - //@ts-ignore type checking does not correctly infer types. - expect(verifier(signed)).toBe(true) - }) - - it('should report an unsigned message as unverified', async () => { - //@ts-ignore type checking does not correctly infer types. - expect(verifier(request)).toBe(false) - }) - - it('should report a manipulated message as unverified', async () => { - //@ts-ignore type checking does not correctly infer types. - const typedData = typedDataBuilder(request) - const signature = await wallet.signTypedData(walletAddress, typedData) - const signed = { - ...request, - options: { - ...request.options, - signature: defined(signature), - }, - } - //@ts-ignore type checking does not correctly infer types. - expect(verifier(signed)).toBe(true) - - const manipulated = { ...request, domain: manipulatedDomain } - //@ts-ignore type checking does not correctly infer types. - expect(verifier(manipulated)).toBe(false) - }) - - it('should report an incorrectly signed request as unverified', async () => { - //@ts-ignore type checking does not correctly infer types. - const typedData = typedDataBuilder(request) - const signature = await wallet.signTypedData(badAddress, typedData) - const signed = { - ...request, - options: { - ...request.options, - signature: defined(signature), - }, - } - - //@ts-ignore type checking does not correctly infer types. - expect(verifier(signed)).toBe(false) - }) - - it('should report requests against unauthenticated domains to be unverified', async () => { - const unauthenticatedRequest = { - ...request, - domain: unauthenticatedDomain, - options: { - signature: noString, - nonce: noNumber, - }, - } - //@ts-ignore type checking does not correctly infer types. - expect(verifier(unauthenticatedRequest)).toBe(false) - }) - }) -} - -const schemaCases = [ - { - request: signatureRequest, - schema: domainRestrictedSignatureRequestSchema(SequentialDelayDomainSchema), - name: 'verifyDomainRestrictedSignatureRequestSignature()', - }, - { - request: quotaRequest, - schema: domainQuotaStatusRequestSchema(SequentialDelayDomainSchema), - name: 'verifyDomainQuotaStatusRequestSignature()', - }, - { - request: disableRequest, - schema: disableDomainRequestSchema(SequentialDelayDomainSchema), - name: 'verifyDisableDomainRequestSignature()', - }, -] - -for (const { request, schema, name } of schemaCases) { - describe(name, () => { - it('should report a correctly constructed request as validated', async () => { - expect(schema.is(request)).toBe(true) - }) - - it('should report an invalid request as not validated', async () => { - expect(schema.is({ ...request, options: {} })).toBe(false) - }) - }) -} diff --git a/packages/phone-number-privacy/common/test/poprf.test.ts b/packages/phone-number-privacy/common/test/poprf.test.ts deleted file mode 100644 index 474d324c8fa..00000000000 --- a/packages/phone-number-privacy/common/test/poprf.test.ts +++ /dev/null @@ -1,148 +0,0 @@ -import * as poprf from '@celo/poprf' -import { - PoprfClient, - PoprfCombiner, - PoprfServer, - ThresholdPoprfClient, - ThresholdPoprfServer, -} from '../src/poprf' - -const TEST_POPRF_KEYPAIR = poprf.keygen(Buffer.from('TEST POPRF KEYPAIR SEED')) -const TEST_THRESHOLD_N = 3 -const TEST_THRESHOLD_T = 2 -const TEST_THRESHOLD_POPRF_KEYS = poprf.thresholdKeygen( - TEST_THRESHOLD_N, - TEST_THRESHOLD_T, - Buffer.from('TEST THRESHOLD POPRF KEYPAIR SEED') -) - -const TEST_MESSAGE_A = Buffer.from('TEST MESSAGE A') -const TEST_MESSAGE_B = Buffer.from('TEST MESSAGE B') -const TEST_TAG_A = Buffer.from('TEST TAG A') -const TEST_TAG_B = Buffer.from('TEST TAG B') -const TEST_BLINDING_SEED = Buffer.from('TEST BLINDING SEED') - -describe('PoprfClient', () => { - it('results in a different blinding when called multiple times without a seed', () => { - const clientA = new PoprfClient(TEST_POPRF_KEYPAIR.publicKey, TEST_TAG_A, TEST_MESSAGE_A) - const clientB = new PoprfClient(TEST_POPRF_KEYPAIR.publicKey, TEST_TAG_A, TEST_MESSAGE_A) - expect(clientA.blindedMessage).not.toEqual(clientB.blindedMessage) - }) - - it('results in the same blinding when called multiple times with the same message and seed', () => { - const clientA = new PoprfClient( - TEST_POPRF_KEYPAIR.publicKey, - TEST_TAG_A, - TEST_MESSAGE_A, - TEST_BLINDING_SEED - ) - const clientB = new PoprfClient( - TEST_POPRF_KEYPAIR.publicKey, - TEST_TAG_A, - TEST_MESSAGE_A, - TEST_BLINDING_SEED - ) - expect(clientA.blindedMessage).toEqual(clientB.blindedMessage) - }) -}) - -describe('end-to-end', () => { - it('successfully completes client-server exchange', () => { - const server = new PoprfServer(TEST_POPRF_KEYPAIR.privateKey) - const client = new PoprfClient(TEST_POPRF_KEYPAIR.publicKey, TEST_TAG_A, TEST_MESSAGE_A) - - const response = server.blindEval(client.tag, client.blindedMessage) - const evaluation = client.unblindResponse(response) - - // POPRF hashed outputs should be 32 bytes. - expect(evaluation.length).toEqual(32) - expect(evaluation.toString('base64')).toEqual('Oh4FGO2zJ/jZDLkpW4LJk3xr5RdIHg0mYuGg2/b44+s=') - }) - - it('client rejects a exchange when the server uses the wrong key', () => { - const badPrivateKey = poprf.keygen(Buffer.from('BAD POPRF KEYPAIR SEED')).privateKey - const server = new PoprfServer(badPrivateKey) - const client = new PoprfClient(TEST_POPRF_KEYPAIR.publicKey, TEST_TAG_A, TEST_MESSAGE_A) - - const response = server.blindEval(client.tag, client.blindedMessage) - expect(() => client.unblindResponse(response)).toThrow(/verification failed/) - }) - - it('client rejects a exchange when the server uses the wrong tag', () => { - const server = new PoprfServer(TEST_POPRF_KEYPAIR.privateKey) - const client = new PoprfClient(TEST_POPRF_KEYPAIR.publicKey, TEST_TAG_A, TEST_MESSAGE_A) - - const response = server.blindEval(TEST_TAG_B, client.blindedMessage) - expect(() => client.unblindResponse(response)).toThrow(/verification failed/) - }) - - it('client rejects a exchange when the server uses the wrong message', () => { - const server = new PoprfServer(TEST_POPRF_KEYPAIR.privateKey) - const client = new PoprfClient(TEST_POPRF_KEYPAIR.publicKey, TEST_TAG_A, TEST_MESSAGE_A) - - const badMessage = new PoprfClient(TEST_POPRF_KEYPAIR.publicKey, TEST_TAG_A, TEST_MESSAGE_B) - .blindedMessage - - const response = server.blindEval(TEST_TAG_A, badMessage) - expect(() => client.unblindResponse(response)).toThrow(/verification failed/) - }) - - it('successfully completes client-server exchange with combiner', () => { - const servers = [...Array(TEST_THRESHOLD_N).keys()].map( - (i) => new ThresholdPoprfServer(TEST_THRESHOLD_POPRF_KEYS.getShare(i)) - ) - const combiner = new PoprfCombiner(TEST_THRESHOLD_T) - const client = new PoprfClient( - TEST_THRESHOLD_POPRF_KEYS.thresholdPublicKey, - TEST_TAG_A, - TEST_MESSAGE_A - ) - - const blindedPartials = servers.map((s) => - s.blindPartialEval(client.tag, client.blindedMessage) - ) - const response = combiner.blindAggregate(blindedPartials) - expect(response).toBeDefined() - if (response === undefined) { - throw new Error('response is undefined') - } - const evaluation = client.unblindResponse(response) - - // POPRF hashed outputs should be 32 bytes. - expect(evaluation.length).toEqual(32) - expect(evaluation.toString('base64')).toEqual('C1jKGStMWC3lNpYDV61D+3waetY0bHlD4ElYzV+Isqc=') - }) - - it('successfully completes client-server exchange with threshold client and server', () => { - const servers = [...Array(TEST_THRESHOLD_N).keys()].map( - (i) => new ThresholdPoprfServer(TEST_THRESHOLD_POPRF_KEYS.getShare(i)) - ) - const client = new ThresholdPoprfClient( - TEST_THRESHOLD_POPRF_KEYS.thresholdPublicKey, - TEST_THRESHOLD_POPRF_KEYS.polynomial, - TEST_TAG_A, - TEST_MESSAGE_A - ) - - const blindedPartials = servers.map((s) => - s.blindPartialEval(client.tag, client.blindedMessage) - ) - const partials = blindedPartials.map((r) => client.unblindPartialResponse(r)) - expect(partials.map((p) => p.toString('base64'))).toEqual([ - 'AQAAALCjiT0dQp/OCz5OOXNmAwOV6waen+w9hgZsefHbCXPENhQv1kPK/aSSRbUqWHGDAFaN2IDxT+bnP6bfVjEvzMs1zZtLJZXcGTauucpGx1ESGc8TqKutLIKnGg4iKp0qAH6J7os1L11ARrqv3gSMt0KBD6dd8pK8/4bkx219wkLFu3SRM0/DYAUrgNkDnTGRAH88fDHRcLwesFo65hux4Owq92C6wqJTnsLGxYOoEm7tM8Eycx2M/eHS1QhyNC5NAZFrtacLSp3GHA46CD5oxsZ9zeAWjv0pCJA8tp2gLdHyjJW0czaTDY8EEn5evr9UAB0+sC5ELC0ljNchw0otkWuOEUvV3V3ygQId6/r/s7gMDeCw3MacTT2HRyB2W41oAG6h7rlnIIEdtIQR/P3JAUcf7dTv5HN65LgkKk+4jh0azqsQFYgKUL776F9dsz+pAeFFyND44saFuJf4uK3KeTspY5EWOPUK2o9oU86z0OPvBlkJl6o6GUz2OzFr4AmYAN0IYZy9cd6SGrEUpLIBFe2iFxGRuzMK100USPDrM8MkawNTsOla1j9OdgBilhZsAUJP00Rhrx8zfx0olc2Hk29ZK3MhdJ++x94W+z1KMMDu+/5us6uCzUU6rngt1mDgAC4Y2bv8xwKYePddddCQ2NSnzKTHt39ju9T73hr575IlXibX9/kNwILmrpf4iwhQANMbwK4IG9dMG7iLNjfy4efOENAkRF3gQ5+WWg3gRKsLo74672nJPmmKsEpPW4Y2AQ==', - 'AgAAAA94+5GfNTTztPtB8asRkd+U9ycdzhllEPPyeuwhGpFyXUruFEs4wsbLCI6sQHntAEYs52/06QnyTDSNAFhjaaZio4+OgmxWhkdcqc9HmjRSWWnGSfgEqAMPYgCBh7sJAezrKTlMkqkzPCoq0HND9CYpyMQ/6o+r4wteRapcDhDCltkRLu9KxPkBMqf5/VvNAL6XRUFAWNpi4MUKNnp8D3vIiVS5tlFuSa5BX3VzJO9CXLR16bXIgLkR55Lwz5qkAXkhKsGPWGinmKVRQw4s5CySg6ERS0RN/Q54dBXVhvXNkEyZ+e1aaO1sonUsSYteAMR6TmdGpxEuXQirqKRuoj5p8DxWI5qA3Jq+QhbdIGP091gK9oJYG3oIUsPKuGAvAfdZOA/a3S7woEWhXj6FkBTiIqP/CJvTO7wd7rMDvSlrn7Qg4sfnqtEVhklDgPHpAH9xiw7OYO9n6V54+KMK9BriSqkmek/PR5BcWJmwdK1M6kDtQ49z5DM/ZZoM0GnIAKt7Id8WL+Q0OTjk1U4Y4eGf+wtlSU1V3SqVYibXfjs3bCwfm2osZ5q1O1yLcxZkAdnxKoQuF09wmPKQIUwV6D6+AJLAQTbAwkuYjOH7o5zsDvYLf6q08dfJQtT5Vxd2AQMwsfFBOVtCp+xoMkSrqknFIJsJEfeqLqup0C86G3Uq7RR70ou9MBe0fMgK1H4lAM6/Ve7sfjYxbwwndWMBhhzrb6QS1KY1LFzP12aBHyL3BkyluQhzoKEZxsfRB34fAA==', - 'AwAAAHxYwuz82TqEoRNqAA//f2NWDBBfN44NNP+Uxemrxi9Soe2EfRg7hwJXKeD4UU/OABySagQyLgF7Oy2iAzSxITB2ja0BUycKZVcQwu641PodT6D448t6M78Z5ISP2t+fASemh30SvQuIcdCGag2Wn8C8mb5TOJBOxSO2ybxtpp8RbBxvL+YASTMy3uF/y6ysAcfISp9zYDwo9nB2eeXOM+fwlTznGL1h+VWbZ497Uw0nR6o1UR53gZlraVVy+U/+AKW5R5PIr+2daV/B0WidOyJpIsOqj4tZ3ul2uDjEP1aDn5y2aNflmsPxQ6NN1BwzAQ1CyRH6iZzVp3/huJNoUJQknWCnaIaF34CpP6ajdKx6ts/Z+SBLaN25PFyK/KlIAKzQQkNPhJ+kLTODpDhbjtbsCDVknPYizswljLuFKp7mTW0pQhJ+GMEVbqdV+j/PAAiPCnBVC+1Up9cK0FSxJq6X9/vUNd10TsZn1tVIcVzj3lE1HNqPHnHnHqLR+pKMAPOKzFQfG8j05WGZDJjWfHxWOr5XL8zwg3Y0O4+WVcOT9AfL8Tu5djvZop/JCq42ARfnz/dI8VWucQ41A39oW06UJYnahWSxKWShhXnWe9/CQHqJb6nlRLJkkmLXbDP9ALZs/xmpVmL+UuIHCrIEqtL5JYdBOrclIxl+IT8MOHnQT58HB+D4i5ATfZ1qhCOgALgOKPPHxuFqmr6fUloSXNqZoEo/RzQqSC1IpubDIM2je2hJu5zPAPP8+OKn/ndrAQ==', - ]) - - const combiner = new PoprfCombiner(TEST_THRESHOLD_T) - const evaluation = combiner.aggregate(partials) - expect(evaluation).toBeDefined() - if (evaluation === undefined) { - throw new Error('evaluation is undefined') - } - - // POPRF hashed outputs should be 32 bytes. - expect(evaluation.length).toEqual(32) - expect(evaluation.toString('base64')).toEqual('C1jKGStMWC3lNpYDV61D+3waetY0bHlD4ElYzV+Isqc=') - }) -}) diff --git a/packages/phone-number-privacy/common/test/utils/authentication.test.ts b/packages/phone-number-privacy/common/test/utils/authentication.test.ts deleted file mode 100644 index 4e129b6fbd5..00000000000 --- a/packages/phone-number-privacy/common/test/utils/authentication.test.ts +++ /dev/null @@ -1,443 +0,0 @@ -import { hexToBuffer } from '@celo/base' -import { ContractKit } from '@celo/contractkit' -import Logger from 'bunyan' -import { Request } from 'express' -import { ErrorMessage, ErrorType } from '../../lib' -import { AuthenticationMethod } from '../../src/interfaces/requests' -import * as auth from '../../src/utils/authentication' - -describe('Authentication test suite', () => { - const logger = Logger.createLogger({ - name: 'logger', - level: 'warn', - }) - - describe('authenticateUser utility', () => { - it("Should fail authentication with missing 'Authorization' header", async () => { - const sampleRequest: Request = { - get: (_: string) => '', - body: { - account: '0xc1912fee45d61c87cc5ea59dae31190fffff232d', - }, - } as Request - const mockContractKit = {} as ContractKit - - const warnings: ErrorType[] = [] - - const success = await auth.authenticateUser( - sampleRequest, - mockContractKit, - logger, - true, - warnings - ) - - expect(success).toBe(false) - expect(warnings).toEqual([]) - }) - - it('Should fail authentication with missing signer', async () => { - const sampleRequest: Request = { - get: (name: string) => (name === 'Authorization' ? 'Test' : ''), - body: {}, - } as Request - const mockContractKit = {} as ContractKit - - const warnings: ErrorType[] = [] - - const success = await auth.authenticateUser( - sampleRequest, - mockContractKit, - logger, - true, - warnings - ) - - expect(success).toBe(false) - expect(warnings).toEqual([]) - }) - - it('Should succeed authentication with error in getDataEncryptionKey when shouldFailOpen is true', async () => { - const sampleRequest: Request = { - get: (name: string) => (name === 'Authorization' ? 'Test' : ''), - body: { - account: '0xc1912fee45d61c87cc5ea59dae31190fffff232d', - authenticationMethod: AuthenticationMethod.ENCRYPTION_KEY, - }, - } as Request - const mockContractKit = {} as ContractKit - - const warnings: ErrorType[] = [] - - const success = await auth.authenticateUser( - sampleRequest, - mockContractKit, - logger, - true, - warnings - ) - - expect(success).toBe(true) - expect(warnings).toEqual([ErrorMessage.FAILURE_TO_GET_DEK, ErrorMessage.FAILING_OPEN]) - }) - - it('Should fail authentication with error in getDataEncryptionKey when shouldFailOpen is false', async () => { - const sampleRequest: Request = { - get: (name: string) => (name === 'Authorization' ? 'Test' : ''), - body: { - account: '0xc1912fee45d61c87cc5ea59dae31190fffff232d', - authenticationMethod: AuthenticationMethod.ENCRYPTION_KEY, - }, - } as Request - const mockContractKit = {} as ContractKit - - const warnings: ErrorType[] = [] - - const success = await auth.authenticateUser( - sampleRequest, - mockContractKit, - logger, - false, - warnings - ) - - expect(success).toBe(false) - expect(warnings).toEqual([ErrorMessage.FAILURE_TO_GET_DEK, ErrorMessage.FAILING_CLOSED]) - }) - - it('Should fail authentication when key is not registered', async () => { - const sampleRequest: Request = { - get: (name: string) => (name === 'Authorization' ? 'Test' : ''), - body: { - account: '0xc1912fee45d61c87cc5ea59dae31190fffff232d', - authenticationMethod: AuthenticationMethod.ENCRYPTION_KEY, - }, - } as Request - const mockContractKit = { - contracts: { - getAccounts: async () => { - return Promise.resolve({ - getDataEncryptionKey: async (_: string) => { - return '' - }, - }) - }, - }, - } as ContractKit - - const warnings: ErrorType[] = [] - - const success = await auth.authenticateUser( - sampleRequest, - mockContractKit, - logger, - true, - warnings - ) - - expect(success).toBe(false) - expect(warnings).toEqual([]) - }) - - it('Should fail authentication when key is registered but not valid', async () => { - const sampleRequest: Request = { - get: (name: string) => (name === 'Authorization' ? 'Test' : ''), - body: { - account: '0xc1912fee45d61c87cc5ea59dae31190fffff232d', - authenticationMethod: AuthenticationMethod.ENCRYPTION_KEY, - }, - } as Request - const mockContractKit = { - contracts: { - getAccounts: async () => { - return Promise.resolve({ - getDataEncryptionKey: async (_: string) => { - return 'notAValidKeyEncryption' - }, - }) - }, - }, - } as ContractKit - - const warnings: ErrorType[] = [] - - const success = await auth.authenticateUser(sampleRequest, mockContractKit, logger) - - expect(success).toBe(false) - expect(warnings).toEqual([]) - }) - - it('Should succeed authentication when key is registered and valid', async () => { - const rawKey = '41e8e8593108eeedcbded883b8af34d2f028710355c57f4c10a056b72486aa04' - const body = { - account: '0xc1912fee45d61c87cc5ea59dae31190fffff232d', - authenticationMethod: AuthenticationMethod.ENCRYPTION_KEY, - } - const sig = auth.signWithRawKey(JSON.stringify(body), rawKey) - const sampleRequest: Request = { - get: (name: string) => (name === 'Authorization' ? sig : ''), - body, - } as Request - const mockContractKit = { - contracts: { - getAccounts: async () => { - return Promise.resolve({ - getDataEncryptionKey: async (_: string) => { - // NOTE: elliptic is disabled elsewhere in this library to prevent - // accidental signing of truncated messages. - // tslint:disable-next-line:import-blacklist - const EC = require('elliptic').ec - const ec = new EC('secp256k1') - const key = ec.keyFromPrivate(hexToBuffer(rawKey)) - return key.getPublic(true, 'hex') - }, - }) - }, - }, - } as ContractKit - - const warnings: ErrorType[] = [] - - const success = await auth.authenticateUser( - sampleRequest, - mockContractKit, - logger, - true, - warnings - ) - - expect(success).toBe(true) - expect(warnings).toEqual([]) - }) - - it('Should fail authentication when the message is manipulated', async () => { - const rawKey = '41e8e8593108eeedcbded883b8af34d2f028710355c57f4c10a056b72486aa04' - const body = { - account: '0xc1912fee45d61c87cc5ea59dae31190fffff232d', - authenticationMethod: AuthenticationMethod.ENCRYPTION_KEY, - } - const message = JSON.stringify(body) - - // Modify every fourth character and check that the signature becomes invalid. - for (let i = 0; i < message.length; i += 4) { - const modified = - message.slice(0, i) + - String.fromCharCode(message.charCodeAt(i) + 1) + - message.slice(i + 1) - const sig = auth.signWithRawKey(modified, rawKey) - const sampleRequest: Request = { - get: (name: string) => (name === 'Authorization' ? sig : ''), - body, - } as Request - const mockContractKit = { - contracts: { - getAccounts: async () => { - return Promise.resolve({ - getDataEncryptionKey: async (_: string) => { - // NOTE: elliptic is disabled elsewhere in this library to prevent - // accidental signing of truncated messages. - // tslint:disable-next-line:import-blacklist - const EC = require('elliptic').ec - const ec = new EC('secp256k1') - const key = ec.keyFromPrivate(hexToBuffer(rawKey)) - return key.getPublic(true, 'hex') - }, - }) - }, - }, - } as ContractKit - - const warnings: ErrorType[] = [] - - const success = await auth.authenticateUser( - sampleRequest, - mockContractKit, - logger, - true, - warnings - ) - - expect(success).toBe(false) - expect(warnings).toEqual([]) - } - }) - - it('Should fail authentication when the key is incorrect', async () => { - const rawKey = '41e8e8593108eeedcbded883b8af34d2f028710355c57f4c10a056b72486aa04' - const body = { - account: '0xc1912fee45d61c87cc5ea59dae31190fffff232d', - authenticationMethod: AuthenticationMethod.ENCRYPTION_KEY, - } - const sig = auth.signWithRawKey(JSON.stringify(body), rawKey) - const sampleRequest: Request = { - get: (name: string) => (name === 'Authorization' ? sig : ''), - body, - } as Request - - const mockContractKit = { - contracts: { - getAccounts: async () => { - return Promise.resolve({ - getDataEncryptionKey: async (_: string) => { - // NOTE: elliptic is disabled elsewhere in this library to prevent - // accidental signing of truncated messages. - // tslint:disable-next-line:import-blacklist - const EC = require('elliptic').ec - const ec = new EC('secp256k1') - // Send back a manipulated key. - const key = ec.keyFromPrivate(hexToBuffer('a' + rawKey.slice(1))) - return key.getPublic(true, 'hex') - }, - }) - }, - }, - } as ContractKit - - const warnings: ErrorType[] = [] - - const success = await auth.authenticateUser( - sampleRequest, - mockContractKit, - logger, - true, - warnings - ) - - expect(success).toBe(false) - expect(warnings).toEqual([]) - }) - - it('Should fail authentication when the sigature is modified', async () => { - const rawKey = '41e8e8593108eeedcbded883b8af34d2f028710355c57f4c10a056b72486aa04' - const body = { - account: '0xc1912fee45d61c87cc5ea59dae31190fffff232d', - authenticationMethod: AuthenticationMethod.ENCRYPTION_KEY, - } - // Manipulate the signature. - const sig = auth.signWithRawKey(JSON.stringify(body), rawKey) - const modified = JSON.stringify([0] + JSON.parse(sig)) - const sampleRequest: Request = { - get: (name: string) => (name === 'Authorization' ? modified : ''), - body, - } as Request - - const mockContractKit = { - contracts: { - getAccounts: async () => { - return Promise.resolve({ - getDataEncryptionKey: async (_: string) => { - // NOTE: elliptic is disabled elsewhere in this library to prevent - // accidental signing of truncated messages. - // tslint:disable-next-line:import-blacklist - const EC = require('elliptic').ec - const ec = new EC('secp256k1') - // Send back a manipulated key. - const key = ec.keyFromPrivate(hexToBuffer(rawKey)) - return key.getPublic(true, 'hex') - }, - }) - }, - }, - } as ContractKit - - const warnings: ErrorType[] = [] - - const success = await auth.authenticateUser( - sampleRequest, - mockContractKit, - logger, - true, - warnings - ) - - expect(success).toBe(false) - expect(warnings).toEqual([]) - }) - - it('Should fail authentication when key is registered and valid and signature is incorrectly generated', async () => { - const rawKey = '41e8e8593108eeedcbded883b8af34d2f028710355c57f4c10a056b72486aa04' - const body = { - account: '0xc1912fee45d61c87cc5ea59dae31190fffff232d', - authenticationMethod: AuthenticationMethod.ENCRYPTION_KEY, - } - // NOTE: elliptic is disabled elsewhere in this library to prevent - // accidental signing of truncated messages. - // tslint:disable-next-line:import-blacklist - const EC = require('elliptic').ec - const ec = new EC('secp256k1') - const key = ec.keyFromPrivate(hexToBuffer(rawKey)) - const sig = JSON.stringify(key.sign(JSON.stringify(body)).toDER()) - - const sampleRequest: Request = { - get: (name: string) => (name === 'Authorization' ? sig : ''), - body, - } as Request - const mockContractKit = { - contracts: { - getAccounts: async () => { - return Promise.resolve({ - getDataEncryptionKey: async (_: string) => { - return key.getPublic(true, 'hex') - }, - }) - }, - }, - } as ContractKit - - const warnings: ErrorType[] = [] - - const success = await auth.authenticateUser( - sampleRequest, - mockContractKit, - logger, - true, - warnings - ) - - expect(success).toBe(false) - expect(warnings).toEqual([]) - }) - }) - - describe('isVerified utility', () => { - it('Should succeed when verification is ok', async () => { - const mockContractKit = { - contracts: { - getAttestations: async () => { - return { - getVerifiedStatus: async (_: string, __: string) => { - return { - isVerified: true, - } - }, - } - }, - }, - } as ContractKit - - const result = await auth.isVerified('', '', mockContractKit, logger) - - expect(result).toBe(true) - }) - - it('Should fail when verification is not ok', async () => { - const mockContractKit = { - contracts: { - getAttestations: async () => { - return { - getVerifiedStatus: async (_: string, __: string) => { - return { - isVerified: false, - } - }, - } - }, - }, - } as ContractKit - - const result = await auth.isVerified('', '', mockContractKit, logger) - - expect(result).toBe(false) - }) - }) -}) diff --git a/packages/phone-number-privacy/common/test/utils/input-validation.test.ts b/packages/phone-number-privacy/common/test/utils/input-validation.test.ts deleted file mode 100644 index 1ec3ba1fd53..00000000000 --- a/packages/phone-number-privacy/common/test/utils/input-validation.test.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { GetQuotaRequest, LegacySignMessageRequest } from '../../src/interfaces' -import { REASONABLE_BODY_CHAR_LIMIT } from '../../src/utils/constants' -import * as utils from '../../src/utils/input-validation' - -describe('Input Validation test suite', () => { - describe('isBodyReasonablySized utility', () => { - it('Should return true with small body', () => { - const sampleData: GetQuotaRequest = { - account: 'account', - hashedPhoneNumber: 'x'.repeat(10), - } - - const result = utils.isBodyReasonablySized(sampleData) - - expect(result).toBeTruthy() - }) - - it('Should return false with giant body', () => { - const sampleData: GetQuotaRequest = { - account: 'account', - hashedPhoneNumber: 'x'.repeat(REASONABLE_BODY_CHAR_LIMIT * 2), - } - - const result = utils.isBodyReasonablySized(sampleData) - - expect(result).toBeFalsy() - }) - }) - - describe('hasValidAccountParam utility', () => { - it('Should return true for proper address', () => { - const sampleData = { - account: '0xc1912fee45d61c87cc5ea59dae31190fffff232d', - } - - const result = utils.hasValidAccountParam(sampleData) - - expect(result).toBeTruthy() - }) - - it('Should return false for nonsense address', () => { - const sampleData = { - account: '0xAA', - } - - const result = utils.hasValidAccountParam(sampleData) - - expect(result).toBeFalsy() - }) - - it('Should return false with missing address', () => { - const sampleData = { - account: '', - } - - const result = utils.hasValidAccountParam(sampleData) - - expect(result).toBeFalsy() - }) - }) - - describe('hasValidBlindedPhoneNumberParam utility', () => { - it('Should return true for blinded query', () => { - const sampleData: LegacySignMessageRequest = { - blindedQueryPhoneNumber: Buffer.from( - '1912fee45d61c87cc5ea59dae31190ff1912fee45d61c8' - ).toString('base64'), - account: 'acc', - } - - const result = utils.hasValidBlindedPhoneNumberParam(sampleData) - - expect(result).toBeTruthy() - }) - - it('Should return false for not base64 query', () => { - const sampleData: LegacySignMessageRequest = { - blindedQueryPhoneNumber: Buffer.from( - 'JanAdamMickiewicz1234!@JanAdamMickiewicz1234!@123412345678901234' - ).toString('utf-8'), - account: 'acc', - } - - const result = utils.hasValidBlindedPhoneNumberParam(sampleData) - - expect(result).toBeFalsy() - }) - - it('Should return false for too short blinded query', () => { - const sampleData: LegacySignMessageRequest = { - blindedQueryPhoneNumber: Buffer.from('1912fee45d61c87cc5e').toString('base64'), - account: 'acc', - } - - const result = utils.hasValidBlindedPhoneNumberParam(sampleData) - - expect(result).toBeFalsy() - }) - - it('Should return false for missing param in query', () => { - const sampleData: LegacySignMessageRequest = { - blindedQueryPhoneNumber: '', - account: 'acc', - } - - const result = utils.hasValidBlindedPhoneNumberParam(sampleData) - - expect(result).toBeFalsy() - }) - }) -}) diff --git a/packages/phone-number-privacy/common/test/utils/key-version.test.ts b/packages/phone-number-privacy/common/test/utils/key-version.test.ts deleted file mode 100644 index 46db29d71e4..00000000000 --- a/packages/phone-number-privacy/common/test/utils/key-version.test.ts +++ /dev/null @@ -1,228 +0,0 @@ -import { Request } from 'express' -import { Response as FetchResponse } from 'node-fetch' -import { - ErrorMessage, - getRequestKeyVersion, - getResponseKeyVersion, - KEY_VERSION_HEADER, - requestHasValidKeyVersion, - responseHasExpectedKeyVersion, - rootLogger, - WarningMessage, -} from '../../src' - -describe('key version test suite', () => { - const logger = rootLogger('key version test suite') - - const request = { - headers: {}, - } as Request - - let response: FetchResponse - - const invalidKeyVersionHeaders: (string | string[])[] = [ - 'a', - '-1', - '1.5', - '1a', - 'blah', - 'one', - '-', - '+', - ['1', '2', '3'], - ' . ', - ] - - beforeEach(() => { - delete request.headers[KEY_VERSION_HEADER] - response = new FetchResponse() - }) - - describe(getRequestKeyVersion, () => { - it(`Should return undefined if key version header has not been set`, () => { - const res = getRequestKeyVersion(request, logger) - expect(res).toBe(undefined) - }) - - it(`Should return undefined if key version header is undefined`, () => { - request.headers[KEY_VERSION_HEADER] = undefined - const res = getRequestKeyVersion(request, logger) - expect(res).toBe(undefined) - }) - - it(`Should return undefined if key version header is empty`, () => { - request.headers[KEY_VERSION_HEADER] = '' - const res = getRequestKeyVersion(request, logger) - expect(res).toBe(undefined) - }) - - it(`Should return undefined if key version header is whitespace`, () => { - request.headers[KEY_VERSION_HEADER] = ' ' - const res = getRequestKeyVersion(request, logger) - expect(res).toBe(undefined) - }) - - for (let kv = 0; kv <= 10; kv++) { - it(`Should return valid key version header ${kv}`, () => { - request.headers[KEY_VERSION_HEADER] = kv.toString() - const res = getRequestKeyVersion(request, logger) - expect(res).toBe(kv) - }) - } - - it(`Should return valid key version header when there's whitespace`, () => { - request.headers[KEY_VERSION_HEADER] = ' 1 ' - const res = getRequestKeyVersion(request, logger) - expect(res).toBe(1) - }) - - invalidKeyVersionHeaders.forEach((kv) => { - it(`Should throw for invalid key version ${kv}`, () => { - request.headers[KEY_VERSION_HEADER] = kv.toString() - expect(() => getRequestKeyVersion(request, logger)).toThrow( - WarningMessage.INVALID_KEY_VERSION_REQUEST - ) - }) - }) - }) - - describe(requestHasValidKeyVersion, () => { - it(`Should return true if key version header has not been set`, () => { - const res = requestHasValidKeyVersion(request, logger) - expect(res).toBe(true) - }) - it(`Should return true if key version header is undefined`, () => { - request.headers[KEY_VERSION_HEADER] = undefined - const res = requestHasValidKeyVersion(request, logger) - expect(res).toBe(true) - }) - it(`Should return true if key version header is empty`, () => { - request.headers[KEY_VERSION_HEADER] = '' - const res = requestHasValidKeyVersion(request, logger) - expect(res).toBe(true) - }) - - it(`Should return true if key version header is whitespace`, () => { - request.headers[KEY_VERSION_HEADER] = ' ' - const res = requestHasValidKeyVersion(request, logger) - expect(res).toBe(true) - }) - - for (let kv = 0; kv <= 10; kv++) { - it(`Should return true for valid key version header ${kv}`, () => { - request.headers[KEY_VERSION_HEADER] = kv.toString() - const res = requestHasValidKeyVersion(request, logger) - expect(res).toBe(true) - }) - } - - it(`Should return true for valid key version header when there's whitespace`, () => { - request.headers[KEY_VERSION_HEADER] = ' 1 ' - const res = requestHasValidKeyVersion(request, logger) - expect(res).toBe(true) - }) - - invalidKeyVersionHeaders.forEach((kv) => { - it(`Should return false for invalid key version ${kv}`, () => { - request.headers[KEY_VERSION_HEADER] = kv.toString() - const res = requestHasValidKeyVersion(request, logger) - expect(res).toBe(false) - }) - }) - }) - - describe(getResponseKeyVersion, () => { - it(`Should return undefined if key version header has not been set`, () => { - const res = getResponseKeyVersion(response, logger) - expect(res).toBe(undefined) - }) - it(`Should return undefined if key version header is undefined`, () => { - response.headers.delete(KEY_VERSION_HEADER) - const res = getResponseKeyVersion(response, logger) - expect(res).toBe(undefined) - }) - - it(`Should return undefined if key version header is empty`, () => { - response.headers.set(KEY_VERSION_HEADER, '') - const res = getResponseKeyVersion(response, logger) - expect(res).toBe(undefined) - }) - - it(`Should return undefined if key version header is whitespace`, () => { - response.headers.set(KEY_VERSION_HEADER, ' ') - const res = getResponseKeyVersion(response, logger) - expect(res).toBe(undefined) - }) - - for (let kv = 0; kv <= 10; kv++) { - it(`Should return valid key version header ${kv}`, () => { - response.headers.set(KEY_VERSION_HEADER, kv.toString()) - const res = getResponseKeyVersion(response, logger) - expect(res).toBe(kv) - }) - } - - it(`Should return valid key version header when there's whitespace`, () => { - response.headers.set(KEY_VERSION_HEADER, ' 1 ') - const res = getResponseKeyVersion(response, logger) - expect(res).toBe(1) - }) - - invalidKeyVersionHeaders.forEach((kv) => { - it(`Should throw for invalid key version ${kv}`, () => { - response.headers.set(KEY_VERSION_HEADER, kv.toString()) - expect(() => getResponseKeyVersion(response, logger)).toThrow( - ErrorMessage.INVALID_KEY_VERSION_RESPONSE - ) - }) - }) - }) - - describe(responseHasExpectedKeyVersion, () => { - const testCases = [ - { - responseKeyVersion: 1, - expectedKeyVersion: 1, - expectedResult: true, - }, - { - responseKeyVersion: 2, - expectedKeyVersion: 1, - expectedResult: false, - }, - { - responseKeyVersion: undefined, - expectedKeyVersion: 1, - expectedResult: false, - }, - { - responseKeyVersion: -1, - expectedKeyVersion: -1, - expectedResult: false, - }, - { - responseKeyVersion: 1.5, - expectedKeyVersion: 1.5, - expectedResult: false, - }, - { - responseKeyVersion: 'a', - expectedKeyVersion: Number('a'), - expectedResult: false, - }, - ] - - testCases.forEach((testCase) => { - it(JSON.stringify(testCase), () => { - const { responseKeyVersion, expectedKeyVersion, expectedResult } = testCase - if (responseKeyVersion === undefined) { - response.headers.delete(KEY_VERSION_HEADER) - } else { - response.headers.set(KEY_VERSION_HEADER, responseKeyVersion.toString()) - } - const res = responseHasExpectedKeyVersion(response, expectedKeyVersion, logger) - expect(res).toBe(expectedResult) - }) - }) - }) -}) diff --git a/packages/phone-number-privacy/common/test/utils/sequential-delay.test.ts b/packages/phone-number-privacy/common/test/utils/sequential-delay.test.ts deleted file mode 100644 index decd72f9de8..00000000000 --- a/packages/phone-number-privacy/common/test/utils/sequential-delay.test.ts +++ /dev/null @@ -1,327 +0,0 @@ -import { defined, noBool, noNumber, noString } from '@celo/utils/lib/sign-typed-data-utils' -import { - checkSequentialDelayRateLimit, - DomainIdentifiers, - SequentialDelayDomain, - SequentialDelayResult, -} from '../../src/domains' - -type TestAttempt = { - timestamp: number - expectedResult: SequentialDelayResult -} - -describe('Sequential Delay Test Suite', () => { - const checkTestAttempts = (t: number, domain: SequentialDelayDomain, attempts: TestAttempt[]) => { - let result: SequentialDelayResult | undefined - for (const attempt of attempts) { - console.log(result) - console.log(`t + ${attempt.timestamp - t}`) - result = checkSequentialDelayRateLimit(domain, attempt.timestamp, result?.state) - expect(result).toEqual(attempt.expectedResult) - } - } - - describe('checkSequentialDelayRateLimit', () => { - it('should not accept attempts until initial delay', () => { - const t = 0 // initial delay - - const domain: SequentialDelayDomain = { - name: DomainIdentifiers.SequentialDelay, - version: '1', - stages: [{ delay: t, resetTimer: noBool, batchSize: noNumber, repetitions: noNumber }], - address: noString, - salt: noString, - } - - const attempts: TestAttempt[] = [ - { - timestamp: t - 1, - expectedResult: { - accepted: false, - notBefore: 0, - state: { timer: 0, counter: 0, disabled: false, now: t - 1 }, - }, - }, - { - timestamp: t, - expectedResult: { - accepted: true, - state: { timer: t, counter: 1, disabled: false, now: t }, - }, - }, - ] - - checkTestAttempts(t, domain, attempts) - }) - - it('should accept multiple requests when batchSize is greater than one', () => { - const t = 0 // initial delay - - const domain: SequentialDelayDomain = { - name: DomainIdentifiers.SequentialDelay, - version: '1', - stages: [{ delay: t, batchSize: defined(2), resetTimer: noBool, repetitions: noNumber }], - address: noString, - salt: noString, - } - - const attempts: TestAttempt[] = [ - { - timestamp: t + 1, - expectedResult: { - accepted: true, - state: { timer: t + 1, counter: 1, disabled: false, now: t + 1 }, - }, - }, - { - timestamp: t + 1, - expectedResult: { - accepted: true, - state: { timer: t + 1, counter: 2, disabled: false, now: t + 1 }, - }, - }, - { - timestamp: t + 1, - expectedResult: { - accepted: false, - notBefore: undefined, - state: { timer: t + 1, counter: 2, disabled: false, now: t + 1 }, - }, - }, - ] - - checkTestAttempts(t, domain, attempts) - }) - - it('should reject requests when disabled is true', () => { - const t = 0 // initial delay - - const domain: SequentialDelayDomain = { - name: DomainIdentifiers.SequentialDelay, - version: '1', - stages: [{ delay: t, batchSize: defined(2), resetTimer: noBool, repetitions: noNumber }], - address: noString, - salt: noString, - } - - let result: SequentialDelayResult | undefined - result = checkSequentialDelayRateLimit(domain, t + 1, result?.state) - expect(result).toEqual({ - accepted: true, - state: { timer: t + 1, counter: 1, disabled: false, now: t + 1 }, - }) - - // Set the domain to disabled and attempt to make another reqeust. - result!.state!.disabled = true - result = checkSequentialDelayRateLimit(domain, t + 1, result?.state) - expect(result).toEqual({ - accepted: false, - notBefore: undefined, - state: { timer: t + 1, counter: 1, disabled: true, now: t + 1 }, - }) - }) - - it('should accumulate quota when resetTimer is false', () => { - const t = 10 // initial delay - - const domain: SequentialDelayDomain = { - name: DomainIdentifiers.SequentialDelay, - version: '1', - stages: [ - { delay: t, resetTimer: defined(false), batchSize: noNumber, repetitions: noNumber }, - { delay: 1, resetTimer: defined(false), batchSize: noNumber, repetitions: noNumber }, - { delay: 1, resetTimer: defined(false), batchSize: noNumber, repetitions: noNumber }, - { delay: 1, resetTimer: defined(false), batchSize: noNumber, repetitions: noNumber }, - ], - address: noString, - salt: noString, - } - - const attempts: TestAttempt[] = [ - { - timestamp: t + 3, - expectedResult: { - accepted: true, - state: { timer: t, counter: 1, disabled: false, now: t + 3 }, - }, - }, - { - timestamp: t + 3, - expectedResult: { - accepted: true, - state: { timer: t + 1, counter: 2, disabled: false, now: t + 3 }, - }, - }, - { - timestamp: t + 3, - expectedResult: { - accepted: true, - state: { timer: t + 2, counter: 3, disabled: false, now: t + 3 }, - }, - }, - { - timestamp: t + 3, - expectedResult: { - accepted: true, - state: { timer: t + 3, counter: 4, disabled: false, now: t + 3 }, - }, - }, - { - timestamp: t + 3, - expectedResult: { - accepted: false, - notBefore: undefined, - state: { timer: t + 3, counter: 4, disabled: false, now: t + 3 }, - }, - }, - ] - - checkTestAttempts(t, domain, attempts) - }) - - it('should not accumulate quota when resetTimer is true', () => { - const t = 0 // initial delay - - const domain: SequentialDelayDomain = { - name: DomainIdentifiers.SequentialDelay, - version: '1', - stages: [ - { delay: t, resetTimer: noBool, batchSize: noNumber, repetitions: noNumber }, - { delay: 1, resetTimer: noBool, batchSize: noNumber, repetitions: noNumber }, - ], - address: noString, - salt: noString, - } - - const attempts: TestAttempt[] = [ - { - timestamp: t + 2, - expectedResult: { - accepted: true, - state: { timer: t + 2, counter: 1, disabled: false, now: t + 2 }, - }, - }, - { - timestamp: t + 2, - expectedResult: { - accepted: false, - notBefore: t + 3, - state: { timer: t + 2, counter: 1, disabled: false, now: t + 2 }, - }, - }, - { - timestamp: t + 3, - expectedResult: { - accepted: true, - state: { timer: t + 3, counter: 2, disabled: false, now: t + 3 }, - }, - }, - ] - - checkTestAttempts(t, domain, attempts) - }) - - it('should return the correct results in the example sequence', () => { - const t = 10 // initial delay - - const domain: SequentialDelayDomain = { - name: DomainIdentifiers.SequentialDelay, - version: '1', - stages: [ - { delay: t, resetTimer: noBool, batchSize: defined(2), repetitions: noNumber }, - { delay: 1, resetTimer: defined(false), batchSize: noNumber, repetitions: noNumber }, - { delay: 1, resetTimer: defined(true), batchSize: noNumber, repetitions: noNumber }, - { delay: 2, resetTimer: defined(false), batchSize: noNumber, repetitions: defined(1) }, - { delay: 4, resetTimer: noBool, batchSize: defined(2), repetitions: defined(2) }, - ], - address: noString, - salt: noString, - } - - const attempts: TestAttempt[] = [ - { - timestamp: t - 1, - expectedResult: { - accepted: false, - notBefore: t, - state: { timer: 0, counter: 0, disabled: false, now: t - 1 }, - }, - }, - { - timestamp: t, - expectedResult: { - accepted: true, - state: { timer: t, counter: 1, disabled: false, now: t }, - }, - }, - { - timestamp: t + 1, - expectedResult: { - accepted: true, - state: { timer: t + 1, counter: 2, disabled: false, now: t + 1 }, - }, - }, - { - timestamp: t + 3, - expectedResult: { - accepted: true, - state: { timer: t + 2, counter: 3, disabled: false, now: t + 3 }, - }, - }, - { - timestamp: t + 3, - expectedResult: { - accepted: true, - state: { timer: t + 3, counter: 4, disabled: false, now: t + 3 }, - }, - }, - { - timestamp: t + 6, - expectedResult: { - accepted: true, - state: { timer: t + 5, counter: 5, disabled: false, now: t + 6 }, - }, - }, - { - timestamp: t + 8, - expectedResult: { - accepted: false, - notBefore: t + 9, - state: { timer: t + 5, counter: 5, disabled: false, now: t + 8 }, - }, - }, - { - timestamp: t + 9, - expectedResult: { - accepted: true, - state: { timer: t + 9, counter: 6, disabled: false, now: t + 9 }, - }, - }, - { - timestamp: t + 10, - expectedResult: { - accepted: true, - state: { timer: t + 10, counter: 7, disabled: false, now: t + 10 }, - }, - }, - { - timestamp: t + 14, - expectedResult: { - accepted: true, - state: { timer: t + 14, counter: 8, disabled: false, now: t + 14 }, - }, - }, - { - timestamp: t + 15, - expectedResult: { - accepted: true, - state: { timer: t + 15, counter: 9, disabled: false, now: t + 15 }, - }, - }, - ] - - checkTestAttempts(t, domain, attempts) - }) - }) -}) diff --git a/packages/phone-number-privacy/common/tsconfig.json b/packages/phone-number-privacy/common/tsconfig.json deleted file mode 100644 index 1f3b359f10f..00000000000 --- a/packages/phone-number-privacy/common/tsconfig.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "compilerOptions": { - "plugins": [ - { - "name": "typescript-tslint-plugin" - } - ], - "lib": ["es2017"], - "module": "commonjs", - "strict": true, - "allowJs": false, - "allowSyntheticDefaultImports": true, - "esModuleInterop": true, - "sourceMap": true, - "declaration": true, - "target": "es2017", - "rootDir": "src", - "outDir": "./lib", - "skipLibCheck": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "preserveConstEnums": true, - "composite": true - }, - "include": ["src", "index.d.ts"], - "compileOnSave": true -} diff --git a/packages/phone-number-privacy/common/tslint.json b/packages/phone-number-privacy/common/tslint.json deleted file mode 100644 index 5fc86ecb716..00000000000 --- a/packages/phone-number-privacy/common/tslint.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": ["@celo/typescript/tslint.json"], - "rules": { - "no-global-arrow-functions": false, - "no-console": true - } -} diff --git a/packages/phone-number-privacy/monitor/.env b/packages/phone-number-privacy/monitor/.env deleted file mode 100644 index 7d00d584c40..00000000000 --- a/packages/phone-number-privacy/monitor/.env +++ /dev/null @@ -1,12 +0,0 @@ -PHONE_NUMBER='+14155550123' - -PRIVATE_KEY='0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' - -# Options: json, human (default), stackdriver -LOG_FORMAT=stackdriver -# Options: fatal, error, warn, info (default), debug, trace -LOG_LEVEL=info -SERVICE_NAME='odis-monitor' - -BLOCKCHAIN_PROVIDER="https://alfajores-forno.celo-testnet.org" -NETWORK='alfajores' \ No newline at end of file diff --git a/packages/phone-number-privacy/monitor/.firebaserc b/packages/phone-number-privacy/monitor/.firebaserc deleted file mode 100644 index b8893af884b..00000000000 --- a/packages/phone-number-privacy/monitor/.firebaserc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "projects": { - "default": "celo-phone-number-privacy" - } -} diff --git a/packages/phone-number-privacy/monitor/.gitignore b/packages/phone-number-privacy/monitor/.gitignore deleted file mode 100644 index bad9645de0b..00000000000 --- a/packages/phone-number-privacy/monitor/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -dist/ - -# Firebase cache -.firebase/ -firebase-debug.log diff --git a/packages/phone-number-privacy/monitor/README.md b/packages/phone-number-privacy/monitor/README.md deleted file mode 100644 index c538bb9ce5f..00000000000 --- a/packages/phone-number-privacy/monitor/README.md +++ /dev/null @@ -1,3 +0,0 @@ -## ODIS Monitor - -A firebase schedule function that monitors ODIS by regularly querying the combiner. diff --git a/packages/phone-number-privacy/monitor/firebase.json b/packages/phone-number-privacy/monitor/firebase.json deleted file mode 100644 index 028b3af2579..00000000000 --- a/packages/phone-number-privacy/monitor/firebase.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "functions": { - "source": ".", - "predeploy": ["yarn run lint", "yarn run build"] - } -} diff --git a/packages/phone-number-privacy/monitor/package.json b/packages/phone-number-privacy/monitor/package.json deleted file mode 100644 index 42ec65d16f7..00000000000 --- a/packages/phone-number-privacy/monitor/package.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "name": "@celo/phone-number-privacy-monitor", - "version": "3.0.0-dev", - "description": "Regularly queries ODIS to ensure the system is functioning properly", - "author": "Celo", - "license": "Apache-2.0", - "main": "dist/index.js", - "scripts": { - "deploy": "yarn build && firebase deploy --only functions:odisMonitorScheduleFunctionLegacyPNP,functions:odisMonitorScheduleFunctionPNP,functions:odisMonitorScheduleFunctionDomains", - "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", - "config:get:staging": "firebase functions:config:get --project celo-phone-number-privacy-stg", - "config:get:alfajores": "firebase functions:config:get --project celo-phone-number-privacy", - "config:get:mainnet": "firebase functions:config:get --project celo-pgpnp-mainnet", - "config:set:staging": "firebase functions:config:set --project celo-phone-number-privacy-stg", - "config:set:alfajores": "firebase functions:config:set --project celo-phone-number-privacy", - "config:set:mainnet": "firebase functions:config:set --project celo-pgpnp-mainnet", - "clean": "tsc -b . --clean", - "build": "tsc -b .", - "lint": "tslint --project .", - "loadTest": "ts-node src/scripts/run-load-test.ts run" - }, - "dependencies": { - "@celo/contractkit": "^4.1.1-dev", - "@celo/cryptographic-utils": "^4.1.1-dev", - "@celo/encrypted-backup": "^4.1.1-dev", - "@celo/identity": "^4.1.1-dev", - "@celo/wallet-local": "^4.1.1-dev", - "@celo/phone-number-privacy-common": "^3.0.0-dev", - "@celo/utils": "^4.1.1-dev", - "firebase-admin": "^9.12.0", - "firebase-functions": "^3.15.7" - }, - "devDependencies": { - "firebase-functions-test": "^0.3.3", - "firebase-tools": "9.20.0" - }, - "engines": { - "node": ">=14" - } -} \ No newline at end of file diff --git a/packages/phone-number-privacy/monitor/src/index.ts b/packages/phone-number-privacy/monitor/src/index.ts deleted file mode 100644 index 3ee9bb4567d..00000000000 --- a/packages/phone-number-privacy/monitor/src/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { CombinerEndpointPNP } from '@celo/phone-number-privacy-common' -import * as functions from 'firebase-functions' -import { testDomainSignQuery, testPNPSignQuery } from './test' - -const contextName = functions.config().monitor.context_name -const blockchainProvider = functions.config().blockchain.provider -if (!contextName || !blockchainProvider) { - throw new Error('blockchain provider and context name must be set in function config') -} - -// New functions do not overwrite ODIS 1.0 monitor function. -export const odisMonitorScheduleFunctionLegacyPNP = functions - .region('us-central1') - .pubsub.schedule('every 5 minutes') - .onRun(async () => - testPNPSignQuery(blockchainProvider, contextName, CombinerEndpointPNP.LEGACY_PNP_SIGN) - ) - -export const odisMonitorScheduleFunctionPNP = functions - .region('us-central1') - .pubsub.schedule('every 5 minutes') - .onRun(async () => - testPNPSignQuery(blockchainProvider, contextName, CombinerEndpointPNP.PNP_SIGN) - ) - -export const odisMonitorScheduleFunctionDomains = functions - .region('us-central1') - .pubsub.schedule('every 5 minutes') - .onRun(async () => testDomainSignQuery(contextName)) diff --git a/packages/phone-number-privacy/monitor/src/query.ts b/packages/phone-number-privacy/monitor/src/query.ts deleted file mode 100644 index 646d5d199e1..00000000000 --- a/packages/phone-number-privacy/monitor/src/query.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { newKit } from '@celo/contractkit' -import { generateKeys, generateMnemonic, MnemonicStrength } from '@celo/cryptographic-utils' -import { - buildOdisDomain, - OdisHardeningConfig, - odisHardenKey, - odisQueryAuthorizer, -} from '@celo/encrypted-backup' -import { OdisUtils } from '@celo/identity' -import { - AuthSigner, - getServiceContext, - OdisAPI, - OdisContextName, -} from '@celo/identity/lib/odis/query' -import { CombinerEndpointPNP, fetchEnv } from '@celo/phone-number-privacy-common' -import { genSessionID } from '@celo/phone-number-privacy-common/lib/utils/logger' -import { normalizeAddressWith0x, privateKeyToAddress } from '@celo/utils/lib/address' -import { defined } from '@celo/utils/lib/sign-typed-data-utils' -import { LocalWallet } from '@celo/wallet-local' - -const phoneNumber = fetchEnv('PHONE_NUMBER') - -const newPrivateKey = async () => { - const mnemonic = await generateMnemonic(MnemonicStrength.s256_24words) - return (await generateKeys(mnemonic)).privateKey -} - -export const queryOdisForSalt = async ( - blockchainProvider: string, - contextName: OdisContextName, - endpoint: CombinerEndpointPNP.LEGACY_PNP_SIGN | CombinerEndpointPNP.PNP_SIGN, - timeoutMs: number = 10000 -) => { - console.log(`contextName: ${contextName}`) // tslint:disable-line:no-console - console.log(`blockchain provider: ${blockchainProvider}`) // tslint:disable-line:no-console - - const serviceContext = getServiceContext(contextName, OdisAPI.PNP) - - const contractKit = newKit(blockchainProvider, new LocalWallet()) - const privateKey = await newPrivateKey() - const accountAddress = normalizeAddressWith0x(privateKeyToAddress(privateKey)) - contractKit.connection.addAccount(privateKey) - contractKit.defaultAccount = accountAddress - const authSigner: AuthSigner = { - authenticationMethod: OdisUtils.Query.AuthenticationMethod.WALLET_KEY, - contractKit, - } - - const abortController = new AbortController() - const timeout = setTimeout(() => { - abortController.abort() - console.log(`ODIS salt request timed out after ${timeoutMs} ms`) // tslint:disable-line:no-console - }, timeoutMs) - try { - const res = await OdisUtils.Identifier.getObfuscatedIdentifier( - phoneNumber, - OdisUtils.Identifier.IdentifierPrefix.PHONE_NUMBER, - accountAddress, - authSigner, - serviceContext, - undefined, - undefined, - undefined, - genSessionID(), - undefined, - endpoint, - abortController - ) - clearTimeout(timeout) - - return res - } catch (error) { - clearTimeout(timeout) - throw error - } -} - -export const queryOdisForQuota = async ( - blockchainProvider: string, - contextName: OdisContextName, - timeoutMs: number = 10000 -) => { - console.log(`contextName: ${contextName}`) // tslint:disable-line:no-console - console.log(`blockchain provider: ${blockchainProvider}`) // tslint:disable-line:no-console - - const serviceContext = getServiceContext(contextName, OdisAPI.PNP) - - const contractKit = newKit(blockchainProvider, new LocalWallet()) - const privateKey = await newPrivateKey() - const accountAddress = normalizeAddressWith0x(privateKeyToAddress(privateKey)) - contractKit.connection.addAccount(privateKey) - contractKit.defaultAccount = accountAddress - const authSigner: AuthSigner = { - authenticationMethod: OdisUtils.Query.AuthenticationMethod.WALLET_KEY, - contractKit, - } - - const abortController = new AbortController() - const timeout = setTimeout(() => { - abortController.abort() - }, timeoutMs) - - try { - const res = await OdisUtils.Quota.getPnpQuotaStatus( - accountAddress, - authSigner, - serviceContext, - undefined, - undefined, - abortController - ) - - clearTimeout(timeout) - - return res - } catch (error) { - clearTimeout(timeout) - throw error - } -} - -export const queryOdisDomain = async (contextName: OdisContextName) => { - console.log(`contextName: ${contextName}`) // tslint:disable-line:no-console - - const serviceContext = getServiceContext(contextName, OdisAPI.DOMAIN) - const monitorDomainConfig: OdisHardeningConfig = { - rateLimit: [ - { - delay: 0, - resetTimer: defined(true), - // Running every 5 min, this should not run out for the next 9 million years - batchSize: defined(1000000000000), - repetitions: defined(1000000000000), - }, - ], - environment: serviceContext, - } - const authorizer = odisQueryAuthorizer(Buffer.from('ODIS domains monitor authorizer test seed')) - const domain = buildOdisDomain(monitorDomainConfig, authorizer.address) - // Throws if signature verification fails - return odisHardenKey(Buffer.from('password'), domain, serviceContext, authorizer.wallet) -} diff --git a/packages/phone-number-privacy/monitor/src/scripts/run-load-test.ts b/packages/phone-number-privacy/monitor/src/scripts/run-load-test.ts deleted file mode 100644 index cac2eb23634..00000000000 --- a/packages/phone-number-privacy/monitor/src/scripts/run-load-test.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { OdisContextName } from '@celo/identity/lib/odis/query' -import yargs from 'yargs' -import { concurrentLoadTest, serialLoadTest } from '../test' - -/* tslint:disable:no-console */ - -const runLoadTest = (contextName: string, numWorker: number, isSerial: boolean) => { - let blockchainProvider: string - switch (contextName) { - case 'alfajoresstaging': - case 'alfajores': - blockchainProvider = 'https://alfajores-forno.celo-testnet.org' - break - case 'mainnet': - blockchainProvider = 'https://forno.celo.org' - break - default: - console.error('Invalid contextName') - yargs.showHelp() - process.exit(1) - } - if (numWorker < 1) { - console.error('Invalid numWorkers') - yargs.showHelp() - process.exit(1) - } - if (isSerial) { - serialLoadTest(numWorker, blockchainProvider!, contextName as OdisContextName) // tslint:disable-line:no-floating-promises - } else { - concurrentLoadTest(numWorker, blockchainProvider!, contextName as OdisContextName) // tslint:disable-line:no-floating-promises - } -} -// tslint:disable-next-line: no-unused-expression -yargs - .scriptName('ODIS-load-test') - .recommendCommands() - .demandCommand(1) - .strict(true) - .showHelpOnFail(true) - .command( - 'run ', - 'Load test ODIS.', - (args) => - args - .positional('contextName', { - type: 'string', - description: 'Desired network.', - }) - .positional('numWorkers', { - type: 'number', - description: 'Number of machines that will be sending request to ODIS.', - }) - .option('isSerial', { - type: 'boolean', - description: 'run test workers in series.', - default: false, - }), - (args) => runLoadTest(args.contextName!, args.numWorkers!, args.isSerial) - ).argv diff --git a/packages/phone-number-privacy/monitor/src/test.ts b/packages/phone-number-privacy/monitor/src/test.ts deleted file mode 100644 index 0e10c2c243f..00000000000 --- a/packages/phone-number-privacy/monitor/src/test.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { concurrentMap, sleep } from '@celo/base' -import { Result } from '@celo/base/lib/result' -import { BackupError } from '@celo/encrypted-backup' -import { IdentifierHashDetails } from '@celo/identity/lib/odis/identifier' -import { ErrorMessages, OdisContextName } from '@celo/identity/lib/odis/query' -import { PnpClientQuotaStatus } from '@celo/identity/lib/odis/quota' -import { CombinerEndpointPNP, rootLogger } from '@celo/phone-number-privacy-common' -import { queryOdisDomain, queryOdisForQuota, queryOdisForSalt } from './query' - -const logger = rootLogger('odis-monitor') - -export async function testPNPSignQuery( - blockchainProvider: string, - contextName: OdisContextName, - endpoint: CombinerEndpointPNP.LEGACY_PNP_SIGN | CombinerEndpointPNP.PNP_SIGN, - timeoutMs?: number -) { - logger.info(`Performing test PNP query for ${endpoint}`) - try { - const odisResponse: IdentifierHashDetails = await queryOdisForSalt( - blockchainProvider, - contextName, - endpoint, - timeoutMs - ) - logger.info({ odisResponse }, 'ODIS salt request successful. System is healthy.') - } catch (err) { - if ((err as Error).message === ErrorMessages.ODIS_QUOTA_ERROR) { - logger.info( - { error: err }, - 'ODIS salt request out of quota. This is expected. System is healthy.' - ) - } else { - logger.error('ODIS salt request failed.') - logger.error({ err }) - throw err - } - } -} - -export async function testPNPQuotaQuery( - blockchainProvider: string, - contextName: OdisContextName, - timeoutMs?: number -) { - logger.info(`Performing test PNP query for ${CombinerEndpointPNP.PNP_QUOTA}`) - try { - const odisResponse: PnpClientQuotaStatus = await queryOdisForQuota( - blockchainProvider, - contextName, - timeoutMs - ) - logger.info({ odisResponse }, 'ODIS quota request successful. System is healthy.') - } catch (err) { - logger.error('ODIS quota request failed.') - logger.error({ err }) - throw err - } -} - -export async function testDomainSignQuery(contextName: OdisContextName) { - logger.info('Performing test domains query') - let odisResponse: Result - try { - odisResponse = await queryOdisDomain(contextName) - logger.info({ odisResponse }, 'ODIS response') - } catch (err) { - logger.error('ODIS key hardening request failed.') - logger.error({ err }) - throw err - } - if (odisResponse.ok) { - logger.info('System is healthy') - } else { - throw new Error('Received not ok response') - } -} - -export async function serialLoadTest( - n: number, - blockchainProvider: string, - contextName: OdisContextName, - endpoint: - | CombinerEndpointPNP.LEGACY_PNP_SIGN - | CombinerEndpointPNP.PNP_QUOTA - | CombinerEndpointPNP.PNP_SIGN = CombinerEndpointPNP.PNP_SIGN, - timeoutMs?: number -) { - for (let i = 0; i < n; i++) { - try { - switch (endpoint) { - case CombinerEndpointPNP.LEGACY_PNP_SIGN: - case CombinerEndpointPNP.PNP_SIGN: - await testPNPSignQuery(blockchainProvider, contextName, endpoint, timeoutMs) - break - case CombinerEndpointPNP.PNP_QUOTA: - await testPNPQuotaQuery(blockchainProvider, contextName, timeoutMs) - } - } catch {} // tslint:disable-line:no-empty - } -} - -export async function concurrentLoadTest( - workers: number, - blockchainProvider: string, - contextName: OdisContextName, - endpoint: - | CombinerEndpointPNP.LEGACY_PNP_SIGN - | CombinerEndpointPNP.PNP_QUOTA - | CombinerEndpointPNP.PNP_SIGN = CombinerEndpointPNP.PNP_SIGN, - timeoutMs?: number -) { - while (true) { - const reqs = [] - for (let i = 0; i < workers; i++) { - reqs.push(i) - } - await concurrentMap(workers, reqs, async (i) => { - await sleep(i * 10) - while (true) { - try { - switch (endpoint) { - case CombinerEndpointPNP.LEGACY_PNP_SIGN: - case CombinerEndpointPNP.PNP_SIGN: - await testPNPSignQuery(blockchainProvider, contextName, endpoint, timeoutMs) - break - case CombinerEndpointPNP.PNP_QUOTA: - await testPNPQuotaQuery(blockchainProvider, contextName, timeoutMs) - } - } catch {} // tslint:disable-line:no-empty - } - }) - } -} diff --git a/packages/phone-number-privacy/monitor/tsconfig.json b/packages/phone-number-privacy/monitor/tsconfig.json deleted file mode 100644 index 5a050f2a779..00000000000 --- a/packages/phone-number-privacy/monitor/tsconfig.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "compilerOptions": { - "plugins": [ - { - "name": "typescript-tslint-plugin" - } - ], - "lib": ["es2017"], - "module": "commonjs", - "strict": true, - "allowJs": false, - "allowSyntheticDefaultImports": true, - "esModuleInterop": true, - "sourceMap": true, - "declaration": true, - "target": "es2017", - "rootDir": "src", - "outDir": "./dist", - "skipLibCheck": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "preserveConstEnums": true, - "composite": true - }, - "include": ["src", "index.d.ts"], - "compileOnSave": true -} diff --git a/packages/phone-number-privacy/monitor/tslint.json b/packages/phone-number-privacy/monitor/tslint.json deleted file mode 100644 index 5fc86ecb716..00000000000 --- a/packages/phone-number-privacy/monitor/tslint.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": ["@celo/typescript/tslint.json"], - "rules": { - "no-global-arrow-functions": false, - "no-console": true - } -} diff --git a/packages/phone-number-privacy/monitor/ui-debug.log b/packages/phone-number-privacy/monitor/ui-debug.log deleted file mode 100644 index 2f9d04d84c6..00000000000 --- a/packages/phone-number-privacy/monitor/ui-debug.log +++ /dev/null @@ -1 +0,0 @@ -Web / API server started at http://localhost:4000 diff --git a/packages/phone-number-privacy/signer/.env b/packages/phone-number-privacy/signer/.env deleted file mode 100644 index 7625631db3a..00000000000 --- a/packages/phone-number-privacy/signer/.env +++ /dev/null @@ -1,45 +0,0 @@ -NODE_ENV=development -#SERVER_PORT=443 -#SERVER_SSL_KEY_PATH=./server.key -#SERVER_SSL_CERT_PATH=./server.cert -BLOCKCHAIN_PROVIDER=https://alfajores-forno.celo-testnet.org -DB_HOST=http://localhost -DB_USERNAME=postgres -DB_PASSWORD=mockPass -DB_DATABASE=phoneNumberPrivacy -DB_USE_SSL=true -#KEYSTORE_TYPE=AzureKeyVault -#KEYSTORE_AZURE_CLIENT_ID=useMock -#KEYSTORE_AZURE_CLIENT_SECRET=useMock -#KEYSTORE_AZURE_TENANT=useMock -#KEYSTORE_AZURE_VAULT_NAME=useMock -PHONE_NUMBER_PRIVACY_KEY_NAME_BASE='phoneNumberPrivacy' -DOMAINS_KEY_NAME_BASE='domains' -PHONE_NUMBER_PRIVACY_LATEST_KEY_VERSION='1' -DOMAINS_LATEST_KEY_VERSION='1' -KEYSTORE_TYPE=MockSecretManager -KEYSTORE_GOOGLE_PROJECT_ID=mockProjectId -# Options: json, human (default), stackdriver -LOG_FORMAT=stackdriver -# Options: fatal, error, warn, info (default), debug, trace -LOG_LEVEL=info -SERVICE_NAME='odis-signer' - -# For e2e Tests -ODIS_SIGNER_SERVICE_URL="SIGNER OPERATORS FILL IN" -STAGING_ODIS_BLOCKCHAIN_PROVIDER=https://alfajores-forno.celo-testnet.org -ALFAJORES_ODIS_BLOCKCHAIN_PROVIDER=https://alfajores-forno.celo-testnet.org -MAINNET_ODIS_BLOCKCHAIN_PROVIDER=https://forno.celo.org -ODIS_DOMAINS_TEST_KEY_VERSION=1 -ODIS_PNP_TEST_KEY_VERSION=1 -DEPLOYED_SIGNER_SERVICE_VERSION=2.0.1 -# PUBKEYS -STAGING_DOMAINS_PUBKEY=7FsWGsFnmVvRfMDpzz95Np76wf/1sPaK0Og9yiB+P8QbjiC8FV67NBans9hzZEkBaQMhiapzgMR6CkZIZPvgwQboAxl65JWRZecGe5V3XO4sdKeNemdAZ2TzQuWkuZoA -ALFAJORES_DOMAINS_PUBKEY=+ZrxyPvLChWUX/DyPw6TuGwQH0glDJEbSrSxUARyP5PuqYyP/U4WZTV1e0bAUioBZ6QCJMiLpDwTaFvy8VnmM5RBbLQUMrMg5p4+CBCqj6HhsMfcyUj8V0LyuNdStlCB -MAINNET_DOMAINS_PUBKEY=LX4tLiuYm8geZ3ztmH7oIWz4ohXt3ePRTd9BbG9RO86NMrApflioiOzKYtIsyjEA0uarnX8Emo+luTY4bwEWpgZDyPYE6UMWAoBaZBdy6NDMgAxSbdNtaQEq51fBjCUA -# Polynomials -STAGING_POLYNOMIAL=0200000000000000ec5b161ac167995bd17cc0e9cf3f79369efac1fff5b0f68ad0e83dca207e3fc41b8e20bc155ebb3416a7b3d87364490169032189aa7380c47a0a464864fbe0c106e803197ae4959165e7067b95775cee2c74a78d7a67406764f342e5a4b99a003a510287524c9437b12ebb0bfdc7ea46078b807d1b665966961784bd71c4227c272b01c0fcd19c5b92226c1aac324b010abef36192e8ff3abb25686b3e6707bc747b129c32e572b5850db8446bd8f0af9a3fbf6b579793002b1b68528ca4ac00 -ALFAJORES_PHONE_NUMBER_PRIVACY_POLYNOMIAL=020000000000000090fa11c56744759fcd777b909e9dc5245b39e33ba24be92caaf3a6f71f2f63a2873c6f23adfab2b2f211534c2da98b01280ed2a00b0808c06ff02fc56f66690ceaa14aabebfec65b6681e641fbdbaabcdbb4320fbd422b1e0452d3274908cb00f3d2ba1d64ddc12f387ef5c6fb98265cee27afa66626edf91b9839d49f23890d75a550a49a2e7a75b06b3b49734a160035558eb2079c41926388ac560e75f1962dada39e5c30ba35bef59eb84ff4329432cdc10383b4dea40f5ad8fabbb09a81 -MAINNET_PHONE_NUMBER_PRIVACY_POLYNOMIAL=060000000000000016fade1df2e68418f0c47c6cc5ecab70e2ed4a89c2f63ecadd6ad2e106a962c407e8b75a0d368d1a69e540c7c5634e01a7f2b8c00bea4303bdfdba8f54229ff197bc399a3c16b9a8838258e31022c2bb2a397c6e835d7e86d8c47b5a63e2e30017f865337fd0060497457135173e2b0eaec6f8f14f0cacb17a5d150218e15bd46963ed1b9d56f956f9c4fc692813100042f098b7f70913f671e28ed1c99104b9b740549c42c59212b6671f1e1675674f7e6b6d690a13bd474ab9f0c83cd48e017514ca3874606f6abde2b957c791376e24d55efe6ccc7a1194a685b9589ca873a51c7e77b7b814a76cd9af2aafef500155280fb84efd3219b04312635568788b3393fd45a11f431a7eef8a8fc59ff2bfd4aab744baf9221bf1774653dda61d8193b720f60c627d5a9fec5c2c16a27e948f2f4545b460090303327262ec87f51fbf860f58d5e051d91d5bb869c8912300a9b1c2d922d329c9b7d5179946e049d52ed9b3876f36e5c8b2a47831eb235a51d8d877a284fbe07750449f9654d332808beb9641404188813cddb8ffad906752d71f3f042b583f501b3b7f3906946f9931c598575bf4c8d3e8941168f8cc8e001c092117257bb073db3885dffca5e8dd76b689d395bb5555cf00f9943a9e1ec9939f9d700407330163220f3c15a9420011b8693fb95c635168b6b0a021263b246301343e80161eac44fe79ba657fe59deb9d297ced18d090a8f65dc9c2e0990177f186d7501a2256ac9ecca36743e118f5dd4ce35dc976d38c8679d53cd11b0f11edb45c3473ce848d35875e63b2d100 -ALFAJORES_DOMAINS_POLYNOMIAL=0200000000000000f99af1c8fbcb0a15945ff0f23f0e93b86c101f48250c911b4ab4b15004723f93eea98c8ffd4e166535757b46c0522a0167a40224c88ba43c13685bf2f159e63394416cb41432b320e69e3e0810aa8fa1e1b0c7dcc948fc5742f2b8d752b65081f10d83821b4e2cf90b56cc4fc8c98dc00e5f24f2c5b53fa8ad7c2ebd3963c9223cf95209692d267a4f8084edfc0b5f01f7a31d82bf5421c544b6258749c691b79e6f36d9ba963ead6f25b9986b6bcb7d45b5edb33a616af630b4ce17bf552c81 -MAINNET_DOMAINS_POLYNOMIAL=05000000000000002d7e2d2e2b989bc81e677ced987ee8216cf8a215eddde3d14ddf416c6f513bce8d32b0297e58a888ecca62d22cca3100d2e6ab9d7f049a8fa5b936386f0116a60643c8f604e9431602805a641772e8d0cc800c526dd36d69012ae757c18c250029d97c8a3d4b81e305780b49d511c80dc3009c02b8f651a06c8ec2d5530937a1f7eadf730ad46762a4c089bbd973a000ba77717ec36ebb6fd58904b444a6cde7dd3b3b7ac6fa37f9cd8d00aa67e7cfe81adee5ed45218f7f78b4f8473b564601f4361d228dc6dabf7decd3f61f5bb0ad2c7bd7fe5b7a88054959543e82f4deb08d4fe9af4ac775c9353e038e79f82200863ac9cb7fd6b5fa263eb9d1dead51002607f3eadac153596b671b854715bdb07bee1b0bc8d5178f0dac1b4d00ed0700f46e37135e96604d389f3a323028e29b07f36279e829da00eee1794f3ad6e5dca24eba65a7821755cc464add27c7a601c7e187756e79a5ec3c847f4d91b037fe3cd40590fc1a46b46c2f68c0edcbe5cd7727162a195a711008e4e956eb8a81011b290057cee3f14b9a4198a3e9909cac69a9e7d648fa3dd185794acc4c1e4b994637dca36621d463b42e015115ac2c015fc176d8f143bf99cca654ae95a3101afbdc0c5026f95fbf31af1ac115399f5b6b6d1de09af367745415be9533f8c080 diff --git a/packages/phone-number-privacy/signer/.gitignore b/packages/phone-number-privacy/signer/.gitignore deleted file mode 100644 index 4b329a550ca..00000000000 --- a/packages/phone-number-privacy/signer/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -dist/ - -# Firebase cache -.firebase/ - -gcp-service-account.json -server.cert -server.key - -# Ignore updates for dev deployments -./azure-templates/container-parameters.json - -package-lock.json \ No newline at end of file diff --git a/packages/phone-number-privacy/signer/README.md b/packages/phone-number-privacy/signer/README.md deleted file mode 100644 index 8d6a2944c84..00000000000 --- a/packages/phone-number-privacy/signer/README.md +++ /dev/null @@ -1,179 +0,0 @@ -# ODIS - Signer Service - -A service that generates unique partial signatures for blinded messages. Using a threshold BLS signature scheme, when K/N signatures are combined, a deterministic signature is obtained. - -## APIs (ODIS v2) - -ODIS v2 provides support for three APIs, which need to be explicitly enabled ([see below](#enabling-apis-odis-v2) for configuration info): - -- **Legacy PNP API**: retrieve signatures for blinded messages, rate-limited using ODIS v1's scheme, based on an account's transaction history and verification status. -- **PNP API**: retrieve signatures for blinded messages, rate-limited based on quota purchased on-chain in `OdisPayments.sol`. -- **Domains API**: retrieve signatures over domains with custom rate-limiting schemes, as defined in more detail in [CIP-40](https://github.com/celo-org/celo-proposals/blob/master/CIPs/cip-0040.md). - -## Configuration - -You can use the following environment variables to configure the ODIS Signer service: - -### Server - -- `NODE_ENV` - `development` or `production` -- `SERVER_PORT` - The port on which the express node app runs (8080 by default). -- `SERVER_SSL_KEY_PATH` - (Optional) Path to SSL .key file. -- `SERVER_SSL_CERT_PATH` - (Optional) Path to SSL .cert file. - -### Enabling APIs (ODIS v2) - -Each API must be explicitly enabled by setting the following env vars to true (all are false by default): - -- `LEGACY_PHONE_NUMBER_PRIVACY_API_ENABLED` -- `PHONE_NUMBER_PRIVACY_API_ENABLED` -- `DOMAINS_API_ENABLED` - -### Database - -The service currently supports Postgres, MSSQL, and MySQL. - -- `DB_TYPE` - `postgres`, `mysql`, or `mssql` (postgres by default). -- `DB_HOST` - The URL under which your database is accessible. -- `DB_PORT` - The port on the database host (uses default for chosen DB type). -- `DB_USERNAME` - DB configuration: The DB username (postgres by default). -- `DB_PASSWORD` - DB configuration: The DB password. -- `DB_DATABASE` - DB configuration: The DB database name (phoneNumberPrivacy by default). -- `DB_USE_SSL` - DB configuration: Use SSL connection to the database (true by default). - -#### DB Migrations - -To update the signer DB schema, first run `yarn db:migrate:make ` to create a new migrations file. Then, fill in the new migration file as needed using the previous migration files as references. - -Migrations will run automatically on startup. - -### Blockchain provider - -The service needs a connection to a full node in order to access chain state. The `BLOCKCHAIN_PROVIDER` config should be a url to a node with its JSON RPC enabled. -This could be a node with RPC set up. Preferably this would be an node dedicated to this service. Alternatively, the public Forno endpoints can be used but their uptime guarantees are not as strong. For development with Alfajores, the forno url is `https://alfajores-forno.celo-testnet.org`. For Mainnet, it would be `https://forno.celo.org` - -- `BLOCKCHAIN_PROVIDER` - The blockchain node provider for chain state access. ` -- `BLOCKCHAIN_API_KEY` - Optional API key to be added to the authentication header. ` - -### Security - -The ODIS Signer service provides partial signatures that can be combined to generate domain-specific encryption keys. These keys are used for a variety of different purposes from phone number privacy to account backup encryption. It's very important to keep your BLS key share safe. We provide the following recommended best practices for keeping your key secure. - -#### Leverage a cloud keystore - -All cloud providers have a keystore offering that keeps your key secure while still being accessible by your service. ODIS Signer supports Azure, GCP, and AWS keystores. You can find configuration details in the [Keystores](#keystores) section below. - -#### Lock down your cloud - -- [ ] Ensure that you have multi-factor authentication enabled for all cloud accounts. -- [ ] Reduce access to the ODIS resources to as minimal of a set of people as possible. -- [ ] Revisit your cloud's admin set and ensure it is up to date. -- [ ] Enable Just-In-Time access policies if your cloud provider has this functionality available. For example, Azure provides [Privileged Identity Management](https://docs.microsoft.com/en-us/azure/active-directory/privileged-identity-management/pim-configure) which allows you to specify an approval list and limited time window in which an employee may access a given resource. -- [ ] Monitor/Audit access to the keystore and ODIS resource group. - -#### Create a secure backup - -The BLS key share should only exist in the keystore or as an encrypted backup. To create a backup, you can either download an encrypted copy from your keystore or manually encrypt it locally. Make sure that you keep it somewhere memorable (ex. external hard drive or password manager). Here are a couple options to create a local encrypted backup: - -- [Azure Key Vault](https://docs.microsoft.com/en-us/azure/key-vault/general/backup?tabs=azure-cli) -- [MacOS](https://support.apple.com/guide/mac-help/protect-your-mac-information-with-encryption-mh40593/mac) -- [Windows](https://support.microsoft.com/en-us/windows/how-to-encrypt-a-file-1131805c-47b8-2e3e-a705-807e13c10da7) -- [GPG Command](https://www.gnupg.org/gph/en/manual/x110.html) - -### Keystores - -Currently, the service supports Azure Key Vault (AKV), Google Secret Manager and AWS Secrets Manager. -You must specify the type, and then the keystore configs for that type as follows. - -- `KEYSTORE_TYPE` - `AzureKeyVault`, `GoogleSecretManager` or `AWSSecretManager` - -In addition, you must name your keys in your keystore according to the pattern `-` where - -- `keyName` is configurable via the env variables `PHONE_NUMBER_PRIVACY_KEY_NAME_BASE` and `DOMAINS_KEY_NAME_BASE` which default to `phoneNumberPrivacy` and `domains` respectively. -- `keyVersion` is an integer corresponding to the iteration of the given key share. The variables `PHONE_NUMBER_PRIVACY_LATEST_KEY_VERSION` and `DOMAINS_LATEST_KEY_VERSION` should specify the latest version of the appropriate key share. This version will be fetched when the signer starts up. - -For example, the first iteration of the key share used for phone number privacy should be stored as `phoneNumberPrivacy-1` and the second iteration (after resharing) should be stored as `phoneNumberPrivacy-2` unless you specify a `PHONE_NUMBER_PRIVACY_KEY_NAME_BASE` env variable, in which case `phoneNumberPrivacy` should be replaced with that value. The version numbers and `-` delimeter are mandatory and not configurable. - -**Note: if you modify the stored secrets, you must restart the signer to ensure the updated versions are used in the signer.** - -#### Azure Key Vault - -Use the following to configure the AKV connection. These values are generated when creating a service principal account (see [Configuring your Key Vault](https://www.npmjs.com/package/@azure/keyvault-keys#configuring-your-key-vault)). Or if the service is being hosted on Azure itself, authentication can be done by granted key access to the VM's managed identity, in which case the client_id, client_secret, and tenant configs can be left blank. - -- `KEYSTORE_AZURE_VAULT_NAME` - The name of your Azure Key Vault. -- `KEYSTORE_AZURE_CLIENT_ID` - (Optional) The clientId of the service principal account that has [Get, List] access to secrets. -- `KEYSTORE_AZURE_CLIENT_SECRET` - (Optional) The client secret of the same service principal account. -- `KEYSTORE_AZURE_TENANT` - (Optional) The tenant that the service principal is a member of. - -#### Google Secret Manager - -Use the following to configure the Google Secret Manager. To authenticate with Google Cloud, you can see [Setting Up Authentication](https://cloud.google.com/docs/authentication/production). By default, the google lib will use the default app credentials assigned to the host VM. If the service is being run outside of GCP, you can manually set the `GOOGLE_APPLICATION_CREDENTIALS` env var to the path to a service account json file. - -- `KEYSTORE_GOOGLE_PROJECT_ID` - The google cloud project id. - -#### AWS Secrets Manager - -Use the following to configure the AWS Secrets Manager. To authenticate with Amazon Web Services, you can see [Setting Credentials in Node.js](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/setting-credentials-node.html). If you are running the signer inside AWS, we do recommend to authenticate using IAM Roles. - -- `KEYSTORE_AWS_REGION` - The AWS Region code where the secret is, for example: `us-east-1`. -- `KEYSTORE_AWS_SECRET_KEY` - The key for the secret key/value pair. - -## Operations - -### Setup - -The service requires a connection to a secret store and to a SQL database. The SQL connection parameters should be configured with the `DB_*` configs stated above. Before starting the service, be sure to create a database and set the name as the value in the `DB_DATABASE` environment variable. - -#### Running locally or without docker - -To run without docker, or for development, start by git cloning the celo-monorepo. Next, run `yarn` from the monorepo root to install dependencies. - -Then start the service: `yarn start` - -#### Running in docker - -Docker images for the signer service are published to Celo's [container registry on Google Cloud](https://console.cloud.google.com/gcr/images/celo-testnet/US/celo-monorepo). Search for images with tag `phone-number-privacy-*`. Then pull the image: - -`docker pull us.gcr.io/celo-testnet/celo-monorepo:phone-number-privacy-{LATEST_TAG_HERE}` - -To start the service, run: - -`docker run -d -p 80:8080 {ENV_VARS_HERE} {IMAGE_TAG_HERE}` - -Then check on the service to make sure its running: - -`docker container ls` - -`docker logs -f {CONTAINER_ID_HERE}` - -#### Key rotations - -After a key resharing, signers should rotate their key shares as follows: - -1. Store the new key share in the keystore according to the naming convention specified in the [Keystores](#keystores) section above. -2. Increment `PHONE_NUMBER_PRIVACY_LATEST_KEY_VERSION` or `DOMAINS_LATEST_KEY_VERSION` as appropriate. This will instruct the signer to prefetch this new key version the next time it starts up, but there is no need to restart the signer at this point. -3. Notify the combiner operator that your signer is ready for the key rotation. -4. The combiner operator will run e2e tests against your signer to verify it has the correct key configuration. -5. The combiner operator will update the combiner to request the new key share version via a custom request header field once all signers are ready. -6. The signers will fetch the new key shares from their keystores upon receiving these requests. -7. When the combiner operator sees that all signers are signing with the new key share and confirms that the system is healthy, signers will be instructed to delete their old key shares. Deleting the deprecated key shares ensures they cannot be stored and used by an attacker. - -### Validate before going live - -You can test your mainnet service is set up correctly by running specific tests in the e2e suite ("[Signer configuration test]" cases) which check signatures against the public polynomial for the respective APIs. Because the tests require quota, you must first point your provider endpoint to Alfajores. - -1. Change your signer’s blockchain provider (`BLOCKCHAIN_PROVIDER`) to Alfajores Forno: `https://alfajores-forno.celo-testnet.org` -2. Navigate to the signer directory in monorepo (this directory). -3. Modify the .env file: - - - Change `ODIS_SIGNER_SERVICE_URL` to your service endpoint. - -4. Run `yarn test:signer:mainnet`. - - *Technical note: this command intentionally points the test's blockchain provider to Alfajores, in order to top up quota on Alfajores before running the test cases. It still verifies signatures against the respective mainnet polynomials.* -5. Verify that all tests pass. -6. Change your signer’s blockchain provider back to its original value (if using Forno: `https://forno.celo.org`). - -### Logs - -Error logs will be prefixed with `CELO_ODIS_ERROR_XX`. You can see a full list of them in [errors.ts](https://github.com/celo-org/celo-monorepo/blob/master/packages/phone-number-privacy/common/src/interfaces/errors.ts) in the common package. diff --git a/packages/phone-number-privacy/signer/docs/deploy-on-aws.md b/packages/phone-number-privacy/signer/docs/deploy-on-aws.md deleted file mode 100644 index c0d61e53bc3..00000000000 --- a/packages/phone-number-privacy/signer/docs/deploy-on-aws.md +++ /dev/null @@ -1,115 +0,0 @@ -# Deploying to AWS - -## Prerequisites - -- awscli (https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html) -- AWS credentials configured (https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html) - -## Steps - -### Setting up the network and database - -The next steps will explain how to create a new VPC and a RDS Postgres database needed to run the signer. If desired, it is possible to use an existing VPC and a database deployed in some any other way. Treat it as a reference for the port/security group configuration. - -1. OPTIONAL: [Create new vpc and subnets](https://docs.aws.amazon.com/directoryservice/latest/admin-guide/gsg_create_vpc.html) for the resources (use the CIDR block and AZs that fits with your network setup). The VPC should have at least two subnets, and if it does not have any public subnet (for running the Signer container) the private subnets should have an [Internet Gateway configured](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Internet_Gateway.html) - -1. Create the security groups for the database and signer: - - ```bash - aws ec2 create-security-group --description "odis database" --group-name odis-db --vpc-id - aws ec2 create-security-group --description "signer" --group-name signer --vpc-id - ``` - -1. Create the security group rules The database should expose 5432 (the port could be specified and change during the database creation), and the signer by default uses port 8080: - - ```bash - aws ec2 authorize-security-group-ingress --group-id --protocol tcp --port 5432 --source-group sg-0a1064e7d9cba38a9 - aws ec2 authorize-security-group-ingress --group-id --protocol tcp --port 8080 --cidr 0.0.0.0/0 - ``` - -1. [Create a RDS PostgreSQL DB](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_CreateDBInstance.html) (from the AWS web console). -The database does not require public access and you should accessing the VPC and security group previously created (or any other VPC and the security group if it is the case). - -For the DB Instance size, `Free tier` should be enough. - -### Running the Signer on Fargate - -ECS Fargate is a container execution service provided by AWS. It runs containers without requiring explicit management of hosts or virtual machines. -Alternatively the signer service can be run using any other service that allows to run containers, such as EC2 or EKS. In the case of EC2, you will need to install docker, configure the instance profile and follow the documentation from [the signer readme](https://github.com/celo-org/celo-monorepo/tree/master/packages/phone-number-privacy/signer). - -1. Create the service-linked role. If it is the first time you run ECS on your account you will need to run this command. - - ```bash - aws iam create-service-linked-role --aws-service-name ecs.amazonaws.com - ``` - -1. Create a Task Role for the signer ([documentation](https://docs.amazonaws.cn/en_us/AmazonECS/latest/userguide/ecs-cli-tutorial-fargate.html)). -First we will create the `assume-role` policy that allows ECS tasks to be assigned to this task role. - - ```bash - cat <<'EOF' > /tmp/task-execution-assume-role.json - { - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "", - "Effect": "Allow", - "Principal": { - "Service": "ecs-tasks.amazonaws.com" - }, - "Action": "sts:AssumeRole" - } - ] - } - EOF - ``` - - Now we will create the task-role: - - ```bash - aws iam --region us-east-2 create-role --role-name signerTaskExecutionRole --assume-role-policy-document file:///tmp/task-execution-assume-role.json - ``` - - Finally we create the policy assigned to this task-role that allows retrieval of secrets from AWS Secret Manager. Then we attach that policy to the task role. - - ```bash - cat <<'EOF' > /tmp/secret-manager-signer-policy.json - { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": [ - "secretsmanager:GetResourcePolicy", - "secretsmanager:GetSecretValue", - "secretsmanager:DescribeSecret", - "secretsmanager:ListSecretVersionIds" - ], - "Resource": [ - "arn:aws:secretsmanager:us-east-2:{YOUR_ACCOUNT_ID}:secret:{YOUR_SECRET_ID}" - ] - } - ] - } - EOF - - aws iam create-policy --policy-name signerTaskAllowSecretManager --policy-document file:///tmp/secret-manager-signer-policy.json - aws iam attach-role-policy --role-name signerTaskExecutionRole --policy-arn arn:aws:iam::{YOUR_ACCOUNT_ID}:policy/signerTaskAllowSecretManager - ``` - - If you want to manage the RDS postgres permissions using IAM, you can also add permissions for signer access to this policy. - -1. Create ECS Fargate cluster - - ```bash - aws ecs create-cluster --cluster-name odis --capacity-providers FARGATE_SPOT --default-capacity-provider-strategy FARGATE_SPOT - ``` - -1. Create task definition. Using the web interface, create a task definition with the next configuration: - - - [Task definition detail](./images/fargate-task-definition.png) - - [Container definition detail](./images/fargate-container-definition.png) - -1. Create the service using the task definition. - - - [Service definition detail](./images/fargate-service-definition.png) diff --git a/packages/phone-number-privacy/signer/docs/deploy-on-azure.md b/packages/phone-number-privacy/signer/docs/deploy-on-azure.md deleted file mode 100644 index 1741c5acb0b..00000000000 --- a/packages/phone-number-privacy/signer/docs/deploy-on-azure.md +++ /dev/null @@ -1,3 +0,0 @@ -# Deploying to AWS - -The recommended method for deploying to Azure is to use the ARM templates and instructions specified in the [azure-templates folder](../azure-templates/README.md). \ No newline at end of file diff --git a/packages/phone-number-privacy/signer/docs/images/fargate-container-definition.png b/packages/phone-number-privacy/signer/docs/images/fargate-container-definition.png deleted file mode 100644 index 64478805010..00000000000 Binary files a/packages/phone-number-privacy/signer/docs/images/fargate-container-definition.png and /dev/null differ diff --git a/packages/phone-number-privacy/signer/docs/images/fargate-service-definition.png b/packages/phone-number-privacy/signer/docs/images/fargate-service-definition.png deleted file mode 100644 index 1d29a451882..00000000000 Binary files a/packages/phone-number-privacy/signer/docs/images/fargate-service-definition.png and /dev/null differ diff --git a/packages/phone-number-privacy/signer/docs/images/fargate-task-definition.png b/packages/phone-number-privacy/signer/docs/images/fargate-task-definition.png deleted file mode 100644 index 78021ce26dc..00000000000 Binary files a/packages/phone-number-privacy/signer/docs/images/fargate-task-definition.png and /dev/null differ diff --git a/packages/phone-number-privacy/signer/index.d.ts b/packages/phone-number-privacy/signer/index.d.ts deleted file mode 100644 index 102dc17cf17..00000000000 --- a/packages/phone-number-privacy/signer/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare module 'bunyan-debug-stream' diff --git a/packages/phone-number-privacy/signer/jest.config.js b/packages/phone-number-privacy/signer/jest.config.js deleted file mode 100644 index 11c683c662f..00000000000 --- a/packages/phone-number-privacy/signer/jest.config.js +++ /dev/null @@ -1,14 +0,0 @@ -const { nodeFlakeTracking } = require('@celo/flake-tracker/src/jest/config.js') - -module.exports = { - preset: 'ts-jest', - ...nodeFlakeTracking, - setupFiles: ['dotenv/config'], - coverageReporters: [['lcov', { projectRoot: '../../../' }], 'text'], - collectCoverageFrom: ['./src/**'], - coverageThreshold: { - global: { - lines: 80, - }, - }, -} diff --git a/packages/phone-number-privacy/signer/package.json b/packages/phone-number-privacy/signer/package.json deleted file mode 100644 index 17fef3b4541..00000000000 --- a/packages/phone-number-privacy/signer/package.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "name": "@celo/phone-number-privacy-signer", - "version": "3.0.0-dev", - "description": "Signing participator of ODIS", - "author": "Celo", - "license": "Apache-2.0", - "main": "dist/index.js", - "scripts": { - "start": "yarn build && node -r dotenv/config dist/index.js", - "start:docker": "yarn build && node dist/index.js", - "clean": "tsc -b . --clean", - "build": "tsc -b .", - "lint": "tslint --project .", - "test": "SKIP_KNOWN_FLAKES=false jest --testPathIgnorePatterns test/end-to-end", - "test:debughandles": "jest --watch --runInBand --detectOpenHandles --testPathIgnorePatterns test/end-to-end", - "test:debug": "node --inspect ../../../node_modules/.bin/jest --runInBand", - "test:coverage": "yarn test --coverage", - "test:integration": "jest --runInBand test/integration", - "test:integration:debugdb": "VERBOSE_DB_LOGGING=true jest --runInBand test/integration", - "test:e2e": "jest --runInBand test/end-to-end --testPathIgnorePatterns test/end-to-end/disabled-apis.test.ts", - "test:e2e:disabledapis": "jest --runInBand test/end-to-end/disabled-apis.test.ts", - "test:e2e:staging:0": "CONTEXT_NAME=staging ODIS_SIGNER_SERVICE_URL=https://staging-pgpnp-signer0.azurefd.net yarn test:e2e", - "test:e2e:staging:1": "CONTEXT_NAME=staging ODIS_SIGNER_SERVICE_URL=https://staging-pgpnp-signer1.azurefd.net yarn test:e2e", - "test:e2e:staging:2": "CONTEXT_NAME=staging ODIS_SIGNER_SERVICE_URL=https://staging-pgpnp-signer2.azurefd.net yarn test:e2e", - "test:e2e:alfajores:1": "CONTEXT_NAME=alfajores ODIS_SIGNER_SERVICE_URL=https://odis-alfajores-signer-1-b.azurefd.net yarn test:e2e", - "test:e2e:alfajores:2": "CONTEXT_NAME=alfajores ODIS_SIGNER_SERVICE_URL=https://odis-alfajores-signer2.azurefd.net yarn test:e2e", - "test:e2e:alfajores:3": "CONTEXT_NAME=alfajores ODIS_SIGNER_SERVICE_URL=https://odis-alfajores-signer3.azurefd.net yarn test:e2e", - "test:e2e:mainnet:westus2": "CONTEXT_NAME=mainnet ODIS_SIGNER_SERVICE_URL=https://mainnet-pgpnp-westus2.azurefd.net yarn test:e2e", - "test:e2e:mainnet:brazilsouth": "CONTEXT_NAME=mainnet ODIS_SIGNER_SERVICE_URL=https://mainnet-pgpnp-brazilsouth.azurefd.net yarn test:e2e", - "test:e2e:mainnet:eastasia": "CONTEXT_NAME=mainnet ODIS_SIGNER_SERVICE_URL=https://mainnet-pgpnp-eastasia.azurefd.net yarn test:e2e", - "test:e2e:mainnet:westeurope": "CONTEXT_NAME=mainnet ODIS_SIGNER_SERVICE_URL=https://mainnet-pgpnp-westeurope.azurefd.net yarn test:e2e", - "test:signer:mainnet": "MAINNET_ODIS_BLOCKCHAIN_PROVIDER=https://alfajores-forno.celo-testnet.org CONTEXT_NAME=mainnet yarn jest test/end-to-end -t='\\[Signer configuration test\\]'", - "db:migrate": "ts-node scripts/run-migrations.ts", - "db:migrate:make": "knex --migrations-directory ./src/common/database/migrations migrate:make -x ts", - "bls:keygen": "ts-node scripts/threshold-bls-keygen.ts", - "poprf:keygen": "ts-node scripts/poprf-keygen.ts", - "ssl:keygen": "./scripts/create-ssl-cert.sh" - }, - "dependencies": { - "@celo/base": "^4.1.1-dev", - "@celo/contractkit": "^4.1.1-dev", - "@celo/phone-number-privacy-common": "^3.0.0-dev", - "@celo/poprf": "^0.1.9", - "@celo/utils": "^4.1.1-dev", - "@celo/wallet-hsm-azure": "^4.1.1-dev", - "@google-cloud/secret-manager": "3.0.0", - "@types/bunyan": "^1.8.8", - "aws-sdk": "^2.705.0", - "blind-threshold-bls": "https://github.com/celo-org/blind-threshold-bls-wasm#e1e2f8a", - "dotenv": "^8.2.0", - "express": "^4.17.1", - "knex": "^2.1.0", - "mssql": "^6.3.1", - "mysql2": "^2.1.0", - "pg": "^8.2.1", - "prom-client": "12.0.0", - "promise.allsettled": "^1.0.2" - }, - "devDependencies": { - "@types/express": "^4.17.6", - "@types/supertest": "^2.0.12", - "sqlite3": "^5.0.8", - "supertest": "^6.2.3", - "ts-mockito": "^2.6.1", - "ts-node": "^8.3.0", - "typescript": "4.4.3" - }, - "peerDependencies": { - "@celo/flake-tracker": "0.0.1-dev" - }, - "engines": { - "node": ">=10" - } -} \ No newline at end of file diff --git a/packages/phone-number-privacy/signer/scripts/create-ssl-cert.sh b/packages/phone-number-privacy/signer/scripts/create-ssl-cert.sh deleted file mode 100755 index 5a0de974a45..00000000000 --- a/packages/phone-number-privacy/signer/scripts/create-ssl-cert.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -echo 'Creating a self-signed cert' -echo 'USE ONLY FOR DEVELOPMENT OR TESTING' - -openssl req -new -newkey rsa:4096 -days 1000 -nodes -x509 \ - -subj "/C=US/ST=CA/L=SF/O=Dis/CN=celo.org" \ - -keyout server.key -out server.cert \ No newline at end of file diff --git a/packages/phone-number-privacy/signer/scripts/poprf-keygen.ts b/packages/phone-number-privacy/signer/scripts/poprf-keygen.ts deleted file mode 100644 index 313e257f856..00000000000 --- a/packages/phone-number-privacy/signer/scripts/poprf-keygen.ts +++ /dev/null @@ -1,21 +0,0 @@ -// tslint:disable: no-console -import * as poprf from '@celo/poprf' -import crypto from 'crypto' - -const t = 2 -const n = 3 -console.log('Creating POPRF Threshold BLS keypairs with %s/%s ratio...', t, n) -console.log('USE ONLY FOR DEVELOPMENT OR TESTING') - -const seed = crypto.randomBytes(32) -const keys = poprf.thresholdKeygen(n, t, seed) -console.log('Private keys (hex):') -for (let i = 0; i < keys.numShares(); i++) { - console.log('Key #%s: %s', i + 1, Buffer.from(keys.getShare(i)).toString('hex')) -} - -console.log('Threshold Public key (base64):') -console.log(Buffer.from(keys.thresholdPublicKey).toString('base64')) - -console.log('Polynomial (hex):') -console.log(Buffer.from(keys.polynomial).toString('hex')) diff --git a/packages/phone-number-privacy/signer/scripts/run-migrations.ts b/packages/phone-number-privacy/signer/scripts/run-migrations.ts deleted file mode 100644 index 583e13b6b29..00000000000 --- a/packages/phone-number-privacy/signer/scripts/run-migrations.ts +++ /dev/null @@ -1,20 +0,0 @@ -// tslint:disable: no-console - -import { initDatabase } from '../src/common/database/database' -import { config } from '../src/config' - -async function start() { - console.info('Running migrations') - console.warn('It is no longer necessary to run db migrations seperately prior to startup') - await initDatabase(config, undefined, false) -} - -start() - .then(() => { - console.info('Migrations complete') - process.exit(0) - }) - .catch((e) => { - console.error('Migration failed', e) - process.exit(1) - }) diff --git a/packages/phone-number-privacy/signer/scripts/threshold-bls-keygen.ts b/packages/phone-number-privacy/signer/scripts/threshold-bls-keygen.ts deleted file mode 100644 index cc5172604ba..00000000000 --- a/packages/phone-number-privacy/signer/scripts/threshold-bls-keygen.ts +++ /dev/null @@ -1,21 +0,0 @@ -// tslint:disable: no-console -import threshold_bls from 'blind-threshold-bls' -import crypto from 'crypto' - -const t = 2 -const n = 3 -console.log('Creating OPRF Threshold BLS keypairs with %s/%s ratio...', t, n) -console.log('USE ONLY FOR DEVELOPMENT OR TESTING') - -const seed = crypto.randomBytes(32) -const keys = threshold_bls.thresholdKeygen(n, t, seed) -console.log('Private keys (hex):') -for (let i = 0; i < keys.numShares(); i++) { - console.log('Key #%s: %s', i + 1, Buffer.from(keys.getShare(i)).toString('hex')) -} - -console.log('Threshold Public key (base64):') -console.log(Buffer.from(keys.thresholdPublicKey).toString('base64')) - -console.log('Polynomial (hex):') -console.log(Buffer.from(keys.polynomial).toString('hex')) diff --git a/packages/phone-number-privacy/signer/src/common/action.ts b/packages/phone-number-privacy/signer/src/common/action.ts deleted file mode 100644 index 7b77c4b38eb..00000000000 --- a/packages/phone-number-privacy/signer/src/common/action.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { - DomainRequest, - OdisRequest, - PhoneNumberPrivacyRequest, -} from '@celo/phone-number-privacy-common' -import { SignerConfig } from '../config' -import { DomainSession } from '../domain/session' -import { PnpSession } from '../pnp/session' -import { IO } from './io' - -export type Session = R extends DomainRequest - ? DomainSession - : never | R extends PhoneNumberPrivacyRequest - ? PnpSession - : never - -export interface Action { - readonly config: SignerConfig - readonly io: IO - perform(session: Session, timeoutError: symbol): Promise -} diff --git a/packages/phone-number-privacy/signer/src/common/bls/bls-cryptography-client.ts b/packages/phone-number-privacy/signer/src/common/bls/bls-cryptography-client.ts deleted file mode 100644 index 6e8b2d0205f..00000000000 --- a/packages/phone-number-privacy/signer/src/common/bls/bls-cryptography-client.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { ErrorMessage } from '@celo/phone-number-privacy-common' -import threshold_bls from 'blind-threshold-bls' -import Logger from 'bunyan' -import { Counters } from '../metrics' -/* - * Computes the BLS signature for the blinded phone number. - */ -export function computeBlindedSignature( - base64BlindedMessage: string, - privateKey: string, - logger: Logger -) { - try { - const keyBuffer = Buffer.from(privateKey, 'hex') - const msgBuffer = Buffer.from(base64BlindedMessage, 'base64') - - logger.debug('Calling theshold sign') - const signedMsg = threshold_bls.partialSignBlindedMessage(keyBuffer, msgBuffer) - logger.debug('Back from threshold sign, parsing results') - - if (!signedMsg) { - throw new Error('Empty threshold sign result') - } - - return Buffer.from(signedMsg).toString('base64') - } catch (err) { - Counters.signatureComputationErrors.inc() - logger.error({ err }, ErrorMessage.SIGNATURE_COMPUTATION_FAILURE) - throw new Error(ErrorMessage.SIGNATURE_COMPUTATION_FAILURE) - } -} diff --git a/packages/phone-number-privacy/signer/src/common/controller.ts b/packages/phone-number-privacy/signer/src/common/controller.ts deleted file mode 100644 index 1854f930f20..00000000000 --- a/packages/phone-number-privacy/signer/src/common/controller.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { - ErrorMessage, - ErrorType, - OdisRequest, - OdisResponse, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import { Request, Response } from 'express' -import { Action } from './action' -import { Counters, Histograms, meter } from './metrics' - -export class Controller { - constructor(readonly action: Action) {} - - public async handle( - request: Request<{}, {}, unknown>, - response: Response> - ): Promise { - Counters.requests.labels(this.action.io.endpoint).inc() - // Unique error to be thrown on timeout - const timeoutError = Symbol() - await meter( - async () => { - const session = await this.action.io.init(request, response) - // Init returns a response to the user internally. - if (session) { - await this.action.perform(session, timeoutError) - } - }, - [], - (err: any) => { - response.locals.logger.error({ err }, `Error in handler for ${this.action.io.endpoint}`) - - let errMsg: ErrorType = ErrorMessage.UNKNOWN_ERROR - if (err === timeoutError) { - Counters.timeouts.inc() - errMsg = ErrorMessage.TIMEOUT_FROM_SIGNER - } else if ( - err instanceof Error && - // Propagate standard error & warning messages thrown during endpoint handling - (Object.values(ErrorMessage).includes(err.message as ErrorMessage) || - Object.values(WarningMessage).includes(err.message as WarningMessage)) - ) { - errMsg = err.message as ErrorType - } - this.action.io.sendFailure(errMsg, 500, response) - }, - Histograms.responseLatency, - [this.action.io.endpoint] - ) - } -} diff --git a/packages/phone-number-privacy/signer/src/common/database/database.ts b/packages/phone-number-privacy/signer/src/common/database/database.ts deleted file mode 100644 index cee48e7f7dc..00000000000 --- a/packages/phone-number-privacy/signer/src/common/database/database.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { rootLogger } from '@celo/phone-number-privacy-common' -import Logger from 'bunyan' -import { Knex, knex } from 'knex' -import { DEV_MODE, SignerConfig, SupportedDatabase, VERBOSE_DB_LOGGING } from '../../config' -import { ACCOUNTS_COLUMNS, ACCOUNTS_TABLE } from './models/account' - -export async function initDatabase( - config: SignerConfig, - migrationsPath?: string, - doTestQuery = true -): Promise { - const logger = rootLogger(config.serviceName) - logger.info({ config: config.db }, 'Initializing database connection') - const { type, host, port, user, password, database, ssl, poolMaxSize } = config.db - - let connection: any - let client: string - if (type === SupportedDatabase.Postgres) { - logger.info('Using Postgres') - client = 'pg' - connection = { - user, - password, - database, - host, - port: port ?? 5432, - ssl, - pool: { max: poolMaxSize }, - } - } else if (type === SupportedDatabase.MySql) { - logger.info('Using MySql') - client = 'mysql2' - connection = { - user, - password, - database, - host, - port: port ?? 3306, - ssl, - pool: { max: poolMaxSize }, - } - } else if (type === SupportedDatabase.MsSql) { - logger.info('Using MS SQL') - client = 'mssql' - connection = { - user, - password, - database, - server: host, - port: port ?? 1433, - pool: { max: poolMaxSize }, - } - } else if (type === SupportedDatabase.Sqlite) { - logger.info('Using SQLite') - client = 'sqlite3' - connection = ':memory:' - } else { - throw new Error(`Unsupported database type: ${type}`) - } - - const db = knex({ - client, - useNullAsDefault: type === SupportedDatabase.Sqlite, - connection, - debug: DEV_MODE && VERBOSE_DB_LOGGING, - }) - - logger.info('Running Migrations') - - await db.migrate.latest({ - directory: migrationsPath ?? './dist/common/database/migrations', - loadExtensions: ['.js'], - }) - - if (doTestQuery) { - await executeTestQuery(db, logger) - } - - logger.info('Database initialized successfully') - return db -} - -async function executeTestQuery(db: Knex, logger: Logger) { - logger.info('Counting accounts') - const result = await db(ACCOUNTS_TABLE.LEGACY).count(ACCOUNTS_COLUMNS.address).first() - - if (!result) { - throw new Error('No result from count, have migrations been run?') - } - - const count = Object.values(result)[0] - if (count === undefined || count === null || count === '') { - throw new Error('No result from count, have migrations been run?') - } - - logger.info(`Found ${count} accounts`) -} diff --git a/packages/phone-number-privacy/signer/src/common/database/migrations/20200330212224_create-accounts-table.ts b/packages/phone-number-privacy/signer/src/common/database/migrations/20200330212224_create-accounts-table.ts deleted file mode 100644 index fae5a17ca13..00000000000 --- a/packages/phone-number-privacy/signer/src/common/database/migrations/20200330212224_create-accounts-table.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Knex } from 'knex' -import { ACCOUNTS_COLUMNS, ACCOUNTS_TABLE } from '../models/account' - -export async function up(knex: Knex): Promise { - // This check was necessary to switch from using .ts migrations to .js migrations. - if (!(await knex.schema.hasTable(ACCOUNTS_TABLE.LEGACY))) { - return knex.schema.createTable(ACCOUNTS_TABLE.LEGACY, (t) => { - t.string(ACCOUNTS_COLUMNS.address).notNullable().primary() - t.dateTime(ACCOUNTS_COLUMNS.createdAt).notNullable() - t.integer(ACCOUNTS_COLUMNS.numLookups).unsigned() - }) - } - return null -} - -export async function down(knex: Knex): Promise { - return knex.schema.dropTable(ACCOUNTS_TABLE.LEGACY) -} diff --git a/packages/phone-number-privacy/signer/src/common/database/migrations/20200811163913_create_requests_table.ts b/packages/phone-number-privacy/signer/src/common/database/migrations/20200811163913_create_requests_table.ts deleted file mode 100644 index 8c8014725d5..00000000000 --- a/packages/phone-number-privacy/signer/src/common/database/migrations/20200811163913_create_requests_table.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Knex } from 'knex' -import { REQUESTS_COLUMNS, REQUESTS_TABLE } from '../models/request' - -export async function up(knex: Knex): Promise { - // This check was necessary to switch from using .ts migrations to .js migrations. - if (!(await knex.schema.hasTable(REQUESTS_TABLE.LEGACY))) { - return knex.schema.createTable(REQUESTS_TABLE.LEGACY, (t) => { - t.string(REQUESTS_COLUMNS.address).notNullable() - t.dateTime(REQUESTS_COLUMNS.timestamp).notNullable() - t.string(REQUESTS_COLUMNS.blindedQuery).notNullable() - t.primary([ - REQUESTS_COLUMNS.address, - REQUESTS_COLUMNS.timestamp, - REQUESTS_COLUMNS.blindedQuery, - ]) - }) - } - return null -} - -export async function down(knex: Knex): Promise { - return knex.schema.dropTable(REQUESTS_TABLE.LEGACY) -} diff --git a/packages/phone-number-privacy/signer/src/common/database/migrations/20210421212301_create-indices.ts b/packages/phone-number-privacy/signer/src/common/database/migrations/20210421212301_create-indices.ts deleted file mode 100644 index 94672330d33..00000000000 --- a/packages/phone-number-privacy/signer/src/common/database/migrations/20210421212301_create-indices.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Knex } from 'knex' -import { ACCOUNTS_COLUMNS, ACCOUNTS_TABLE } from '../models/account' - -export async function up(knex: Knex): Promise { - if (!(await knex.schema.hasTable(ACCOUNTS_TABLE.LEGACY))) { - throw new Error('Unexpected error: Could not find ACCOUNTS_TABLE.LEGACY') - } - return knex.schema.alterTable(ACCOUNTS_TABLE.LEGACY, (t) => { - t.index(ACCOUNTS_COLUMNS.address) - }) -} - -export async function down(knex: Knex): Promise { - return knex.schema.alterTable(ACCOUNTS_TABLE.LEGACY, (t) => { - t.dropIndex(ACCOUNTS_COLUMNS.address) - }) -} diff --git a/packages/phone-number-privacy/signer/src/common/database/migrations/20210921173354_create-domain-state.ts b/packages/phone-number-privacy/signer/src/common/database/migrations/20210921173354_create-domain-state.ts deleted file mode 100644 index cb8c7406e2d..00000000000 --- a/packages/phone-number-privacy/signer/src/common/database/migrations/20210921173354_create-domain-state.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Knex } from 'knex' -import { DOMAIN_STATE_COLUMNS } from '../models/domain-state' - -export async function up(knex: Knex): Promise { - // Due to a name change, the old migration uses the old names of these tables - // The change-name-domain-state migration then updates the name - // to match the value stored in DOMAIN_STATE_TABLE - if (!(await knex.schema.hasTable('domainsStates'))) { - return knex.schema.createTable('domainsStates', (t) => { - t.string(DOMAIN_STATE_COLUMNS.domainHash).notNullable().primary() - t.integer(DOMAIN_STATE_COLUMNS.counter).nullable() - t.boolean(DOMAIN_STATE_COLUMNS.disabled).notNullable().defaultTo(false) - t.integer(DOMAIN_STATE_COLUMNS.timer).nullable() - }) - } - - return null -} - -export async function down(knex: Knex): Promise { - return knex.schema.dropTable('domainsStates') -} diff --git a/packages/phone-number-privacy/signer/src/common/database/migrations/20220119165335_domain-requests.ts b/packages/phone-number-privacy/signer/src/common/database/migrations/20220119165335_domain-requests.ts deleted file mode 100644 index d5873ba0803..00000000000 --- a/packages/phone-number-privacy/signer/src/common/database/migrations/20220119165335_domain-requests.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Knex } from 'knex' -import { DOMAIN_REQUESTS_COLUMNS, DOMAIN_REQUESTS_TABLE } from '../models/domain-request' - -export async function up(knex: Knex): Promise { - if (!(await knex.schema.hasTable(DOMAIN_REQUESTS_TABLE))) { - return knex.schema.createTable(DOMAIN_REQUESTS_TABLE, (t) => { - t.string(DOMAIN_REQUESTS_COLUMNS.domainHash).notNullable() - // Note: this field was nullable in an older version of the migration. - // More context is included as part of this issue: - // https://github.com/celo-org/celo-monorepo/issues/9909 - t.dateTime(DOMAIN_REQUESTS_COLUMNS.timestamp).notNullable() - t.string(DOMAIN_REQUESTS_COLUMNS.blindedMessage).notNullable() - t.primary([ - DOMAIN_REQUESTS_COLUMNS.domainHash, - DOMAIN_REQUESTS_COLUMNS.timestamp, - DOMAIN_REQUESTS_COLUMNS.blindedMessage, - ]) - }) - } - - return null -} - -export async function down(knex: Knex): Promise { - return knex.schema.dropTable(DOMAIN_REQUESTS_TABLE) -} diff --git a/packages/phone-number-privacy/signer/src/common/database/migrations/20220923161710_pnp-requests-onchain.ts b/packages/phone-number-privacy/signer/src/common/database/migrations/20220923161710_pnp-requests-onchain.ts deleted file mode 100644 index 9e1dd91184e..00000000000 --- a/packages/phone-number-privacy/signer/src/common/database/migrations/20220923161710_pnp-requests-onchain.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Knex } from 'knex' -import { REQUESTS_COLUMNS, REQUESTS_TABLE } from '../models/request' - -export async function up(knex: Knex): Promise { - if (!(await knex.schema.hasTable(REQUESTS_TABLE.ONCHAIN))) { - return knex.schema.createTable(REQUESTS_TABLE.ONCHAIN, (t) => { - t.string(REQUESTS_COLUMNS.address).notNullable() - t.dateTime(REQUESTS_COLUMNS.timestamp).notNullable() - t.string(REQUESTS_COLUMNS.blindedQuery).notNullable() - t.primary([ - REQUESTS_COLUMNS.address, - REQUESTS_COLUMNS.timestamp, - REQUESTS_COLUMNS.blindedQuery, - ]) - }) - } - return null -} - -export async function down(knex: Knex): Promise { - return knex.schema.dropTable(REQUESTS_TABLE.ONCHAIN) -} diff --git a/packages/phone-number-privacy/signer/src/common/database/migrations/20220923165433_pnp-accounts-onchain.ts b/packages/phone-number-privacy/signer/src/common/database/migrations/20220923165433_pnp-accounts-onchain.ts deleted file mode 100644 index 8e3d3e16843..00000000000 --- a/packages/phone-number-privacy/signer/src/common/database/migrations/20220923165433_pnp-accounts-onchain.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Knex } from 'knex' -import { ACCOUNTS_COLUMNS, ACCOUNTS_TABLE } from '../models/account' - -export async function up(knex: Knex): Promise { - // This check was necessary to switch from using .ts migrations to .js migrations. - if (!(await knex.schema.hasTable(ACCOUNTS_TABLE.ONCHAIN))) { - return knex.schema.createTable(ACCOUNTS_TABLE.ONCHAIN, (t) => { - t.string(ACCOUNTS_COLUMNS.address).notNullable().primary().index() - t.dateTime(ACCOUNTS_COLUMNS.createdAt).notNullable() - t.integer(ACCOUNTS_COLUMNS.numLookups).unsigned() - }) - } - return null -} - -export async function down(knex: Knex): Promise { - return knex.schema.dropTable(ACCOUNTS_TABLE.ONCHAIN) -} diff --git a/packages/phone-number-privacy/signer/src/common/database/migrations/20221102141044_change-name-domain-state.ts b/packages/phone-number-privacy/signer/src/common/database/migrations/20221102141044_change-name-domain-state.ts deleted file mode 100644 index d1515a40f37..00000000000 --- a/packages/phone-number-privacy/signer/src/common/database/migrations/20221102141044_change-name-domain-state.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Knex } from 'knex' -import { DOMAIN_STATE_TABLE } from '../models/domain-state' - -// The original create-domain-state migration used a different name for DOMAIN_STATE_TABLE -export async function up(knex: Knex): Promise { - return knex.schema.renameTable('domainsStates', DOMAIN_STATE_TABLE) -} - -export async function down(knex: Knex): Promise { - return knex.schema.renameTable(DOMAIN_STATE_TABLE, 'domainsStates') -} diff --git a/packages/phone-number-privacy/signer/src/common/database/migrations/20221213125526_add-constraint-domain-requests.ts b/packages/phone-number-privacy/signer/src/common/database/migrations/20221213125526_add-constraint-domain-requests.ts deleted file mode 100644 index b86ff3c083d..00000000000 --- a/packages/phone-number-privacy/signer/src/common/database/migrations/20221213125526_add-constraint-domain-requests.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Knex } from 'knex' -import { DOMAIN_REQUESTS_COLUMNS, DOMAIN_REQUESTS_TABLE } from '../models/domain-request' - -// The goal of this migration is to ensure that the timestamp column is -// not nullable, to make sure that all signers' DB states end up in the same place -// despite the required change in the old migration from nullable -> nonNullable -// for the timestamp column (due to errors in MySQL). -// Revisit all of this when returning to: -// https://github.com/celo-org/celo-monorepo/issues/9909 -export async function up(knex: Knex): Promise { - return knex.schema.alterTable(DOMAIN_REQUESTS_TABLE, (t) => { - t.dropNullable(DOMAIN_REQUESTS_COLUMNS.timestamp) - }) -} - -export async function down(knex: Knex): Promise { - return knex.schema.alterTable(DOMAIN_REQUESTS_TABLE, (t) => { - t.setNullable(DOMAIN_REQUESTS_COLUMNS.timestamp) - }) -} diff --git a/packages/phone-number-privacy/signer/src/common/database/models/account.ts b/packages/phone-number-privacy/signer/src/common/database/models/account.ts deleted file mode 100644 index aab8bd3444b..00000000000 --- a/packages/phone-number-privacy/signer/src/common/database/models/account.ts +++ /dev/null @@ -1,24 +0,0 @@ -export enum ACCOUNTS_TABLE { - ONCHAIN = 'accountsOnChain', - LEGACY = 'accounts', -} - -export enum ACCOUNTS_COLUMNS { - address = 'address', - createdAt = 'created_at', - numLookups = 'num_lookups', -} - -export interface AccountRecord { - [ACCOUNTS_COLUMNS.address]: string - [ACCOUNTS_COLUMNS.createdAt]: Date - [ACCOUNTS_COLUMNS.numLookups]: number -} - -export function toAccountRecord(account: string, numLookups: number): AccountRecord { - return { - [ACCOUNTS_COLUMNS.address]: account, - [ACCOUNTS_COLUMNS.createdAt]: new Date(), - [ACCOUNTS_COLUMNS.numLookups]: numLookups, - } -} diff --git a/packages/phone-number-privacy/signer/src/common/database/models/domain-request.ts b/packages/phone-number-privacy/signer/src/common/database/models/domain-request.ts deleted file mode 100644 index 84a3a6fd959..00000000000 --- a/packages/phone-number-privacy/signer/src/common/database/models/domain-request.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Domain, domainHash } from '@celo/phone-number-privacy-common' - -export const DOMAIN_REQUESTS_TABLE = 'domainRequests' -export enum DOMAIN_REQUESTS_COLUMNS { - domainHash = 'domainHash', - timestamp = 'timestamp', - blindedMessage = 'blinded_message', -} - -export interface DomainRequestRecord { - [DOMAIN_REQUESTS_COLUMNS.domainHash]: string - [DOMAIN_REQUESTS_COLUMNS.timestamp]: Date - [DOMAIN_REQUESTS_COLUMNS.blindedMessage]: string -} - -export function toDomainRequestRecord( - domain: D, - blindedMessage: string -): DomainRequestRecord { - return { - [DOMAIN_REQUESTS_COLUMNS.domainHash]: domainHash(domain).toString('hex'), - [DOMAIN_REQUESTS_COLUMNS.timestamp]: new Date(), - [DOMAIN_REQUESTS_COLUMNS.blindedMessage]: blindedMessage, - } -} diff --git a/packages/phone-number-privacy/signer/src/common/database/models/domain-state.ts b/packages/phone-number-privacy/signer/src/common/database/models/domain-state.ts deleted file mode 100644 index ae1a964eced..00000000000 --- a/packages/phone-number-privacy/signer/src/common/database/models/domain-state.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { - Domain, - domainHash, - DomainState, - SequentialDelayDomainState, -} from '@celo/phone-number-privacy-common/lib/domains' - -export const DOMAIN_STATE_TABLE = 'domainState' -export enum DOMAIN_STATE_COLUMNS { - domainHash = 'domainHash', - counter = 'counter', - timer = 'timer', - disabled = 'disabled', -} - -export interface DomainStateRecord { - [DOMAIN_STATE_COLUMNS.domainHash]: string - [DOMAIN_STATE_COLUMNS.disabled]: boolean - [DOMAIN_STATE_COLUMNS.counter]: number - [DOMAIN_STATE_COLUMNS.timer]: number -} - -export function toDomainStateRecord( - domain: D, - domainState: DomainState -): DomainStateRecord { - return { - [DOMAIN_STATE_COLUMNS.domainHash]: domainHash(domain).toString('hex'), - [DOMAIN_STATE_COLUMNS.disabled]: domainState.disabled, - [DOMAIN_STATE_COLUMNS.counter]: domainState.counter, - [DOMAIN_STATE_COLUMNS.timer]: domainState.timer, - } -} - -export function toSequentialDelayDomainState( - record: DomainStateRecord, - attemptTime?: number -): SequentialDelayDomainState { - return { - disabled: record[DOMAIN_STATE_COLUMNS.disabled], - counter: record[DOMAIN_STATE_COLUMNS.counter], - timer: record[DOMAIN_STATE_COLUMNS.timer], - // Timestamp precision is lowered to seconds to reduce the chance of effective timing attacks. - now: attemptTime ?? Math.floor(Date.now() / 1000), - } -} diff --git a/packages/phone-number-privacy/signer/src/common/database/models/request.ts b/packages/phone-number-privacy/signer/src/common/database/models/request.ts deleted file mode 100644 index dcdb5ae5f75..00000000000 --- a/packages/phone-number-privacy/signer/src/common/database/models/request.ts +++ /dev/null @@ -1,27 +0,0 @@ -export enum REQUESTS_TABLE { - LEGACY = 'requests', - ONCHAIN = 'requestsOnChain', -} - -export enum REQUESTS_COLUMNS { - address = 'caller_address', - timestamp = 'timestamp', - blindedQuery = 'blinded_query', -} - -export interface PnpSignRequestRecord { - [REQUESTS_COLUMNS.address]: string - [REQUESTS_COLUMNS.timestamp]: Date - [REQUESTS_COLUMNS.blindedQuery]: string -} - -export function toPnpSignRequestRecord( - account: string, - blindedQuery: string -): PnpSignRequestRecord { - return { - [REQUESTS_COLUMNS.address]: account, - [REQUESTS_COLUMNS.timestamp]: new Date(), - [REQUESTS_COLUMNS.blindedQuery]: blindedQuery, - } -} diff --git a/packages/phone-number-privacy/signer/src/common/database/utils.ts b/packages/phone-number-privacy/signer/src/common/database/utils.ts deleted file mode 100644 index 4d2f8c03eef..00000000000 --- a/packages/phone-number-privacy/signer/src/common/database/utils.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { ErrorMessage } from '@celo/phone-number-privacy-common' -import Logger from 'bunyan' -import { Knex } from 'knex' -import { Counters, Labels } from '../metrics' - -export type DatabaseErrorMessage = - | ErrorMessage.DATABASE_GET_FAILURE - | ErrorMessage.DATABASE_INSERT_FAILURE - | ErrorMessage.DATABASE_UPDATE_FAILURE - -export function countAndThrowDBError( - err: any, - logger: Logger, - errorMsg: DatabaseErrorMessage -): T { - let label: Labels - switch (errorMsg) { - case ErrorMessage.DATABASE_UPDATE_FAILURE: - label = Labels.UPDATE - break - case ErrorMessage.DATABASE_GET_FAILURE: - label = Labels.READ - break - case ErrorMessage.DATABASE_INSERT_FAILURE: - label = Labels.INSERT - break - default: - throw new Error('Unknown database label provided') - } - - Counters.databaseErrors.labels(label).inc() - logger.error({ err }, errorMsg) - throw new Error(errorMsg) -} - -export function tableWithLockForTrx(baseQuery: Knex.QueryBuilder, trx?: Knex.Transaction) { - if (trx) { - // Lock relevant database rows for the duration of the transaction - return baseQuery.transacting(trx).forUpdate() - } - return baseQuery -} diff --git a/packages/phone-number-privacy/signer/src/common/database/wrappers/account.ts b/packages/phone-number-privacy/signer/src/common/database/wrappers/account.ts deleted file mode 100644 index b40af283d4c..00000000000 --- a/packages/phone-number-privacy/signer/src/common/database/wrappers/account.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { DB_TIMEOUT, ErrorMessage } from '@celo/phone-number-privacy-common' -import Logger from 'bunyan' -import { Knex } from 'knex' -import { Histograms, meter } from '../../metrics' -import { AccountRecord, ACCOUNTS_COLUMNS, ACCOUNTS_TABLE, toAccountRecord } from '../models/account' -import { countAndThrowDBError, tableWithLockForTrx } from '../utils' - -function accounts(db: Knex, table: ACCOUNTS_TABLE) { - return db(table) -} - -/* - * Returns how many queries the account has already performed. - */ -export async function getPerformedQueryCount( - db: Knex, - accountsTable: ACCOUNTS_TABLE, - account: string, - logger: Logger, - trx?: Knex.Transaction -): Promise { - return meter( - async () => { - logger.debug({ account }, 'Getting performed query count') - const queryCounts = await tableWithLockForTrx(accounts(db, accountsTable), trx) - .select(ACCOUNTS_COLUMNS.numLookups) - .where(ACCOUNTS_COLUMNS.address, account) - .first() - .timeout(DB_TIMEOUT) - return queryCounts === undefined ? 0 : queryCounts[ACCOUNTS_COLUMNS.numLookups] - }, - [], - (err: any) => countAndThrowDBError(err, logger, ErrorMessage.DATABASE_GET_FAILURE), - Histograms.dbOpsInstrumentation, - ['getPerformedQueryCount'] - ) -} - -async function getAccountExists( - db: Knex, - accountsTable: ACCOUNTS_TABLE, - account: string, - logger: Logger, - trx?: Knex.Transaction -): Promise { - return meter( - async () => { - const accountRecord = await tableWithLockForTrx(accounts(db, accountsTable), trx) - .where(ACCOUNTS_COLUMNS.address, account) - .first() - .timeout(DB_TIMEOUT) - - return !!accountRecord - }, - [], - (err: any) => countAndThrowDBError(err, logger, ErrorMessage.DATABASE_GET_FAILURE), - Histograms.dbOpsInstrumentation, - ['getAccountExists'] - ) -} - -/* - * Increments query count in database. If record doesn't exist, create one. - */ -export async function incrementQueryCount( - db: Knex, - accountsTable: ACCOUNTS_TABLE, - account: string, - logger: Logger, - trx: Knex.Transaction -): Promise { - return meter( - async () => { - logger.debug({ account }, 'Incrementing query count') - if (await getAccountExists(db, accountsTable, account, logger, trx)) { - await accounts(db, accountsTable) - .transacting(trx) - .where(ACCOUNTS_COLUMNS.address, account) - .increment(ACCOUNTS_COLUMNS.numLookups, 1) - .timeout(DB_TIMEOUT) - } else { - const newAccountRecord = toAccountRecord(account, 1) - await insertRecord(db, accountsTable, newAccountRecord, logger, trx) - } - }, - [], - (err: any) => countAndThrowDBError(err, logger, ErrorMessage.DATABASE_UPDATE_FAILURE), - Histograms.dbOpsInstrumentation, - ['incrementQueryCount'] - ) -} - -async function insertRecord( - db: Knex, - accountsTable: ACCOUNTS_TABLE, - data: AccountRecord, - logger: Logger, - trx: Knex.Transaction -): Promise { - try { - await accounts(db, accountsTable).transacting(trx).insert(data).timeout(DB_TIMEOUT) - } catch (error) { - countAndThrowDBError(error, logger, ErrorMessage.DATABASE_INSERT_FAILURE) - } -} diff --git a/packages/phone-number-privacy/signer/src/common/database/wrappers/domain-request.ts b/packages/phone-number-privacy/signer/src/common/database/wrappers/domain-request.ts deleted file mode 100644 index 3ce83ccfc67..00000000000 --- a/packages/phone-number-privacy/signer/src/common/database/wrappers/domain-request.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { DB_TIMEOUT, Domain, domainHash, ErrorMessage } from '@celo/phone-number-privacy-common' -import Logger from 'bunyan' -import { Knex } from 'knex' -import { Histograms, meter } from '../../metrics' -import { - DOMAIN_REQUESTS_COLUMNS, - DOMAIN_REQUESTS_TABLE, - DomainRequestRecord, - toDomainRequestRecord, -} from '../models/domain-request' -import { countAndThrowDBError } from '../utils' - -// TODO implement replay handling; this file is currently unused -// https://github.com/celo-org/celo-monorepo/issues/9909 - -function domainRequests(db: Knex) { - return db(DOMAIN_REQUESTS_TABLE) -} - -export async function getDomainRequestRecordExists( - db: Knex, - domain: D, - blindedMessage: string, - trx: Knex.Transaction, - logger: Logger -): Promise { - return meter( - async () => { - const hash = domainHash(domain).toString('hex') - logger.debug({ domain, blindedMessage, hash }, 'Checking if domain request exists') - const existingRequest = await domainRequests(db) - .transacting(trx) - .where({ - [DOMAIN_REQUESTS_COLUMNS.domainHash]: hash, - [DOMAIN_REQUESTS_COLUMNS.blindedMessage]: blindedMessage, - }) - .first() - .timeout(DB_TIMEOUT) - return !!existingRequest - }, - [], - (err: any) => countAndThrowDBError(err, logger, ErrorMessage.DATABASE_GET_FAILURE), - Histograms.dbOpsInstrumentation, - ['getDomainRequestRecordExists'] - ) -} - -export async function storeDomainRequestRecord( - db: Knex, - domain: D, - blindedMessage: string, - trx: Knex.Transaction, - logger: Logger -) { - return meter( - async () => { - logger.debug({ domain, blindedMessage }, 'Storing domain restricted signature request') - await domainRequests(db) - .transacting(trx) - .insert(toDomainRequestRecord(domain, blindedMessage)) - .timeout(DB_TIMEOUT) - }, - [], - (err: any) => countAndThrowDBError(err, logger, ErrorMessage.DATABASE_INSERT_FAILURE), - Histograms.dbOpsInstrumentation, - ['storeDomainRequestRecord'] - ) -} diff --git a/packages/phone-number-privacy/signer/src/common/database/wrappers/domain-state.ts b/packages/phone-number-privacy/signer/src/common/database/wrappers/domain-state.ts deleted file mode 100644 index 2c87d23db43..00000000000 --- a/packages/phone-number-privacy/signer/src/common/database/wrappers/domain-state.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { DB_TIMEOUT, ErrorMessage } from '@celo/phone-number-privacy-common' -import { Domain, domainHash } from '@celo/phone-number-privacy-common/lib/domains' -import Logger from 'bunyan' -import { Knex } from 'knex' -import { Histograms, meter } from '../../metrics' -import { - DOMAIN_STATE_COLUMNS, - DOMAIN_STATE_TABLE, - DomainStateRecord, - toDomainStateRecord, -} from '../models/domain-state' -import { countAndThrowDBError, tableWithLockForTrx } from '../utils' - -function domainStates(db: Knex) { - return db(DOMAIN_STATE_TABLE) -} - -export async function setDomainDisabled( - db: Knex, - domain: D, - trx: Knex.Transaction, - logger: Logger -): Promise { - return meter( - async () => { - const hash = domainHash(domain).toString('hex') - logger.debug({ hash, domain }, 'Disabling domain') - await domainStates(db) - .transacting(trx) - .where(DOMAIN_STATE_COLUMNS.domainHash, hash) - .update(DOMAIN_STATE_COLUMNS.disabled, true) - .timeout(DB_TIMEOUT) - }, - [], - (err: any) => countAndThrowDBError(err, logger, ErrorMessage.DATABASE_UPDATE_FAILURE), - Histograms.dbOpsInstrumentation, - ['disableDomain'] - ) -} - -export async function getDomainStateRecordOrEmpty( - db: Knex, - domain: Domain, - logger: Logger, - trx?: Knex.Transaction -): Promise { - return ( - (await getDomainStateRecord(db, domain, logger, trx)) ?? createEmptyDomainStateRecord(domain) - ) -} - -export function createEmptyDomainStateRecord(domain: Domain, disabled: boolean = false) { - return toDomainStateRecord(domain, { - timer: 0, - counter: 0, - disabled, - now: 0, - }) -} - -export async function getDomainStateRecord( - db: Knex, - domain: D, - logger: Logger, - trx?: Knex.Transaction -): Promise { - return meter( - async () => { - const hash = domainHash(domain).toString('hex') - logger.debug({ hash, domain }, 'Getting domain state from db') - const result = await tableWithLockForTrx(domainStates(db), trx) - .where(DOMAIN_STATE_COLUMNS.domainHash, hash) - .first() - .timeout(DB_TIMEOUT) - - // bools are stored in db as ints (1 or 0), so we must cast them back - if (result) { - result.disabled = !!result.disabled - } - - return result ?? null - }, - [], - (err: any) => countAndThrowDBError(err, logger, ErrorMessage.DATABASE_GET_FAILURE), - Histograms.dbOpsInstrumentation, - ['getDomainStateRecord'] - ) -} - -export async function updateDomainStateRecord( - db: Knex, - domain: D, - domainState: DomainStateRecord, - trx: Knex.Transaction, - logger: Logger -): Promise { - return meter( - async () => { - const hash = domainHash(domain).toString('hex') - logger.debug({ hash, domain, domainState }, 'Update domain state') - // Check whether the domain is already in the database. - // The current signature flow results in redundant queries of the domain state. - // Consider optimizing in the future: https://github.com/celo-org/celo-monorepo/issues/9855 - const result = await getDomainStateRecord(db, domain, logger, trx) - - // Insert or update the domain state record. - if (!result) { - await insertDomainStateRecord(db, domainState, trx, logger) - } else { - await domainStates(db) - .transacting(trx) - .where(DOMAIN_STATE_COLUMNS.domainHash, hash) - .update(domainState) - .timeout(DB_TIMEOUT) - } - }, - [], - (err: any) => countAndThrowDBError(err, logger, ErrorMessage.DATABASE_UPDATE_FAILURE), - Histograms.dbOpsInstrumentation, - ['updateDomainStateRecord'] - ) -} - -export async function insertDomainStateRecord( - db: Knex, - domainState: DomainStateRecord, - trx: Knex.Transaction, - logger: Logger -): Promise { - return meter( - async () => { - logger.debug({ domainState }, 'Insert domain state') - await domainStates(db).transacting(trx).insert(domainState).timeout(DB_TIMEOUT) - return domainState - }, - [], - (err: any) => countAndThrowDBError(err, logger, ErrorMessage.DATABASE_INSERT_FAILURE), - Histograms.dbOpsInstrumentation, - ['insertDomainState'] - ) -} diff --git a/packages/phone-number-privacy/signer/src/common/database/wrappers/request.ts b/packages/phone-number-privacy/signer/src/common/database/wrappers/request.ts deleted file mode 100644 index 6e90960ffb7..00000000000 --- a/packages/phone-number-privacy/signer/src/common/database/wrappers/request.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { DB_TIMEOUT, ErrorMessage } from '@celo/phone-number-privacy-common' -import Logger from 'bunyan' -import { Knex } from 'knex' -import { Histograms, meter } from '../../metrics' -import { - PnpSignRequestRecord, - REQUESTS_COLUMNS, - REQUESTS_TABLE, - toPnpSignRequestRecord, -} from '../models/request' -import { countAndThrowDBError, tableWithLockForTrx } from '../utils' - -function requests(db: Knex, table: REQUESTS_TABLE) { - return db(table) -} - -export async function getRequestExists( - db: Knex, - requestsTable: REQUESTS_TABLE, - account: string, - blindedQuery: string, - logger: Logger, - trx?: Knex.Transaction -): Promise { - return meter( - async () => { - logger.debug( - `Checking if request exists for account: ${account}, blindedQuery: ${blindedQuery}` - ) - const existingRequest = await tableWithLockForTrx(requests(db, requestsTable), trx) - .where({ - [REQUESTS_COLUMNS.address]: account, - [REQUESTS_COLUMNS.blindedQuery]: blindedQuery, - }) - .first() - .timeout(DB_TIMEOUT) - return !!existingRequest - }, - [], - (err: any) => countAndThrowDBError(err, logger, ErrorMessage.DATABASE_GET_FAILURE), - Histograms.dbOpsInstrumentation, - ['getRequestExists'] - ) -} - -export async function storeRequest( - db: Knex, - requestsTable: REQUESTS_TABLE, - account: string, - blindedQuery: string, - logger: Logger, - trx: Knex.Transaction -): Promise { - return meter( - async () => { - logger.debug(`Storing salt request for: ${account}, blindedQuery: ${blindedQuery}`) - await requests(db, requestsTable) - .transacting(trx) - .insert(toPnpSignRequestRecord(account, blindedQuery)) - .timeout(DB_TIMEOUT) - }, - [], - (err: any) => countAndThrowDBError(err, logger, ErrorMessage.DATABASE_INSERT_FAILURE), - Histograms.dbOpsInstrumentation, - ['storeRequest'] - ) -} diff --git a/packages/phone-number-privacy/signer/src/common/io.ts b/packages/phone-number-privacy/signer/src/common/io.ts deleted file mode 100644 index 96df9e8115b..00000000000 --- a/packages/phone-number-privacy/signer/src/common/io.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { - ErrorType, - FailureResponse, - OdisRequest, - OdisResponse, - SignerEndpoint, - SuccessResponse, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import Logger from 'bunyan' -import { Request, Response } from 'express' -import { Session } from './action' - -export abstract class IO { - abstract readonly endpoint: SignerEndpoint - - constructor(readonly enabled: boolean) {} - - abstract init( - request: Request<{}, {}, unknown>, - response: Response> - ): Promise | null> - - abstract validate(request: Request<{}, {}, unknown>): request is Request<{}, {}, R> - - abstract authenticate( - request: Request<{}, {}, R>, - warnings?: string[], - logger?: Logger - ): Promise - - abstract sendFailure( - error: ErrorType, - status: number, - response: Response>, - ...args: unknown[] - ): void - - abstract sendSuccess( - status: number, - response: Response>, - ...args: unknown[] - ): void - - protected inputChecks( - request: Request<{}, {}, unknown>, - response: Response> - ): request is Request<{}, {}, R> { - if (!this.enabled) { - this.sendFailure(WarningMessage.API_UNAVAILABLE, 503, response) - return false - } - if (!this.validate(request)) { - this.sendFailure(WarningMessage.INVALID_INPUT, 400, response) - return false - } - return true - } -} diff --git a/packages/phone-number-privacy/signer/src/common/key-management/aws-key-provider.ts b/packages/phone-number-privacy/signer/src/common/key-management/aws-key-provider.ts deleted file mode 100644 index 338af55b8c8..00000000000 --- a/packages/phone-number-privacy/signer/src/common/key-management/aws-key-provider.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { ErrorMessage, rootLogger } from '@celo/phone-number-privacy-common' -import { SecretsManager } from 'aws-sdk' -import { config } from '../../config' -import { Key, KeyProviderBase } from './key-provider-base' - -interface SecretStringResult { - [key: string]: string -} - -export class AWSKeyProvider extends KeyProviderBase { - public async fetchPrivateKeyFromStore(key: Key) { - const logger = rootLogger(config.serviceName) - try { - // Credentials are managed by AWS client as described in https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/setting-credentials-node.html - const { region, secretKey } = config.keystore.aws - const client = new SecretsManager({ region }) - client.config.update({ region }) - - const customKeyVersionString = this.getCustomKeyVersionString(key) - logger.debug(`Attempting to fetch key named: ${customKeyVersionString}`) - const privateKey = await this.fetch(client, customKeyVersionString, secretKey) - this.setPrivateKey(key, privateKey) - } catch (err) { - logger.info('Error retrieving key') - logger.error(err) - throw new Error(ErrorMessage.KEY_FETCH_ERROR) - } - } - - private async fetch(client: SecretsManager, secretName: string, secretKey: string) { - // check for empty strings from undefined env vars - if (!secretName) { - throw new Error('key name is undefined') - } - - const response = await client.getSecretValue({ SecretId: secretName }).promise() - - let privateKey - if (response.SecretString) { - privateKey = this.tryParseSecretString(response.SecretString, secretKey) - } else if (response.SecretBinary) { - // @ts-ignore AWS sdk typings not quite correct - const buff = Buffer.from(response.SecretBinary, 'base64') - privateKey = buff.toString('ascii') - } else { - throw new Error('Response has neither string nor binary') - } - - if (!privateKey) { - throw new Error('Secret is empty or undefined') - } - - return privateKey - } - - private tryParseSecretString(secretString: string, key: string) { - if (!secretString) { - throw new Error('Cannot parse empty string') - } - if (!key) { - throw new Error('Cannot parse secret without key') - } - - try { - const secret = JSON.parse(secretString) as SecretStringResult - return secret[key] - } catch (e) { - throw new Error('Expecting JSON, secret string is not valid JSON') - } - } -} diff --git a/packages/phone-number-privacy/signer/src/common/key-management/azure-key-provider.ts b/packages/phone-number-privacy/signer/src/common/key-management/azure-key-provider.ts deleted file mode 100644 index d6c5a675816..00000000000 --- a/packages/phone-number-privacy/signer/src/common/key-management/azure-key-provider.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { ErrorMessage, rootLogger } from '@celo/phone-number-privacy-common' -import { AzureKeyVaultClient } from '@celo/wallet-hsm-azure' -import { config } from '../../config' -import { Key, KeyProviderBase } from './key-provider-base' - -export class AzureKeyProvider extends KeyProviderBase { - public async fetchPrivateKeyFromStore(key: Key) { - const logger = rootLogger(config.serviceName) - try { - const { vaultName } = config.keystore.azure - const client = new AzureKeyVaultClient(vaultName) - - const customKeyVersionString = this.getCustomKeyVersionString(key) - logger.debug(`Attempting to fetch key named: ${customKeyVersionString}`) - const privateKey = await this.fetch(client, customKeyVersionString) - this.setPrivateKey(key, privateKey) - } catch (err) { - logger.info('Error retrieving key') - logger.error(err) - throw new Error(ErrorMessage.KEY_FETCH_ERROR) - } - } - - private async fetch(client: AzureKeyVaultClient, secretName: string) { - // check for empty strings from undefined env vars - if (!secretName) { - throw new Error('key name is undefined') - } - - const privateKey = await client.getSecret(secretName) - - if (!privateKey) { - throw new Error('Key is empty or undefined') - } - - return privateKey - } -} diff --git a/packages/phone-number-privacy/signer/src/common/key-management/google-key-provider.ts b/packages/phone-number-privacy/signer/src/common/key-management/google-key-provider.ts deleted file mode 100644 index 0d3c7f9fa08..00000000000 --- a/packages/phone-number-privacy/signer/src/common/key-management/google-key-provider.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { ErrorMessage, rootLogger } from '@celo/phone-number-privacy-common' -import { SecretManagerServiceClient } from '@google-cloud/secret-manager/build/src/v1' -import { config } from '../../config' -import { Key, KeyProviderBase } from './key-provider-base' - -export class GoogleKeyProvider extends KeyProviderBase { - public async fetchPrivateKeyFromStore(key: Key) { - const logger = rootLogger(config.serviceName) - try { - const { projectId } = config.keystore.google - const client = new SecretManagerServiceClient() - - const customKeyName = this.getCustomKeyName(key) - const keyVersion = key.version.toString() - logger.debug(`Attempting to fetch key named: ${customKeyName}, version: ${keyVersion}`) - const privateKey = await this.fetch(client, projectId, customKeyName, keyVersion) - this.setPrivateKey(key, privateKey) - } catch (err) { - logger.info('Error retrieving key') - logger.error(err) - throw new Error(ErrorMessage.KEY_FETCH_ERROR) - } - } - - private async fetch( - client: SecretManagerServiceClient, - projectId: string, - secretName: string, - secretVersion: string - ) { - // check for empty strings from undefined env vars - if (!(projectId && secretName && secretVersion)) { - throw new Error('key name is undefined') - } - const secretID = `projects/${projectId}/secrets/${secretName}/versions/${secretVersion}` - const [versionResponse] = await client.accessSecretVersion({ name: secretID }) - - // Extract the payload as a string. - const privateKey = versionResponse?.payload?.data?.toString() - - if (!privateKey) { - throw new Error('Key is empty or undefined') - } - - return privateKey - } -} diff --git a/packages/phone-number-privacy/signer/src/common/key-management/key-provider-base.ts b/packages/phone-number-privacy/signer/src/common/key-management/key-provider-base.ts deleted file mode 100644 index db1f8e03fcd..00000000000 --- a/packages/phone-number-privacy/signer/src/common/key-management/key-provider-base.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { config } from '../../config' - -export enum DefaultKeyName { - PHONE_NUMBER_PRIVACY = 'phoneNumberPrivacy', - DOMAINS = 'domains', -} -export interface Key { - name: DefaultKeyName - version: number -} -export interface KeyProvider { - fetchPrivateKeyFromStore: (key: Key) => Promise - getPrivateKey: (key: Key) => string - getPrivateKeyOrFetchFromStore: (key: Key) => Promise -} - -const PRIVATE_KEY_SIZE = 72 - -export abstract class KeyProviderBase implements KeyProvider { - protected privateKeys: Map - - constructor() { - this.privateKeys = new Map() - } - - public getPrivateKey(key: Key) { - const privateKey = this.privateKeys.get(this.getCustomKeyVersionString(key)) - if (!privateKey) { - throw new Error(`Private key is unavailable: ${key}`) - } - return privateKey - } - - public async getPrivateKeyOrFetchFromStore(key: Key): Promise { - if (key.version < 0) { - throw new Error('Invalid private key version. Key version must be a positive integer.') - } - try { - return this.getPrivateKey(key) - } catch { - await this.fetchPrivateKeyFromStore(key) - return this.getPrivateKey(key) - } - } - - public abstract fetchPrivateKeyFromStore(key: Key): Promise - - getCustomKeyVersionString(key: Key): string { - return `${this.getCustomKeyName(key)}-${key.version}` - } - - protected setPrivateKey(key: Key, privateKey: string) { - privateKey = privateKey ? privateKey.trim() : '' - if (privateKey.length !== PRIVATE_KEY_SIZE) { - throw new Error('Invalid private key') - } - this.privateKeys.set(this.getCustomKeyVersionString(key), privateKey) - } - - protected getCustomKeyName(key: Key) { - switch (key.name) { - case DefaultKeyName.PHONE_NUMBER_PRIVACY: - return config.keystore.keys.phoneNumberPrivacy.name || key.name - case DefaultKeyName.DOMAINS: - return config.keystore.keys.domains.name || key.name - default: - return key.name - } - } -} diff --git a/packages/phone-number-privacy/signer/src/common/key-management/key-provider.ts b/packages/phone-number-privacy/signer/src/common/key-management/key-provider.ts deleted file mode 100644 index 6e6e9a384d1..00000000000 --- a/packages/phone-number-privacy/signer/src/common/key-management/key-provider.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { rootLogger } from '@celo/phone-number-privacy-common' -import { SignerConfig, SupportedKeystore } from '../../config' -import { AWSKeyProvider } from './aws-key-provider' -import { AzureKeyProvider } from './azure-key-provider' -import { GoogleKeyProvider } from './google-key-provider' -import { DefaultKeyName, Key, KeyProvider } from './key-provider-base' -import { MockKeyProvider } from './mock-key-provider' - -export function keysToPrefetch(config: SignerConfig): Key[] { - return [ - { - name: DefaultKeyName.PHONE_NUMBER_PRIVACY, - version: config.keystore.keys.phoneNumberPrivacy.latest, - }, - { - name: DefaultKeyName.DOMAINS, - version: config.keystore.keys.domains.latest, - }, - ] -} - -export async function initKeyProvider(config: SignerConfig): Promise { - const logger = rootLogger(config.serviceName) - logger.info('Initializing keystore') - const type = config.keystore.type - - let keyProvider: KeyProvider - - if (type === SupportedKeystore.AZURE_KEY_VAULT) { - logger.info('Using Azure key vault') - keyProvider = new AzureKeyProvider() - } else if (type === SupportedKeystore.GOOGLE_SECRET_MANAGER) { - logger.info('Using Google Secret Manager') - keyProvider = new GoogleKeyProvider() - } else if (type === SupportedKeystore.AWS_SECRET_MANAGER) { - logger.info('Using AWS Secret Manager') - keyProvider = new AWSKeyProvider() - } else if (type === SupportedKeystore.MOCK_SECRET_MANAGER) { - logger.info('Using Mock Secret Manager') - keyProvider = new MockKeyProvider() - } else { - throw new Error('Valid keystore type must be provided') - } - - logger.info(`Fetching keys: ${JSON.stringify(keysToPrefetch(config))}`) - await Promise.all( - keysToPrefetch(config).map(keyProvider.fetchPrivateKeyFromStore.bind(keyProvider)) - ) - logger.info('Done fetching key. Key provider initialized successfully.') - - return keyProvider -} diff --git a/packages/phone-number-privacy/signer/src/common/key-management/mock-key-provider.ts b/packages/phone-number-privacy/signer/src/common/key-management/mock-key-provider.ts deleted file mode 100644 index 8d3e11b5b28..00000000000 --- a/packages/phone-number-privacy/signer/src/common/key-management/mock-key-provider.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { - DOMAINS_THRESHOLD_DEV_PK_SHARE_1_V1, - DOMAINS_THRESHOLD_DEV_PK_SHARE_1_V2, - DOMAINS_THRESHOLD_DEV_PK_SHARE_1_V3, - PNP_THRESHOLD_DEV_PK_SHARE_1_V1, - PNP_THRESHOLD_DEV_PK_SHARE_1_V2, - PNP_THRESHOLD_DEV_PK_SHARE_1_V3, -} from '@celo/phone-number-privacy-common/lib/test/values' -import { DefaultKeyName, Key, KeyProviderBase } from './key-provider-base' - -export class MockKeyProvider extends KeyProviderBase { - // prettier-ignore - constructor( - private keyMocks: Map = new Map([ - [ - `${DefaultKeyName.PHONE_NUMBER_PRIVACY}-1`, - PNP_THRESHOLD_DEV_PK_SHARE_1_V1 - ], - [ - `${DefaultKeyName.PHONE_NUMBER_PRIVACY}-2`, - PNP_THRESHOLD_DEV_PK_SHARE_1_V2, - ], - [ - `${DefaultKeyName.PHONE_NUMBER_PRIVACY}-3`, - PNP_THRESHOLD_DEV_PK_SHARE_1_V3 - ], - [ - `${DefaultKeyName.DOMAINS}-1`, - DOMAINS_THRESHOLD_DEV_PK_SHARE_1_V1 - ], - [ - `${DefaultKeyName.DOMAINS}-2`, - DOMAINS_THRESHOLD_DEV_PK_SHARE_1_V2, - ], - [ - `${DefaultKeyName.DOMAINS}-3`, - DOMAINS_THRESHOLD_DEV_PK_SHARE_1_V3 - ], - ]) - ) { - super() - } - - public async fetchPrivateKeyFromStore(key: Key) { - const keyString = this.keyMocks.get(this.getCustomKeyVersionString(key)) - if (keyString) { - return this.setPrivateKey(key, keyString) - } - throw new Error('unknown key for MockKeyProvider') - } -} diff --git a/packages/phone-number-privacy/signer/src/common/metrics.ts b/packages/phone-number-privacy/signer/src/common/metrics.ts deleted file mode 100644 index 918cbfd7df6..00000000000 --- a/packages/phone-number-privacy/signer/src/common/metrics.ts +++ /dev/null @@ -1,130 +0,0 @@ -import * as client from 'prom-client' -const { Counter, Histogram } = client - -client.collectDefaultMetrics() - -// This is just so autocomplete will remind devs what the options are. -export enum Labels { - READ = 'read', - UPDATE = 'update', - INSERT = 'insert', -} - -export const Counters = { - requests: new Counter({ - name: 'requests', - help: 'Counter for the number of requests received', - labelNames: ['endpoint'], - }), - responses: new Counter({ - name: 'responses', - help: 'Counter for the number of responses sent', - labelNames: ['endpoint', 'statusCode'], - }), - databaseErrors: new Counter({ - name: 'database_errors', - help: 'Counter for the number of database errors', - labelNames: ['type'], - }), - blockchainErrors: new Counter({ - name: 'blockchain_errors', - help: 'Counter for the number of errors from interacting with the blockchain', - labelNames: ['type'], - }), - signatureComputationErrors: new Counter({ - name: 'signature_computation_errors', - help: 'Counter for the number of signature computation errors', - }), - duplicateRequests: new Counter({ - name: 'duplicate_requests', - help: 'Counter for the number of duplicate signature requests received', - }), - requestsWithWalletAddress: new Counter({ - name: 'requests_with_wallet_address', - help: 'Counter for the number of requests in which the account uses a different wallet address', - }), - requestsWithVerifiedAccount: new Counter({ - name: 'requests_with_verified_account', - help: 'Counter for the number of requests in which the account is verified', - }), - requestsWithUnverifiedAccountWithMinBalance: new Counter({ - name: 'requests_with_unverified_account_with_min_balance', - help: 'Counter for the number of requests in which the account is not verified but meets min balance', - }), - testQuotaBypassedRequests: new Counter({ - name: 'test_quota_bypassed_requests', - help: 'Counter for the number of requests not requiring quota (testing only)', - }), - timeouts: new Counter({ - name: 'timeouts', - help: 'Counter for the number of signer timeouts as measured by the signer', - }), - requestsFailingOpen: new Counter({ - name: 'requests_failing_open', - help: 'Counter for the number of requests bypassing quota or authentication checks due to full-node errors', - }), - requestsFailingClosed: new Counter({ - name: 'requests_failing_closed', - help: 'Counter for the number of requests failing quota or authentication checks due to full-node errors', - }), - errorsCaughtInEndpointHandler: new Counter({ - name: 'errors_caught_in_endpoint_handler', - help: 'Counter for the number of errors caught in the outermost endpoint handler', - }), - errorsThrownAfterResponseSent: new Counter({ - name: 'errors_thrown_after_response_sent', - help: 'Counter for the number of errors thrown after a response was already sent', - }), -} -const buckets = [ - 0.001, 0.01, 0.1, 0.2, 0.3, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.2, 1.4, 1.6, 1.8, 2, 2.3, 2.6, 2.9, 3.5, - 4, 5, 10, -] - -export const Histograms = { - responseLatency: new Histogram({ - name: 'signature_endpoint_latency', - help: 'Histogram tracking latency of signature endpoint', - labelNames: ['endpoint'], - buckets, - }), - getBlindedSigInstrumentation: new Histogram({ - name: 'get_blinded_sig_instrumentation', - help: 'Histogram tracking latency of blinded sig function by code segment', - labelNames: ['codeSegment'], - buckets, - }), - getRemainingQueryCountInstrumentation: new Histogram({ - name: 'get_remaining_query_count_instrumentation', - help: 'Histogram tracking latency of getRemainingQueryCount function by code segment', - labelNames: ['codeSegment', 'endpoint'], - buckets, - }), - dbOpsInstrumentation: new Histogram({ - name: 'db_ops_instrumentation', - help: 'Histogram tracking latency of all database operations', - labelNames: ['operation'], - buckets, - }), - userRemainingQuotaAtRequest: new Histogram({ - name: 'user_remaining_quota_at_request', - help: 'Histogram tracking remaining quota of users at time of request', - labelNames: ['endpoint'], - buckets, - }), -} - -declare type InFunction = (...params: T) => Promise - -export async function meter( - inFunction: InFunction, - params: T, - onError: (err: any) => U, - prometheus: client.Histogram, - labels: string[] -): Promise { - const _meter = prometheus.labels(...labels).startTimer() - return inFunction(...params) - .catch(onError) - .finally(_meter) -} diff --git a/packages/phone-number-privacy/signer/src/common/quota.ts b/packages/phone-number-privacy/signer/src/common/quota.ts deleted file mode 100644 index 7900eaf7473..00000000000 --- a/packages/phone-number-privacy/signer/src/common/quota.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { - DomainQuotaStatusRequest, - DomainRestrictedSignatureRequest, - OdisRequest, - PnpQuotaRequest, - PnpQuotaStatus, - SignMessageRequest, -} from '@celo/phone-number-privacy-common' -import { Knex } from 'knex' -import { Session } from './action' -import { DomainStateRecord } from './database/models/domain-state' - -// prettier-ignore -export type OdisQuotaStatus = R extends - | DomainQuotaStatusRequest | DomainRestrictedSignatureRequest ? DomainStateRecord : never - | R extends SignMessageRequest | PnpQuotaRequest ? PnpQuotaStatus: never - -export interface OdisQuotaStatusResult { - sufficient: boolean - state: OdisQuotaStatus -} - -export interface QuotaService { - checkAndUpdateQuotaStatus( - state: OdisQuotaStatus, - session: Session, - trx: Knex.Transaction> - ): Promise> - - getQuotaStatus( - session: Session, - trx?: Knex.Transaction> - ): Promise> -} diff --git a/packages/phone-number-privacy/signer/src/common/web3/contracts.ts b/packages/phone-number-privacy/signer/src/common/web3/contracts.ts deleted file mode 100644 index 7b0801f8f3b..00000000000 --- a/packages/phone-number-privacy/signer/src/common/web3/contracts.ts +++ /dev/null @@ -1,203 +0,0 @@ -import { NULL_ADDRESS, retryAsyncWithBackOffAndTimeout } from '@celo/base' -import { ContractKit, StableToken } from '@celo/contractkit' -import { - FULL_NODE_TIMEOUT_IN_MS, - RETRY_COUNT, - RETRY_DELAY_IN_MS, -} from '@celo/phone-number-privacy-common' -import { BigNumber } from 'bignumber.js' -import Logger from 'bunyan' -import { Counters, Histograms, Labels, meter } from '../metrics' - -export async function getBlockNumber(kit: ContractKit): Promise { - return meter( - retryAsyncWithBackOffAndTimeout, - [ - () => kit.connection.getBlockNumber(), - RETRY_COUNT, - [], - RETRY_DELAY_IN_MS, - undefined, - FULL_NODE_TIMEOUT_IN_MS, - ], - (err: any) => { - Counters.blockchainErrors.labels(Labels.READ).inc() - throw err - }, - Histograms.getBlindedSigInstrumentation, - ['getBlockNumber'] - ) -} - -export async function getTransactionCount( - kit: ContractKit, - logger: Logger, - endpoint: string, - ...addresses: string[] -): Promise { - const _getTransactionCount = (...params: string[]) => - Promise.all( - params - .filter((address) => address !== NULL_ADDRESS) - .map((address) => - retryAsyncWithBackOffAndTimeout( - () => kit.connection.getTransactionCount(address), - RETRY_COUNT, - [], - RETRY_DELAY_IN_MS, - undefined, - FULL_NODE_TIMEOUT_IN_MS - ).catch((err) => { - Counters.blockchainErrors.labels(Labels.READ).inc() - throw err - }) - ) - ).then((values) => { - logger.trace({ addresses, txCounts: values }, 'Fetched txCounts for addresses') - return values.reduce((a, b) => a + b) - }) - return meter( - _getTransactionCount, - addresses.filter((address) => address !== NULL_ADDRESS), - (err: any) => { - throw err - }, - Histograms.getRemainingQueryCountInstrumentation, - ['getTransactionCount', endpoint] - ) -} - -export async function getStableTokenBalance( - kit: ContractKit, - stableToken: StableToken, - logger: Logger, - endpoint: string, - ...addresses: string[] -): Promise { - const _getStableTokenBalance = (...params: string[]) => - Promise.all( - params - .filter((address) => address !== NULL_ADDRESS) - .map((address) => - retryAsyncWithBackOffAndTimeout( - async () => (await kit.contracts.getStableToken(stableToken)).balanceOf(address), - RETRY_COUNT, - [], - RETRY_DELAY_IN_MS, - undefined, - FULL_NODE_TIMEOUT_IN_MS - ).catch((err) => { - Counters.blockchainErrors.labels(Labels.READ).inc() - throw err - }) - ) - ).then((values) => { - logger.trace( - { addresses, balances: values.map((bn) => bn.toString()) }, - `Fetched ${stableToken} balances for addresses` - ) - return values.reduce((a, b) => a.plus(b)) - }) - return meter( - _getStableTokenBalance, - addresses, - (err: any) => { - throw err - }, - Histograms.getRemainingQueryCountInstrumentation, - ['getStableTokenBalance', endpoint] - ) -} - -export async function getCeloBalance( - kit: ContractKit, - logger: Logger, - endpoint: string, - ...addresses: string[] -): Promise { - const _getCeloBalance = (...params: string[]) => - Promise.all( - params - .filter((address) => address !== NULL_ADDRESS) - .map((address) => - retryAsyncWithBackOffAndTimeout( - async () => (await kit.contracts.getGoldToken()).balanceOf(address), - RETRY_COUNT, - [], - RETRY_DELAY_IN_MS, - undefined, - FULL_NODE_TIMEOUT_IN_MS - ).catch((err) => { - Counters.blockchainErrors.labels(Labels.READ).inc() - throw err - }) - ) - ).then((values) => { - logger.trace( - { addresses, balances: values.map((bn) => bn.toString()) }, - 'Fetched celo balances for addresses' - ) - return values.reduce((a, b) => a.plus(b)) - }) - return meter( - _getCeloBalance, - addresses, - (err: any) => { - throw err - }, - Histograms.getRemainingQueryCountInstrumentation, - ['getStableTokenBalance', endpoint] - ) -} - -export async function getWalletAddress( - kit: ContractKit, - logger: Logger, - account: string, - endpoint: string -): Promise { - return meter( - retryAsyncWithBackOffAndTimeout, - [ - async () => (await kit.contracts.getAccounts()).getWalletAddress(account), - RETRY_COUNT, - [], - RETRY_DELAY_IN_MS, - undefined, - FULL_NODE_TIMEOUT_IN_MS, - ], - (err: any) => { - logger.error({ err, account }, 'failed to get wallet address for account') - Counters.blockchainErrors.labels(Labels.READ).inc() - return NULL_ADDRESS - }, - Histograms.getRemainingQueryCountInstrumentation, - ['getWalletAddress', endpoint] - ) -} - -export async function getOnChainOdisPayments( - kit: ContractKit, - logger: Logger, - account: string, - endpoint: string -): Promise { - return meter( - retryAsyncWithBackOffAndTimeout, - [ - async () => (await kit.contracts.getOdisPayments()).totalPaidCUSD(account), - RETRY_COUNT, - [], - RETRY_DELAY_IN_MS, - undefined, - FULL_NODE_TIMEOUT_IN_MS, - ], - (err: any) => { - logger.error({ err, account }, 'failed to get on-chain odis balance for account') - Counters.blockchainErrors.labels(Labels.READ).inc() - throw err - }, - Histograms.getRemainingQueryCountInstrumentation, - ['getOnChainOdisPayments', endpoint] - ) -} diff --git a/packages/phone-number-privacy/signer/src/config.ts b/packages/phone-number-privacy/signer/src/config.ts deleted file mode 100644 index 588a95dd3ee..00000000000 --- a/packages/phone-number-privacy/signer/src/config.ts +++ /dev/null @@ -1,184 +0,0 @@ -import { - BlockchainConfig, - FULL_NODE_TIMEOUT_IN_MS, - toBool, -} from '@celo/phone-number-privacy-common' -import BigNumber from 'bignumber.js' - -require('dotenv').config() - -export function getSignerVersion(): string { - return process.env.npm_package_version ?? '0.0.0' -} -export const DEV_MODE = process.env.NODE_ENV !== 'production' -export const VERBOSE_DB_LOGGING = toBool(process.env.VERBOSE_DB_LOGGING, false) - -export enum SupportedDatabase { - Postgres = 'postgres', // PostgresSQL - MySql = 'mysql', // MySQL - MsSql = 'mssql', // Microsoft SQL Server - Sqlite = 'sqlite3', // SQLite (for testing) -} - -export enum SupportedKeystore { - AZURE_KEY_VAULT = 'AzureKeyVault', - GOOGLE_SECRET_MANAGER = 'GoogleSecretManager', - AWS_SECRET_MANAGER = 'AWSSecretManager', - MOCK_SECRET_MANAGER = 'MockSecretManager', -} - -export interface SignerConfig { - serviceName: string - server: { - port: string | number | undefined - sslKeyPath?: string - sslCertPath?: string - } - quota: { - unverifiedQueryMax: number - additionalVerifiedQueryMax: number - queryPerTransaction: number - minDollarBalance: BigNumber - minEuroBalance: BigNumber - minCeloBalance: BigNumber - queryPriceInCUSD: BigNumber - } - api: { - domains: { - enabled: boolean - } - phoneNumberPrivacy: { - enabled: boolean - shouldFailOpen: boolean - } - legacyPhoneNumberPrivacy: { - enabled: boolean - shouldFailOpen: boolean - } - } - attestations: { - numberAttestationsRequired: number - } - blockchain: BlockchainConfig - db: { - type: SupportedDatabase - user: string - password: string - database: string - host: string - port?: number - ssl: boolean - poolMaxSize: number - } - keystore: { - type: SupportedKeystore - keys: { - phoneNumberPrivacy: { - name: string - latest: number - } - domains: { - name: string - latest: number - } - } - azure: { - clientID: string - clientSecret: string - tenant: string - vaultName: string - } - google: { - projectId: string - } - aws: { - region: string - secretKey: string - } - } - timeout: number - test_quota_bypass_percentage: number - fullNodeTimeoutMs: number -} - -const env = process.env as any -export const config: SignerConfig = { - serviceName: env.SERVICE_NAME ?? 'odis-signer', - server: { - port: Number(env.SERVER_PORT ?? 8080), - sslKeyPath: env.SERVER_SSL_KEY_PATH, - sslCertPath: env.SERVER_SSL_CERT_PATH, - }, - quota: { - unverifiedQueryMax: Number(env.UNVERIFIED_QUERY_MAX ?? 10), - additionalVerifiedQueryMax: Number(env.ADDITIONAL_VERIFIED_QUERY_MAX ?? 30), - queryPerTransaction: Number(env.QUERY_PER_TRANSACTION ?? 2), - // Min balance is .01 cUSD - minDollarBalance: new BigNumber(env.MIN_DOLLAR_BALANCE ?? 1e16), - // Min balance is .01 cEUR - minEuroBalance: new BigNumber(env.MIN_DOLLAR_BALANCE ?? 1e16), - // Min balance is .005 CELO - minCeloBalance: new BigNumber(env.MIN_DOLLAR_BALANCE ?? 5e15), - // Equivalent to 0.001 cUSD/query - queryPriceInCUSD: new BigNumber(env.QUERY_PRICE_PER_CUSD ?? 0.001), - }, - api: { - domains: { - enabled: toBool(env.DOMAINS_API_ENABLED, false), - }, - phoneNumberPrivacy: { - enabled: toBool(env.PHONE_NUMBER_PRIVACY_API_ENABLED, false), - shouldFailOpen: toBool(env.FULL_NODE_ERRORS_SHOULD_FAIL_OPEN, false), - }, - legacyPhoneNumberPrivacy: { - enabled: toBool(env.LEGACY_PHONE_NUMBER_PRIVACY_API_ENABLED, false), - shouldFailOpen: toBool(env.LEGACY_FULL_NODE_ERRORS_SHOULD_FAIL_OPEN, false), - }, - }, - attestations: { - numberAttestationsRequired: Number(env.ATTESTATIONS_NUMBER_ATTESTATIONS_REQUIRED ?? 3), - }, - blockchain: { - provider: env.BLOCKCHAIN_PROVIDER, - apiKey: env.BLOCKCHAIN_API_KEY, - }, - db: { - type: env.DB_TYPE ? env.DB_TYPE.toLowerCase() : SupportedDatabase.Postgres, - user: env.DB_USERNAME, - password: env.DB_PASSWORD, - database: env.DB_DATABASE, - host: env.DB_HOST, - port: env.DB_PORT ? Number(env.DB_PORT) : undefined, - ssl: toBool(env.DB_USE_SSL, true), - poolMaxSize: env.DB_POOL_MAX_SIZE ?? 50, - }, - keystore: { - type: env.KEYSTORE_TYPE, - keys: { - phoneNumberPrivacy: { - name: env.PHONE_NUMBER_PRIVACY_KEY_NAME_BASE, - latest: Number(env.PHONE_NUMBER_PRIVACY_LATEST_KEY_VERSION ?? 1), - }, - domains: { - name: env.DOMAINS_KEY_NAME_BASE, - latest: Number(env.DOMAINS_LATEST_KEY_VERSION ?? 1), - }, - }, - azure: { - clientID: env.KEYSTORE_AZURE_CLIENT_ID, - clientSecret: env.KEYSTORE_AZURE_CLIENT_SECRET, - tenant: env.KEYSTORE_AZURE_TENANT, - vaultName: env.KEYSTORE_AZURE_VAULT_NAME, - }, - google: { - projectId: env.KEYSTORE_GOOGLE_PROJECT_ID, - }, - aws: { - region: env.KEYSTORE_AWS_REGION, - secretKey: env.KEYSTORE_AWS_SECRET_KEY, - }, - }, - timeout: Number(env.ODIS_SIGNER_TIMEOUT ?? 5000), - test_quota_bypass_percentage: Number(env.TEST_QUOTA_BYPASS_PERCENTAGE ?? 0), - fullNodeTimeoutMs: Number(env.TIMEOUT_MS ?? FULL_NODE_TIMEOUT_IN_MS), -} diff --git a/packages/phone-number-privacy/signer/src/domain/endpoints/disable/action.ts b/packages/phone-number-privacy/signer/src/domain/endpoints/disable/action.ts deleted file mode 100644 index 779685b0123..00000000000 --- a/packages/phone-number-privacy/signer/src/domain/endpoints/disable/action.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { timeout } from '@celo/base' -import { DisableDomainRequest, domainHash } from '@celo/phone-number-privacy-common' -import { Knex } from 'knex' -import { Action } from '../../../common/action' -import { toSequentialDelayDomainState } from '../../../common/database/models/domain-state' -import { - createEmptyDomainStateRecord, - getDomainStateRecord, - insertDomainStateRecord, - setDomainDisabled, -} from '../../../common/database/wrappers/domain-state' -import { SignerConfig } from '../../../config' -import { DomainSession } from '../../session' -import { DomainDisableIO } from './io' - -export class DomainDisableAction implements Action { - constructor(readonly db: Knex, readonly config: SignerConfig, readonly io: DomainDisableIO) {} - - public async perform( - session: DomainSession, - timeoutError: symbol - ): Promise { - const domain = session.request.body.domain - session.logger.info( - { - name: domain.name, - version: domain.version, - hash: domainHash(domain).toString('hex'), - }, - 'Processing request to disable domain' - ) - // Inside a database transaction, update or create the domain to mark it disabled. - const res = await this.db.transaction(async (trx) => { - const disableDomainHandler = async () => { - const domainStateRecord = - (await getDomainStateRecord(this.db, domain, session.logger, trx)) ?? - (await insertDomainStateRecord( - this.db, - createEmptyDomainStateRecord(domain, true), - trx, - session.logger - )) - if (!domainStateRecord.disabled) { - await setDomainDisabled(this.db, domain, trx, session.logger) - domainStateRecord.disabled = true - } - return { - success: true, - status: 200, - domainStateRecord, - } - } - // Ensure timeouts roll back DB trx - return timeout(disableDomainHandler, [], this.config.timeout, timeoutError) - }) - - this.io.sendSuccess( - res.status, - session.response, - toSequentialDelayDomainState(res.domainStateRecord) - ) - } -} diff --git a/packages/phone-number-privacy/signer/src/domain/endpoints/disable/io.ts b/packages/phone-number-privacy/signer/src/domain/endpoints/disable/io.ts deleted file mode 100644 index f77a9e8cd34..00000000000 --- a/packages/phone-number-privacy/signer/src/domain/endpoints/disable/io.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { - DisableDomainRequest, - disableDomainRequestSchema, - DisableDomainResponse, - DisableDomainResponseFailure, - DisableDomainResponseSuccess, - DomainSchema, - DomainState, - ErrorType, - send, - SignerEndpoint, - verifyDisableDomainRequestAuthenticity, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import { Request, Response } from 'express' -import { IO } from '../../../common/io' -import { Counters } from '../../../common/metrics' -import { getSignerVersion } from '../../../config' -import { DomainSession } from '../../session' - -export class DomainDisableIO extends IO { - readonly endpoint = SignerEndpoint.DISABLE_DOMAIN - - async init( - request: Request<{}, {}, unknown>, - response: Response - ): Promise | null> { - // Input checks sends a response to the user internally. - if (!super.inputChecks(request, response)) { - return null - } - if (!(await this.authenticate(request))) { - this.sendFailure(WarningMessage.UNAUTHENTICATED_USER, 401, response) - return null - } - return new DomainSession(request, response) - } - - validate(request: Request<{}, {}, unknown>): request is Request<{}, {}, DisableDomainRequest> { - return disableDomainRequestSchema(DomainSchema).is(request.body) - } - - authenticate(request: Request<{}, {}, DisableDomainRequest>): Promise { - return Promise.resolve(verifyDisableDomainRequestAuthenticity(request.body)) - } - - sendSuccess( - status: number, - response: Response, - domainState: DomainState - ) { - send( - response, - { - success: true, - version: getSignerVersion(), - status: domainState, - }, - status, - response.locals.logger - ) - Counters.responses.labels(this.endpoint, status.toString()).inc() - } - - sendFailure(error: ErrorType, status: number, response: Response) { - send( - response, - { - success: false, - version: getSignerVersion(), - error, - }, - status, - response.locals.logger - ) - Counters.responses.labels(this.endpoint, status.toString()).inc() - } -} diff --git a/packages/phone-number-privacy/signer/src/domain/endpoints/quota/action.ts b/packages/phone-number-privacy/signer/src/domain/endpoints/quota/action.ts deleted file mode 100644 index babddd4a7cf..00000000000 --- a/packages/phone-number-privacy/signer/src/domain/endpoints/quota/action.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { timeout } from '@celo/base' -import { domainHash, DomainQuotaStatusRequest } from '@celo/phone-number-privacy-common' -import { Action } from '../../../common/action' -import { toSequentialDelayDomainState } from '../../../common/database/models/domain-state' -import { SignerConfig } from '../../../config' -import { DomainQuotaService } from '../../services/quota' -import { DomainSession } from '../../session' -import { DomainQuotaIO } from './io' - -export class DomainQuotaAction implements Action { - constructor( - readonly config: SignerConfig, - readonly quotaService: DomainQuotaService, - readonly io: DomainQuotaIO - ) {} - - public async perform( - session: DomainSession, - timeoutError: symbol - ): Promise { - const domain = session.request.body.domain - session.logger.info('Processing request to get domain quota status', { - name: domain.name, - version: domain.version, - hash: domainHash(domain).toString('hex'), - }) - const domainStateRecord = await timeout( - () => this.quotaService.getQuotaStatus(session), - [], - this.config.timeout, - timeoutError - ) - this.io.sendSuccess(200, session.response, toSequentialDelayDomainState(domainStateRecord)) - } -} diff --git a/packages/phone-number-privacy/signer/src/domain/endpoints/quota/io.ts b/packages/phone-number-privacy/signer/src/domain/endpoints/quota/io.ts deleted file mode 100644 index 8dca82b76bf..00000000000 --- a/packages/phone-number-privacy/signer/src/domain/endpoints/quota/io.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { - DomainQuotaStatusRequest, - domainQuotaStatusRequestSchema, - DomainQuotaStatusResponse, - DomainQuotaStatusResponseFailure, - DomainQuotaStatusResponseSuccess, - DomainSchema, - DomainState, - ErrorType, - send, - SignerEndpoint, - verifyDomainQuotaStatusRequestAuthenticity, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import { Request, Response } from 'express' -import { IO } from '../../../common/io' -import { Counters } from '../../../common/metrics' -import { getSignerVersion } from '../../../config' -import { DomainSession } from '../../session' - -export class DomainQuotaIO extends IO { - readonly endpoint = SignerEndpoint.DOMAIN_QUOTA_STATUS - - async init( - request: Request<{}, {}, unknown>, - response: Response - ): Promise | null> { - if (!super.inputChecks(request, response)) { - return null - } - if (!(await this.authenticate(request))) { - this.sendFailure(WarningMessage.UNAUTHENTICATED_USER, 401, response) - return null - } - return new DomainSession(request, response) - } - - validate( - request: Request<{}, {}, unknown> - ): request is Request<{}, {}, DomainQuotaStatusRequest> { - return domainQuotaStatusRequestSchema(DomainSchema).is(request.body) - } - - authenticate(request: Request<{}, {}, DomainQuotaStatusRequest>): Promise { - return Promise.resolve(verifyDomainQuotaStatusRequestAuthenticity(request.body)) - } - - sendSuccess( - status: number, - response: Response, - domainState: DomainState - ) { - send( - response, - { - success: true, - version: getSignerVersion(), - status: domainState, - }, - status, - response.locals.logger - ) - Counters.responses.labels(this.endpoint, status.toString()).inc() - } - - sendFailure( - error: ErrorType, - status: number, - response: Response - ) { - send( - response, - { - success: false, - version: getSignerVersion(), - error, - }, - status, - response.locals.logger - ) - Counters.responses.labels(this.endpoint, status.toString()).inc() - } -} diff --git a/packages/phone-number-privacy/signer/src/domain/endpoints/sign/action.ts b/packages/phone-number-privacy/signer/src/domain/endpoints/sign/action.ts deleted file mode 100644 index 446baa506cb..00000000000 --- a/packages/phone-number-privacy/signer/src/domain/endpoints/sign/action.ts +++ /dev/null @@ -1,175 +0,0 @@ -import { timeout } from '@celo/base' -import { - Domain, - domainHash, - DomainRestrictedSignatureRequest, - ErrorType, - getRequestKeyVersion, - ThresholdPoprfServer, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import { EIP712Optional } from '@celo/utils/lib/sign-typed-data-utils' -import { Knex } from 'knex' -import { Action, Session } from '../../../common/action' -import { - DomainStateRecord, - toSequentialDelayDomainState, -} from '../../../common/database/models/domain-state' -import { DefaultKeyName, Key, KeyProvider } from '../../../common/key-management/key-provider-base' -import { SignerConfig } from '../../../config' -import { DomainQuotaService } from '../../services/quota' -import { DomainSession } from '../../session' -import { DomainSignIO } from './io' - -type TrxResult = - | { - success: false - status: number - domainStateRecord: DomainStateRecord - error: ErrorType - } - | { - success: true - status: number - domainStateRecord: DomainStateRecord - key: Key - signature: string - } - -export class DomainSignAction implements Action { - constructor( - readonly db: Knex, - readonly config: SignerConfig, - readonly quota: DomainQuotaService, - readonly keyProvider: KeyProvider, - readonly io: DomainSignIO - ) {} - - public async perform( - session: DomainSession, - timeoutError: symbol - ): Promise { - const domain = session.request.body.domain - session.logger.info( - { - name: domain.name, - version: domain.version, - hash: domainHash(domain).toString('hex'), - }, - 'Processing request to get domain signature ' - ) - const res: TrxResult = await this.db.transaction(async (trx) => { - const domainSignHandler = async (): Promise => { - // Get the current domain state record, or use an empty record if one does not exist. - const domainStateRecord = await this.quota.getQuotaStatus(session, trx) - - // Note that this action occurs in the same transaction as the remainder of the siging - // action. As a result, this is included here rather than in the authentication function. - if (!this.nonceCheck(domainStateRecord, session)) { - return { - success: false, - status: 401, - domainStateRecord, - error: WarningMessage.INVALID_NONCE, - } - } - - const quotaStatus = await this.quota.checkAndUpdateQuotaStatus( - domainStateRecord, - session, - trx - ) - - if (!quotaStatus.sufficient) { - session.logger.warn( - { - name: domain.name, - version: domain.version, - hash: domainHash(domain), - }, - `Exceeded quota` - ) - return { - success: false, - status: 429, - domainStateRecord: quotaStatus.state, - error: WarningMessage.EXCEEDED_QUOTA, - } - } - - const key: Key = { - version: - getRequestKeyVersion(session.request, session.logger) ?? - this.config.keystore.keys.domains.latest, - name: DefaultKeyName.DOMAINS, - } - - // Compute evaluation inside transaction so it will rollback on error. - const evaluation = await this.eval( - domain, - session.request.body.blindedMessage, - key, - session - ) - - return { - success: true, - status: 200, - domainStateRecord: quotaStatus.state, - key, - signature: evaluation.toString('base64'), - } - } - // Ensure timeouts roll back DB trx - return timeout(domainSignHandler, [], this.config.timeout, timeoutError) - }) - - if (res.success) { - this.io.sendSuccess( - res.status, - session.response, - res.key, - res.signature, - toSequentialDelayDomainState(res.domainStateRecord) - ) - } else { - this.io.sendFailure( - res.error, - res.status, - session.response, - toSequentialDelayDomainState(res.domainStateRecord) - ) - } - } - - private nonceCheck( - domainStateRecord: DomainStateRecord, - session: DomainSession - ): boolean { - const nonce: EIP712Optional = session.request.body.options.nonce - if (!nonce.defined) { - session.logger.info('Nonce is undefined') - return false - } - return nonce.value >= domainStateRecord.counter - } - - private async eval( - domain: Domain, - blindedMessage: string, - key: Key, - session: Session - ): Promise { - let privateKey: string - try { - privateKey = await this.keyProvider.getPrivateKeyOrFetchFromStore(key) - } catch (err) { - session.logger.error({ key }, 'Requested key version not supported') - session.logger.error(err) - throw new Error(WarningMessage.INVALID_KEY_VERSION_REQUEST) - } - - const server = new ThresholdPoprfServer(Buffer.from(privateKey, 'hex')) - return server.blindPartialEval(domainHash(domain), Buffer.from(blindedMessage, 'base64')) - } -} diff --git a/packages/phone-number-privacy/signer/src/domain/endpoints/sign/io.ts b/packages/phone-number-privacy/signer/src/domain/endpoints/sign/io.ts deleted file mode 100644 index a55a0f9f396..00000000000 --- a/packages/phone-number-privacy/signer/src/domain/endpoints/sign/io.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { - DomainRestrictedSignatureRequest, - domainRestrictedSignatureRequestSchema, - DomainRestrictedSignatureResponse, - DomainRestrictedSignatureResponseFailure, - DomainRestrictedSignatureResponseSuccess, - DomainSchema, - DomainState, - ErrorType, - KEY_VERSION_HEADER, - requestHasValidKeyVersion, - send, - SignerEndpoint, - verifyDomainRestrictedSignatureRequestAuthenticity, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import { Request, Response } from 'express' -import { IO } from '../../../common/io' -import { Key } from '../../../common/key-management/key-provider-base' -import { Counters } from '../../../common/metrics' -import { getSignerVersion } from '../../../config' -import { DomainSession } from '../../session' - -export class DomainSignIO extends IO { - readonly endpoint = SignerEndpoint.DOMAIN_SIGN - - async init( - request: Request<{}, {}, unknown>, - response: Response - ): Promise | null> { - if (!super.inputChecks(request, response)) { - return null - } - if (!requestHasValidKeyVersion(request, response.locals.logger)) { - this.sendFailure(WarningMessage.INVALID_KEY_VERSION_REQUEST, 400, response) - return null - } - if (!(await this.authenticate(request))) { - this.sendFailure(WarningMessage.UNAUTHENTICATED_USER, 401, response) - return null - } - return new DomainSession(request, response) - } - - validate( - request: Request<{}, {}, unknown> - ): request is Request<{}, {}, DomainRestrictedSignatureRequest> { - return domainRestrictedSignatureRequestSchema(DomainSchema).is(request.body) - } - - authenticate(request: Request<{}, {}, DomainRestrictedSignatureRequest>): Promise { - return Promise.resolve(verifyDomainRestrictedSignatureRequestAuthenticity(request.body)) - } - - sendSuccess( - status: number, - response: Response, - key: Key, - signature: string, - domainState: DomainState - ) { - response.set(KEY_VERSION_HEADER, key.version.toString()) - send( - response, - { - success: true, - version: getSignerVersion(), - signature, - status: domainState, - }, - status, - response.locals.logger - ) - Counters.responses.labels(this.endpoint, status.toString()).inc() - } - - sendFailure( - error: ErrorType, - status: number, - response: Response, - domainState?: DomainState - ) { - send( - response, - { - success: false, - version: getSignerVersion(), - error, - status: domainState, - }, - status, - response.locals.logger - ) - Counters.responses.labels(this.endpoint, status.toString()).inc() - } -} diff --git a/packages/phone-number-privacy/signer/src/domain/services/quota.ts b/packages/phone-number-privacy/signer/src/domain/services/quota.ts deleted file mode 100644 index 475be753afa..00000000000 --- a/packages/phone-number-privacy/signer/src/domain/services/quota.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { - checkSequentialDelayRateLimit, - DomainQuotaStatusRequest, - DomainRestrictedSignatureRequest, - ErrorMessage, - isSequentialDelayDomain, -} from '@celo/phone-number-privacy-common' -import { Knex } from 'knex' -import { - DomainStateRecord, - toDomainStateRecord, - toSequentialDelayDomainState, -} from '../../common/database/models/domain-state' -import { - getDomainStateRecordOrEmpty, - updateDomainStateRecord, -} from '../../common/database/wrappers/domain-state' -import { OdisQuotaStatusResult, QuotaService } from '../../common/quota' -import { DomainSession } from '../session' - -declare type QuotaDependentDomainRequest = - | DomainQuotaStatusRequest - | DomainRestrictedSignatureRequest - -export class DomainQuotaService implements QuotaService { - constructor(readonly db: Knex) {} - - async checkAndUpdateQuotaStatus( - state: DomainStateRecord, - session: DomainSession, - trx: Knex.Transaction, - attemptTime?: number - ): Promise> { - const { domain } = session.request.body - // Timestamp precision is lowered to seconds to reduce the chance of effective timing attacks. - attemptTime = attemptTime ?? Math.floor(Date.now() / 1000) - if (isSequentialDelayDomain(domain)) { - const result = checkSequentialDelayRateLimit( - domain, - attemptTime, - toSequentialDelayDomainState(state, attemptTime) - ) - if (result.accepted) { - const newState = toDomainStateRecord(domain, result.state) - // Persist the updated domain quota to the database. - // This will trigger an insert if its the first update to the domain instance. - await updateDomainStateRecord(this.db, domain, newState, trx, session.logger) - return { sufficient: true, state: newState } - } - // If the result was rejected, the domainStateRecord does not change - return { sufficient: false, state } - } else { - throw new Error(ErrorMessage.UNSUPPORTED_DOMAIN) - } - } - - async getQuotaStatus( - session: DomainSession, - trx?: Knex.Transaction - ): Promise { - return getDomainStateRecordOrEmpty(this.db, session.request.body.domain, session.logger, trx) - } -} diff --git a/packages/phone-number-privacy/signer/src/domain/session.ts b/packages/phone-number-privacy/signer/src/domain/session.ts deleted file mode 100644 index 3d9080aab1d..00000000000 --- a/packages/phone-number-privacy/signer/src/domain/session.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { DomainRequest, OdisResponse } from '@celo/phone-number-privacy-common' -import Logger from 'bunyan' -import { Request, Response } from 'express' - -export class DomainSession { - readonly logger: Logger - - public constructor( - readonly request: Request<{}, {}, R>, - readonly response: Response> - ) { - this.logger = response.locals.logger - } -} diff --git a/packages/phone-number-privacy/signer/src/index.ts b/packages/phone-number-privacy/signer/src/index.ts deleted file mode 100644 index 29c531d8a09..00000000000 --- a/packages/phone-number-privacy/signer/src/index.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { getContractKit, rootLogger } from '@celo/phone-number-privacy-common' -import { initDatabase } from './common/database/database' -import { initKeyProvider } from './common/key-management/key-provider' -import { KeyProvider } from './common/key-management/key-provider-base' -import { config, DEV_MODE } from './config' -import { startSigner } from './server' - -require('dotenv').config() - -async function start() { - const logger = rootLogger(config.serviceName) - logger.info(`Starting. Dev mode: ${DEV_MODE}`) - const db = await initDatabase(config) - const keyProvider: KeyProvider = await initKeyProvider(config) - const server = startSigner(config, db, keyProvider, getContractKit(config.blockchain)) - logger.info('Starting server') - const port = config.server.port ?? 0 - const backupTimeout = config.timeout * 1.2 - server - .listen(port, () => { - logger.info(`Server is listening on port ${port}`) - }) - .setTimeout(backupTimeout) -} - -if (!DEV_MODE) { - start().catch((err) => { - const logger = rootLogger(config.serviceName) - logger.error({ err }, 'Fatal error occured. Exiting') - process.exit(1) - }) -} - -export { initDatabase } from './common/database/database' -export { initKeyProvider } from './common/key-management/key-provider' -export { config, SupportedDatabase, SupportedKeystore } from './config' -export * from './server' diff --git a/packages/phone-number-privacy/signer/src/pnp/endpoints/quota/action.ts b/packages/phone-number-privacy/signer/src/pnp/endpoints/quota/action.ts deleted file mode 100644 index cb114f53858..00000000000 --- a/packages/phone-number-privacy/signer/src/pnp/endpoints/quota/action.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { timeout } from '@celo/base' -import { - ErrorMessage, - LegacyPnpQuotaRequest, - PnpQuotaRequest, -} from '@celo/phone-number-privacy-common' -import { Action } from '../../../common/action' -import { SignerConfig } from '../../../config' -import { PnpQuotaService } from '../../services/quota' -import { PnpSession } from '../../session' -import { PnpQuotaIO } from './io' -import { LegacyPnpQuotaIO } from './io.legacy' - -export class PnpQuotaAction implements Action { - constructor( - readonly config: SignerConfig, - readonly quota: PnpQuotaService, - readonly io: PnpQuotaIO | LegacyPnpQuotaIO - ) {} - - public async perform( - session: PnpSession, - timeoutError: symbol - ): Promise { - const quotaStatus = await timeout( - () => this.quota.getQuotaStatus(session), - [], - this.config.timeout, - timeoutError - ) - if (quotaStatus.performedQueryCount > -1 && quotaStatus.totalQuota > -1) { - this.io.sendSuccess(200, session.response, quotaStatus, session.errors) - return - } - this.io.sendFailure( - quotaStatus.performedQueryCount === -1 - ? ErrorMessage.FAILURE_TO_GET_PERFORMED_QUERY_COUNT - : ErrorMessage.FAILURE_TO_GET_TOTAL_QUOTA, - 500, - session.response - ) - } -} diff --git a/packages/phone-number-privacy/signer/src/pnp/endpoints/quota/io.legacy.ts b/packages/phone-number-privacy/signer/src/pnp/endpoints/quota/io.legacy.ts deleted file mode 100644 index 7fd5ed0bb3d..00000000000 --- a/packages/phone-number-privacy/signer/src/pnp/endpoints/quota/io.legacy.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { ContractKit } from '@celo/contractkit' -import { - authenticateUser, - ErrorType, - hasValidAccountParam, - identifierIsValidIfExists, - isBodyReasonablySized, - LegacyPnpQuotaRequest, - LegacyPnpQuotaRequestSchema, - PnpQuotaResponse, - PnpQuotaResponseFailure, - PnpQuotaResponseSuccess, - PnpQuotaStatus, - send, - SignerEndpoint, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import Logger from 'bunyan' -import { Request, Response } from 'express' -import { IO } from '../../../common/io' -import { Counters } from '../../../common/metrics' -import { getSignerVersion } from '../../../config' -import { PnpSession } from '../../session' - -export class LegacyPnpQuotaIO extends IO { - readonly endpoint = SignerEndpoint.LEGACY_PNP_QUOTA - - constructor( - readonly enabled: boolean, - readonly shouldFailOpen: boolean, - readonly timeoutMs: number, - readonly kit: ContractKit - ) { - super(enabled) - } - - async init( - request: Request<{}, {}, unknown>, - response: Response - ): Promise | null> { - const warnings: ErrorType[] = [] - if (!super.inputChecks(request, response)) { - return null - } - if (!(await this.authenticate(request, warnings, response.locals.logger))) { - this.sendFailure(WarningMessage.UNAUTHENTICATED_USER, 401, response) - return null - } - const session = new PnpSession(request, response) - session.errors.push(...warnings) - return session - } - - validate(request: Request<{}, {}, unknown>): request is Request<{}, {}, LegacyPnpQuotaRequest> { - return ( - LegacyPnpQuotaRequestSchema.is(request.body) && - hasValidAccountParam(request.body) && - identifierIsValidIfExists(request.body) && - isBodyReasonablySized(request.body) - ) - } - - async authenticate( - request: Request<{}, {}, LegacyPnpQuotaRequest>, - warnings: ErrorType[], - logger: Logger - ): Promise { - return authenticateUser( - request, - this.kit, - logger, - this.shouldFailOpen, - warnings, - this.timeoutMs - ) - } - - sendSuccess( - status: number, - response: Response, - quotaStatus: PnpQuotaStatus, - warnings: string[] - ) { - send( - response, - { - success: true, - version: getSignerVersion(), - ...quotaStatus, - warnings, - }, - status, - response.locals.logger - ) - Counters.responses.labels(this.endpoint, status.toString()).inc() - } - - sendFailure(error: ErrorType, status: number, response: Response) { - send( - response, - { - success: false, - version: getSignerVersion(), - error, - }, - status, - response.locals.logger - ) - Counters.responses.labels(this.endpoint, status.toString()).inc() - } -} diff --git a/packages/phone-number-privacy/signer/src/pnp/endpoints/quota/io.ts b/packages/phone-number-privacy/signer/src/pnp/endpoints/quota/io.ts deleted file mode 100644 index 727ac3cdceb..00000000000 --- a/packages/phone-number-privacy/signer/src/pnp/endpoints/quota/io.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { ContractKit } from '@celo/contractkit' -import { - authenticateUser, - ErrorType, - hasValidAccountParam, - isBodyReasonablySized, - PnpQuotaRequest, - PnpQuotaRequestSchema, - PnpQuotaResponse, - PnpQuotaResponseFailure, - PnpQuotaResponseSuccess, - PnpQuotaStatus, - send, - SignerEndpoint, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import Logger from 'bunyan' -import { Request, Response } from 'express' -import { IO } from '../../../common/io' -import { Counters } from '../../../common/metrics' -import { getSignerVersion } from '../../../config' -import { PnpSession } from '../../session' - -export class PnpQuotaIO extends IO { - readonly endpoint = SignerEndpoint.PNP_QUOTA - - constructor( - readonly enabled: boolean, - readonly shouldFailOpen: boolean, - readonly timeoutMs: number, - readonly kit: ContractKit - ) { - super(enabled) - } - - async init( - request: Request<{}, {}, unknown>, - response: Response - ): Promise | null> { - const warnings: ErrorType[] = [] - if (!super.inputChecks(request, response)) { - return null - } - if (!(await this.authenticate(request, warnings, response.locals.logger))) { - this.sendFailure(WarningMessage.UNAUTHENTICATED_USER, 401, response) - return null - } - const session = new PnpSession(request, response) - session.errors.push(...warnings) - return session - } - - validate(request: Request<{}, {}, unknown>): request is Request<{}, {}, PnpQuotaRequest> { - return ( - PnpQuotaRequestSchema.is(request.body) && - hasValidAccountParam(request.body) && - isBodyReasonablySized(request.body) - ) - } - - async authenticate( - request: Request<{}, {}, PnpQuotaRequest>, - warnings: ErrorType[], - logger: Logger - ): Promise { - return authenticateUser( - request, - this.kit, - logger, - this.shouldFailOpen, - warnings, - this.timeoutMs - ) - } - - sendSuccess( - status: number, - response: Response, - quotaStatus: PnpQuotaStatus, - warnings: string[] - ) { - send( - response, - { - success: true, - version: getSignerVersion(), - ...quotaStatus, - warnings, - }, - status, - response.locals.logger - ) - Counters.responses.labels(this.endpoint, status.toString()).inc() - } - - sendFailure(error: ErrorType, status: number, response: Response) { - send( - response, - { - success: false, - version: getSignerVersion(), - error, - }, - status, - response.locals.logger - ) - Counters.responses.labels(this.endpoint, status.toString()).inc() - } -} diff --git a/packages/phone-number-privacy/signer/src/pnp/endpoints/sign/action.legacy.ts b/packages/phone-number-privacy/signer/src/pnp/endpoints/sign/action.legacy.ts deleted file mode 100644 index 642ad9781cd..00000000000 --- a/packages/phone-number-privacy/signer/src/pnp/endpoints/sign/action.legacy.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Knex } from 'knex' -import { REQUESTS_TABLE } from '../../../common/database/models/request' -import { KeyProvider } from '../../../common/key-management/key-provider-base' -import { SignerConfig } from '../../../config' -import { PnpQuotaService } from '../../services/quota' -import { PnpSignAction } from './action' -import { LegacyPnpSignIO } from './io.legacy' - -export class LegacyPnpSignAction extends PnpSignAction { - protected readonly requestsTable = REQUESTS_TABLE.LEGACY - - constructor( - readonly db: Knex, - readonly config: SignerConfig, - readonly quota: PnpQuotaService, - readonly keyProvider: KeyProvider, - readonly io: LegacyPnpSignIO - ) { - super(db, config, quota, keyProvider, io) - } -} diff --git a/packages/phone-number-privacy/signer/src/pnp/endpoints/sign/action.onchain.ts b/packages/phone-number-privacy/signer/src/pnp/endpoints/sign/action.onchain.ts deleted file mode 100644 index 5cd93b41962..00000000000 --- a/packages/phone-number-privacy/signer/src/pnp/endpoints/sign/action.onchain.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Knex } from 'knex' -import { REQUESTS_TABLE } from '../../../common/database/models/request' -import { KeyProvider } from '../../../common/key-management/key-provider-base' -import { SignerConfig } from '../../../config' -import { PnpQuotaService } from '../../services/quota' -import { PnpSignAction } from './action' -import { PnpSignIO } from './io' - -export class OnChainPnpSignAction extends PnpSignAction { - protected readonly requestsTable = REQUESTS_TABLE.ONCHAIN - - constructor( - readonly db: Knex, - readonly config: SignerConfig, - readonly quota: PnpQuotaService, - readonly keyProvider: KeyProvider, - readonly io: PnpSignIO - ) { - super(db, config, quota, keyProvider, io) - } -} diff --git a/packages/phone-number-privacy/signer/src/pnp/endpoints/sign/action.ts b/packages/phone-number-privacy/signer/src/pnp/endpoints/sign/action.ts deleted file mode 100644 index af636279d80..00000000000 --- a/packages/phone-number-privacy/signer/src/pnp/endpoints/sign/action.ts +++ /dev/null @@ -1,160 +0,0 @@ -import { timeout } from '@celo/base' -import { - ErrorMessage, - getRequestKeyVersion, - LegacySignMessageRequest, - SignMessageRequest, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import { Knex } from 'knex' -import { Action, Session } from '../../../common/action' -import { computeBlindedSignature } from '../../../common/bls/bls-cryptography-client' -import { REQUESTS_TABLE } from '../../../common/database/models/request' -import { getRequestExists } from '../../../common/database/wrappers/request' -import { DefaultKeyName, Key, KeyProvider } from '../../../common/key-management/key-provider-base' -import { Counters } from '../../../common/metrics' -import { SignerConfig } from '../../../config' -import { PnpQuotaService } from '../../services/quota' -import { PnpSession } from '../../session' -import { PnpSignIO } from './io' -import { LegacyPnpSignIO } from './io.legacy' - -export abstract class PnpSignAction - implements Action -{ - protected abstract readonly requestsTable: REQUESTS_TABLE - - constructor( - readonly db: Knex, - readonly config: SignerConfig, - readonly quota: PnpQuotaService, - readonly keyProvider: KeyProvider, - readonly io: PnpSignIO | LegacyPnpSignIO - ) {} - - public async perform( - session: PnpSession, - timeoutError: symbol - ): Promise { - // Compute quota lookup, update, and signing within transaction - // so that these occur atomically and rollback on error. - await this.db.transaction(async (trx) => { - const pnpSignHandler = async () => { - const quotaStatus = await this.quota.getQuotaStatus(session, trx) - - let isDuplicateRequest = false - try { - isDuplicateRequest = await getRequestExists( - this.db, - this.requestsTable, - session.request.body.account, - session.request.body.blindedQueryPhoneNumber, - session.logger, - trx - ) - } catch (err) { - session.logger.error(err, 'Failed to check if request already exists in db') - } - - if (isDuplicateRequest) { - Counters.duplicateRequests.inc() - session.logger.info( - 'Request already exists in db. Will service request without charging quota.' - ) - session.errors.push(WarningMessage.DUPLICATE_REQUEST_TO_GET_PARTIAL_SIG) - } else { - // In the case of a database connection failure, performedQueryCount will be -1 - if (quotaStatus.performedQueryCount === -1) { - this.io.sendFailure( - ErrorMessage.DATABASE_GET_FAILURE, - 500, - session.response, - quotaStatus - ) - return - } - // In the case of a blockchain connection failure, totalQuota will be -1 - if (quotaStatus.totalQuota === -1) { - if (this.io.shouldFailOpen) { - // We fail open and service requests on full-node errors to not block the user. - // Error messages are stored in the session and included along with the signature in the response. - quotaStatus.totalQuota = Number.MAX_SAFE_INTEGER - session.logger.warn( - { warning: ErrorMessage.FAILURE_TO_GET_TOTAL_QUOTA }, - ErrorMessage.FAILING_OPEN - ) - Counters.requestsFailingOpen.inc() - } else { - session.logger.warn( - { warning: ErrorMessage.FAILURE_TO_GET_TOTAL_QUOTA }, - ErrorMessage.FAILING_CLOSED - ) - Counters.requestsFailingClosed.inc() - this.io.sendFailure(ErrorMessage.FULL_NODE_ERROR, 500, session.response, quotaStatus) - return - } - } - - // TODO(after 2.0.0) add more specific error messages on DB and key version - // https://github.com/celo-org/celo-monorepo/issues/9882 - // quotaStatus is updated in place; throws on failure to update - const { sufficient } = await this.quota.checkAndUpdateQuotaStatus( - quotaStatus, - session, - trx - ) - if (!sufficient) { - this.io.sendFailure(WarningMessage.EXCEEDED_QUOTA, 403, session.response, quotaStatus) - return - } - } - - const key: Key = { - version: - getRequestKeyVersion(session.request, session.logger) ?? - this.config.keystore.keys.phoneNumberPrivacy.latest, - name: DefaultKeyName.PHONE_NUMBER_PRIVACY, - } - - try { - const signature = await this.sign( - session.request.body.blindedQueryPhoneNumber, - key, - session - ) - this.io.sendSuccess(200, session.response, key, signature, quotaStatus, session.errors) - return - } catch (err) { - session.logger.error({ err }) - quotaStatus.performedQueryCount-- - this.io.sendFailure( - ErrorMessage.SIGNATURE_COMPUTATION_FAILURE, - 500, - session.response, - quotaStatus - ) - // Note that errors thrown after rollback will have no effect, hence doing this last - await trx.rollback() - return - } - } - await timeout(pnpSignHandler, [], this.config.timeout, timeoutError) - }) - } - - private async sign( - blindedMessage: string, - key: Key, - session: Session - ): Promise { - let privateKey: string - try { - privateKey = await this.keyProvider.getPrivateKeyOrFetchFromStore(key) - } catch (err) { - session.logger.info({ key }, 'Requested key version not supported') - session.logger.error(err) - throw new Error(WarningMessage.INVALID_KEY_VERSION_REQUEST) - } - return computeBlindedSignature(blindedMessage, privateKey, session.logger) - } -} diff --git a/packages/phone-number-privacy/signer/src/pnp/endpoints/sign/io.legacy.ts b/packages/phone-number-privacy/signer/src/pnp/endpoints/sign/io.legacy.ts deleted file mode 100644 index f4617119f74..00000000000 --- a/packages/phone-number-privacy/signer/src/pnp/endpoints/sign/io.legacy.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { ContractKit } from '@celo/contractkit' -import { - authenticateUser, - ErrorType, - hasValidAccountParam, - hasValidBlindedPhoneNumberParam, - identifierIsValidIfExists, - isBodyReasonablySized, - KEY_VERSION_HEADER, - LegacySignMessageRequest, - LegacySignMessageRequestSchema, - PnpQuotaStatus, - requestHasValidKeyVersion, - send, - SignerEndpoint, - SignMessageResponse, - SignMessageResponseFailure, - SignMessageResponseSuccess, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import Logger from 'bunyan' -import { Request, Response } from 'express' -import { IO } from '../../../common/io' -import { Key } from '../../../common/key-management/key-provider-base' -import { Counters } from '../../../common/metrics' -import { getSignerVersion } from '../../../config' -import { PnpSession } from '../../session' - -export class LegacyPnpSignIO extends IO { - readonly endpoint = SignerEndpoint.LEGACY_PNP_SIGN - - constructor( - readonly enabled: boolean, - readonly shouldFailOpen: boolean, - readonly timeoutMs: number, - readonly kit: ContractKit - ) { - super(enabled) - } - - async init( - request: Request<{}, {}, unknown>, - response: Response - ): Promise | null> { - const logger = response.locals.logger - const warnings: ErrorType[] = [] - if (!super.inputChecks(request, response)) { - return null - } - if (!requestHasValidKeyVersion(request, logger)) { - this.sendFailure(WarningMessage.INVALID_KEY_VERSION_REQUEST, 400, response) - return null - } - if (!(await this.authenticate(request, warnings, logger))) { - this.sendFailure(WarningMessage.UNAUTHENTICATED_USER, 401, response) - return null - } - const session = new PnpSession(request, response) - session.errors.push(...warnings) - return session - } - - validate( - request: Request<{}, {}, unknown> - ): request is Request<{}, {}, LegacySignMessageRequest> { - return ( - LegacySignMessageRequestSchema.is(request.body) && - hasValidAccountParam(request.body) && - hasValidBlindedPhoneNumberParam(request.body) && - identifierIsValidIfExists(request.body) && - isBodyReasonablySized(request.body) - ) - } - - async authenticate( - request: Request<{}, {}, LegacySignMessageRequest>, - warnings: ErrorType[], - logger: Logger - ): Promise { - return authenticateUser( - request, - this.kit, - logger, - this.shouldFailOpen, - warnings, - this.timeoutMs - ) - } - - sendSuccess( - status: number, - response: Response, - key: Key, - signature: string, - quotaStatus: PnpQuotaStatus, - warnings: string[] - ) { - response.set(KEY_VERSION_HEADER, key.version.toString()) - send( - response, - { - success: true, - version: getSignerVersion(), - signature, - ...quotaStatus, - warnings, - }, - status, - response.locals.logger - ) - Counters.responses.labels(this.endpoint, status.toString()).inc() - } - - sendFailure( - error: string, - status: number, - response: Response, - quotaStatus?: PnpQuotaStatus - ) { - send( - response, - { - success: false, - version: getSignerVersion(), - error, - ...quotaStatus, - }, - status, - response.locals.logger - ) - Counters.responses.labels(this.endpoint, status.toString()).inc() - } -} diff --git a/packages/phone-number-privacy/signer/src/pnp/endpoints/sign/io.ts b/packages/phone-number-privacy/signer/src/pnp/endpoints/sign/io.ts deleted file mode 100644 index 6761fd797d2..00000000000 --- a/packages/phone-number-privacy/signer/src/pnp/endpoints/sign/io.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { ContractKit } from '@celo/contractkit' -import { - authenticateUser, - ErrorType, - hasValidAccountParam, - hasValidBlindedPhoneNumberParam, - isBodyReasonablySized, - KEY_VERSION_HEADER, - PnpQuotaStatus, - requestHasValidKeyVersion, - send, - SignerEndpoint, - SignMessageRequest, - SignMessageRequestSchema, - SignMessageResponse, - SignMessageResponseFailure, - SignMessageResponseSuccess, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import Logger from 'bunyan' -import { Request, Response } from 'express' -import { IO } from '../../../common/io' -import { Key } from '../../../common/key-management/key-provider-base' -import { Counters } from '../../../common/metrics' -import { getSignerVersion } from '../../../config' -import { PnpSession } from '../../session' - -export class PnpSignIO extends IO { - readonly endpoint = SignerEndpoint.PNP_SIGN - - constructor( - readonly enabled: boolean, - readonly shouldFailOpen: boolean, - readonly timeoutMs: number, - readonly kit: ContractKit - ) { - super(enabled) - } - - async init( - request: Request<{}, {}, unknown>, - response: Response - ): Promise | null> { - const logger = response.locals.logger - const warnings: ErrorType[] = [] - if (!super.inputChecks(request, response)) { - return null - } - if (!requestHasValidKeyVersion(request, logger)) { - this.sendFailure(WarningMessage.INVALID_KEY_VERSION_REQUEST, 400, response) - return null - } - if (!(await this.authenticate(request, warnings, logger))) { - this.sendFailure(WarningMessage.UNAUTHENTICATED_USER, 401, response) - return null - } - const session = new PnpSession(request, response) - session.errors.push(...warnings) - return session - } - - validate(request: Request<{}, {}, unknown>): request is Request<{}, {}, SignMessageRequest> { - return ( - SignMessageRequestSchema.is(request.body) && - hasValidAccountParam(request.body) && - hasValidBlindedPhoneNumberParam(request.body) && - isBodyReasonablySized(request.body) - ) - } - - async authenticate( - request: Request<{}, {}, SignMessageRequest>, - warnings: ErrorType[], - logger: Logger - ): Promise { - return authenticateUser( - request, - this.kit, - logger, - this.shouldFailOpen, - warnings, - this.timeoutMs - ) - } - - sendSuccess( - status: number, - response: Response, - key: Key, - signature: string, - quotaStatus: PnpQuotaStatus, - warnings: string[] - ) { - response.set(KEY_VERSION_HEADER, key.version.toString()) - send( - response, - { - success: true, - version: getSignerVersion(), - signature, - ...quotaStatus, - warnings, - }, - status, - response.locals.logger - ) - Counters.responses.labels(this.endpoint, status.toString()).inc() - } - - sendFailure( - error: string, - status: number, - response: Response, - quotaStatus?: PnpQuotaStatus - ) { - send( - response, - { - success: false, - version: getSignerVersion(), - error, - ...quotaStatus, - }, - status, - response.locals.logger - ) - Counters.responses.labels(this.endpoint, status.toString()).inc() - } -} diff --git a/packages/phone-number-privacy/signer/src/pnp/services/quota.legacy.ts b/packages/phone-number-privacy/signer/src/pnp/services/quota.legacy.ts deleted file mode 100644 index 0900437ef04..00000000000 --- a/packages/phone-number-privacy/signer/src/pnp/services/quota.legacy.ts +++ /dev/null @@ -1,280 +0,0 @@ -import { NULL_ADDRESS } from '@celo/base' -import { StableToken } from '@celo/contractkit' -import { - ErrorMessage, - isVerified, - LegacyPnpQuotaRequest, - LegacySignMessageRequest, -} from '@celo/phone-number-privacy-common' -import Logger from 'bunyan' -import { ACCOUNTS_TABLE } from '../../common/database/models/account' -import { REQUESTS_TABLE } from '../../common/database/models/request' -import { Counters, Histograms, meter } from '../../common/metrics' -import { QuotaService } from '../../common/quota' -import { - getCeloBalance, - getStableTokenBalance, - getTransactionCount, - getWalletAddress, -} from '../../common/web3/contracts' -import { config } from '../../config' -import { PnpSession } from '../session' -import { PnpQuotaService } from './quota' - -export class LegacyPnpQuotaService - extends PnpQuotaService - implements QuotaService -{ - protected readonly requestsTable = REQUESTS_TABLE.LEGACY - protected readonly accountsTable = ACCOUNTS_TABLE.LEGACY - - protected async getWalletAddressAndIsVerified( - session: PnpSession - ): Promise<{ walletAddress: string; isAccountVerified: boolean }> { - const { account, hashedPhoneNumber } = session.request.body - const [walletAddressResult, isVerifiedResult] = await meter( - (_session: PnpSession) => - Promise.allSettled([ - getWalletAddress(this.kit, session.logger, account, session.request.url), - hashedPhoneNumber - ? isVerified(account, hashedPhoneNumber, this.kit, session.logger) - : Promise.resolve(false), - ]), - [session], - (err: any) => { - throw err - }, - Histograms.getRemainingQueryCountInstrumentation, - ['getWalletAddressAndIsVerified', session.request.url] - ) - let hadFullNodeError = false, - isAccountVerified = false, - walletAddress = NULL_ADDRESS - if (walletAddressResult.status === 'fulfilled') { - walletAddress = walletAddressResult.value - } else { - session.logger.error(walletAddressResult.reason) - hadFullNodeError = true - } - if (isVerifiedResult.status === 'fulfilled') { - isAccountVerified = isVerifiedResult.value - } else { - session.logger.error(isVerifiedResult.reason) - hadFullNodeError = true - } - if (hadFullNodeError) { - session.errors.push(ErrorMessage.FULL_NODE_ERROR) - } - - if (account.toLowerCase() === walletAddress.toLowerCase()) { - session.logger.debug('walletAddress is the same as accountAddress') - walletAddress = NULL_ADDRESS // So we don't double count quota - } - - return { isAccountVerified, walletAddress } - } - - protected async getBalances( - session: PnpSession, - ...addresses: string[] - ) { - const [cUSDAccountBalanceResult, cEURAccountBalanceResult, celoAccountBalanceResult] = - await meter( - (logger: Logger, ..._addresses: string[]) => - Promise.allSettled([ - getStableTokenBalance( - this.kit, - StableToken.cUSD, - logger, - session.request.url, - ..._addresses - ), - getStableTokenBalance( - this.kit, - StableToken.cEUR, - logger, - session.request.url, - ..._addresses - ), - getCeloBalance(this.kit, logger, session.request.url, ..._addresses), - ]), - [session.logger, ...addresses], - (err: any) => { - throw err - }, - Histograms.getRemainingQueryCountInstrumentation, - ['getBalances', session.request.url] - ) - - let hadFullNodeError = false - let cUSDAccountBalance, cEURAccountBalance, celoAccountBalance - if (cUSDAccountBalanceResult.status === 'fulfilled') { - cUSDAccountBalance = cUSDAccountBalanceResult.value - } else { - session.logger.error(cUSDAccountBalanceResult.reason) - hadFullNodeError = true - } - if (cEURAccountBalanceResult.status === 'fulfilled') { - cEURAccountBalance = cEURAccountBalanceResult.value - } else { - session.logger.error(cEURAccountBalanceResult.reason) - hadFullNodeError = true - } - if (celoAccountBalanceResult.status === 'fulfilled') { - celoAccountBalance = celoAccountBalanceResult.value - } else { - session.logger.error(celoAccountBalanceResult.reason) - hadFullNodeError = true - } - if (hadFullNodeError) { - session.errors.push(ErrorMessage.FULL_NODE_ERROR) - } - - return { cUSDAccountBalance, cEURAccountBalance, celoAccountBalance } - } - - /* - * Calculates how many queries the caller has unlocked based on the algorithm - * unverifiedQueryCount + verifiedQueryCount + (queryPerTransaction * transactionCount) - * If the caller is not verified, they must have a minimum balance to get the unverifiedQueryMax. - */ - protected async getTotalQuotaWithoutMeter( - session: PnpSession - ): Promise { - const { - unverifiedQueryMax, - additionalVerifiedQueryMax, - queryPerTransaction, - minDollarBalance, - minEuroBalance, - minCeloBalance, - } = config.quota - - const { account } = session.request.body - - const { walletAddress, isAccountVerified } = await this.getWalletAddressAndIsVerified(session) - - if (walletAddress !== NULL_ADDRESS) { - Counters.requestsWithWalletAddress.inc() - } - - const transactionCount = await getTransactionCount( - this.kit, - session.logger, - session.request.url, - account, - walletAddress - ) - session.logger.debug({ account, transactionCount }) - - if (isAccountVerified) { - Counters.requestsWithVerifiedAccount.inc() - session.logger.debug({ account }, 'Account is verified') - return this.calculateQuotaForVerifiedAccount( - account, - unverifiedQueryMax, - additionalVerifiedQueryMax, - queryPerTransaction, - transactionCount, - session.logger - ) - } - - session.logger.debug({ account }, 'Account is not verified. Checking if min balance is met.') - - const { cUSDAccountBalance, cEURAccountBalance, celoAccountBalance } = await this.getBalances( - session, - account, - walletAddress - ) - - // Min balance can be in either cUSD, cEUR or CELO - if ( - cUSDAccountBalance?.isGreaterThanOrEqualTo(minDollarBalance) || - cEURAccountBalance?.isGreaterThanOrEqualTo(minEuroBalance) || - celoAccountBalance?.isGreaterThanOrEqualTo(minCeloBalance) - ) { - Counters.requestsWithUnverifiedAccountWithMinBalance.inc() - session.logger.debug( - { - account, - cUSDAccountBalance, - cEURAccountBalance, - celoAccountBalance, - minDollarBalance, - minEuroBalance, - minCeloBalance, - }, - 'Account is not verified but meets min balance' - ) - - return this.calculateQuotaForUnverifiedAccountWithMinBalance( - account, - unverifiedQueryMax, - queryPerTransaction, - transactionCount, - session.logger - ) - } - - session.logger.debug({ account }, 'Account is not verified and does not meet min balance') - - const quota = 0 - - session.logger.trace({ - account, - cUSDAccountBalance, - cEURAccountBalance, - celoAccountBalance, - minDollarBalance, - minEuroBalance, - minCeloBalance, - quota, - }) - - return quota - } - - private calculateQuotaForVerifiedAccount( - account: string, - unverifiedQueryMax: number, - additionalVerifiedQueryMax: number, - queryPerTransaction: number, - transactionCount: number, - logger: Logger - ): number { - const quota = - unverifiedQueryMax + additionalVerifiedQueryMax + queryPerTransaction * transactionCount - - logger.trace({ - account, - unverifiedQueryMax, - additionalVerifiedQueryMax, - queryPerTransaction, - transactionCount, - quota, - }) - - return quota - } - - private calculateQuotaForUnverifiedAccountWithMinBalance( - account: string, - unverifiedQueryMax: number, - queryPerTransaction: number, - transactionCount: number, - logger: Logger - ): number { - const quota = unverifiedQueryMax + queryPerTransaction * transactionCount - - logger.trace({ - account, - unverifiedQueryMax, - queryPerTransaction, - transactionCount, - quota, - }) - - return quota - } -} diff --git a/packages/phone-number-privacy/signer/src/pnp/services/quota.onchain.ts b/packages/phone-number-privacy/signer/src/pnp/services/quota.onchain.ts deleted file mode 100644 index d995f1b316b..00000000000 --- a/packages/phone-number-privacy/signer/src/pnp/services/quota.onchain.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { PnpQuotaRequest, SignMessageRequest } from '@celo/phone-number-privacy-common' -import BigNumber from 'bignumber.js' -import { ACCOUNTS_TABLE } from '../../common/database/models/account' -import { REQUESTS_TABLE } from '../../common/database/models/request' -import { QuotaService } from '../../common/quota' -import { getOnChainOdisPayments } from '../../common/web3/contracts' -import { config } from '../../config' -import { PnpSession } from '../session' -import { PnpQuotaService } from './quota' - -export class OnChainPnpQuotaService - extends PnpQuotaService - implements QuotaService -{ - protected readonly requestsTable = REQUESTS_TABLE.ONCHAIN - protected readonly accountsTable = ACCOUNTS_TABLE.ONCHAIN - /* - * Calculates how many queries the caller has unlocked based on the total - * amount of funds paid to the OdisPayments.sol contract on-chain. - */ - protected async getTotalQuotaWithoutMeter( - session: PnpSession - ): Promise { - const { queryPriceInCUSD } = config.quota - const { account } = session.request.body - const totalPaidInWei = await getOnChainOdisPayments( - this.kit, - session.logger, - account, - session.request.url - ) - const totalQuota = totalPaidInWei - .div(queryPriceInCUSD.times(new BigNumber(1e18))) - .integerValue(BigNumber.ROUND_DOWN) - // If any account hits an overflow here, we need to redesign how - // quota/queries are computed anyways. - return totalQuota.toNumber() - } -} diff --git a/packages/phone-number-privacy/signer/src/pnp/services/quota.ts b/packages/phone-number-privacy/signer/src/pnp/services/quota.ts deleted file mode 100644 index c815cdd5e76..00000000000 --- a/packages/phone-number-privacy/signer/src/pnp/services/quota.ts +++ /dev/null @@ -1,157 +0,0 @@ -import { ContractKit } from '@celo/contractkit' -import { - ErrorMessage, - PnpQuotaRequest, - PnpQuotaStatus, - SignMessageRequest, -} from '@celo/phone-number-privacy-common' -import { Knex } from 'knex' -import { ACCOUNTS_TABLE } from '../../common/database/models/account' -import { REQUESTS_TABLE } from '../../common/database/models/request' -import { getPerformedQueryCount, incrementQueryCount } from '../../common/database/wrappers/account' -import { storeRequest } from '../../common/database/wrappers/request' -import { Counters, Histograms, meter } from '../../common/metrics' -import { OdisQuotaStatusResult, QuotaService } from '../../common/quota' -import { getBlockNumber } from '../../common/web3/contracts' -import { config } from '../../config' -import { PnpSession } from '../session' - -export abstract class PnpQuotaService - implements QuotaService -{ - protected abstract readonly requestsTable: REQUESTS_TABLE - protected abstract readonly accountsTable: ACCOUNTS_TABLE - - constructor(readonly db: Knex, readonly kit: ContractKit) {} - - public async checkAndUpdateQuotaStatus( - state: PnpQuotaStatus, - session: PnpSession, - trx: Knex.Transaction - ): Promise> { - const remainingQuota = state.totalQuota - state.performedQueryCount - Histograms.userRemainingQuotaAtRequest.labels(session.request.url).observe(remainingQuota) - let sufficient = remainingQuota > 0 - if (!sufficient) { - session.logger.warn({ ...state }, 'No remaining quota') - if (this.bypassQuotaForE2ETesting(session.request.body)) { - Counters.testQuotaBypassedRequests.inc() - session.logger.info(session.request.body, 'Request will bypass quota check for e2e testing') - sufficient = true - } - } else { - await Promise.all([ - storeRequest( - this.db, - this.requestsTable, - session.request.body.account, - session.request.body.blindedQueryPhoneNumber, - session.logger, - trx - ), - incrementQueryCount( - this.db, - this.accountsTable, - session.request.body.account, - session.logger, - trx - ), - ]) - state.performedQueryCount++ - } - return { sufficient, state } - } - - public async getQuotaStatus( - session: PnpSession, - trx?: Knex.Transaction - ): Promise { - const { account } = session.request.body - const [performedQueryCountResult, totalQuotaResult, blockNumberResult] = await meter( - (_session: PnpSession) => - Promise.allSettled([ - getPerformedQueryCount(this.db, this.accountsTable, account, session.logger, trx), - this.getTotalQuota(_session), - getBlockNumber(this.kit), - ]), - [session], - (err: any) => { - throw err - }, - Histograms.getRemainingQueryCountInstrumentation, - ['getQuotaStatus', session.request.url] - ) - - const quotaStatus: PnpQuotaStatus = { - // TODO(future) consider making totalQuota,performedQueryCount undefined - totalQuota: -1, - performedQueryCount: -1, - blockNumber: undefined, - } - if (performedQueryCountResult.status === 'fulfilled') { - quotaStatus.performedQueryCount = performedQueryCountResult.value - } else { - session.logger.error( - { err: performedQueryCountResult.reason }, - ErrorMessage.FAILURE_TO_GET_PERFORMED_QUERY_COUNT - ) - session.errors.push( - ErrorMessage.DATABASE_GET_FAILURE, - ErrorMessage.FAILURE_TO_GET_PERFORMED_QUERY_COUNT - ) - } - let hadFullNodeError = false - if (totalQuotaResult.status === 'fulfilled') { - quotaStatus.totalQuota = totalQuotaResult.value - } else { - session.logger.error( - { err: totalQuotaResult.reason }, - ErrorMessage.FAILURE_TO_GET_TOTAL_QUOTA - ) - hadFullNodeError = true - session.errors.push(ErrorMessage.FAILURE_TO_GET_TOTAL_QUOTA) - } - if (blockNumberResult.status === 'fulfilled') { - quotaStatus.blockNumber = blockNumberResult.value - } else { - session.logger.error( - { err: blockNumberResult.reason }, - ErrorMessage.FAILURE_TO_GET_BLOCK_NUMBER - ) - hadFullNodeError = true - session.errors.push(ErrorMessage.FAILURE_TO_GET_BLOCK_NUMBER) - } - if (hadFullNodeError) { - session.errors.push(ErrorMessage.FULL_NODE_ERROR) - } - - return quotaStatus - } - - protected async getTotalQuota( - session: PnpSession - ): Promise { - return meter( - this.getTotalQuotaWithoutMeter.bind(this), - [session], - (err: any) => { - throw err - }, - Histograms.getRemainingQueryCountInstrumentation, - ['getTotalQuota', session.request.url] - ) - } - - /* - * Calculates how many queries the caller has unlocked; - * must be implemented by subclasses. - */ - protected abstract getTotalQuotaWithoutMeter( - session: PnpSession - ): Promise - - private bypassQuotaForE2ETesting(requestBody: SignMessageRequest): boolean { - const sessionID = Number(requestBody.sessionID) - return !Number.isNaN(sessionID) && sessionID % 100 < config.test_quota_bypass_percentage - } -} diff --git a/packages/phone-number-privacy/signer/src/pnp/session.ts b/packages/phone-number-privacy/signer/src/pnp/session.ts deleted file mode 100644 index a6d80c764ae..00000000000 --- a/packages/phone-number-privacy/signer/src/pnp/session.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { - ErrorType, - PhoneNumberPrivacyRequest, - PhoneNumberPrivacyResponse, -} from '@celo/phone-number-privacy-common' -import Logger from 'bunyan' -import { Request, Response } from 'express' - -export class PnpSession { - readonly logger: Logger - readonly errors: ErrorType[] = [] - - public constructor( - readonly request: Request<{}, {}, R>, - readonly response: Response> - ) { - this.logger = response.locals.logger - } -} diff --git a/packages/phone-number-privacy/signer/src/server.ts b/packages/phone-number-privacy/signer/src/server.ts deleted file mode 100644 index 50eba68ca44..00000000000 --- a/packages/phone-number-privacy/signer/src/server.ts +++ /dev/null @@ -1,197 +0,0 @@ -import { ContractKit } from '@celo/contractkit' -import { - ErrorMessage, - getContractKit, - loggerMiddleware, - rootLogger, - SignerEndpoint, -} from '@celo/phone-number-privacy-common' -import Logger from 'bunyan' -import express, { Request, RequestHandler, Response } from 'express' -import fs from 'fs' -import https from 'https' -import { Knex } from 'knex' -import * as PromClient from 'prom-client' -import { Controller } from './common/controller' -import { KeyProvider } from './common/key-management/key-provider-base' -import { Counters } from './common/metrics' -import { getSignerVersion, SignerConfig } from './config' -import { DomainDisableAction } from './domain/endpoints/disable/action' -import { DomainDisableIO } from './domain/endpoints/disable/io' -import { DomainQuotaAction } from './domain/endpoints/quota/action' -import { DomainQuotaIO } from './domain/endpoints/quota/io' -import { DomainSignAction } from './domain/endpoints/sign/action' -import { DomainSignIO } from './domain/endpoints/sign/io' -import { DomainQuotaService } from './domain/services/quota' -import { PnpQuotaAction } from './pnp/endpoints/quota/action' -import { PnpQuotaIO } from './pnp/endpoints/quota/io' -import { LegacyPnpQuotaIO } from './pnp/endpoints/quota/io.legacy' -import { LegacyPnpSignAction } from './pnp/endpoints/sign/action.legacy' -import { OnChainPnpSignAction } from './pnp/endpoints/sign/action.onchain' -import { PnpSignIO } from './pnp/endpoints/sign/io' -import { LegacyPnpSignIO } from './pnp/endpoints/sign/io.legacy' -import { LegacyPnpQuotaService } from './pnp/services/quota.legacy' -import { OnChainPnpQuotaService } from './pnp/services/quota.onchain' - -require('events').EventEmitter.defaultMaxListeners = 15 - -export function startSigner( - config: SignerConfig, - db: Knex, - keyProvider: KeyProvider, - kit?: ContractKit -) { - const logger = rootLogger(config.serviceName) - - kit = kit ?? getContractKit(config.blockchain) - - logger.info('Creating signer express server') - const app = express() - app.use(express.json({ limit: '0.2mb' }) as RequestHandler, loggerMiddleware(config.serviceName)) - - app.get(SignerEndpoint.STATUS, (_req, res) => { - res.status(200).json({ - version: getSignerVersion(), - }) - }) - - app.get(SignerEndpoint.METRICS, (_req, res) => { - res.send(PromClient.register.metrics()) - }) - - const addEndpoint = ( - endpoint: SignerEndpoint, - handler: (req: Request, res: Response) => Promise - ) => - app.post(endpoint, async (req, res) => { - const childLogger: Logger = res.locals.logger - try { - await handler(req, res) - } catch (err: any) { - // Handle any errors that otherwise managed to escape the proper handlers - childLogger.error(ErrorMessage.CAUGHT_ERROR_IN_ENDPOINT_HANDLER) - childLogger.error(err) - Counters.errorsCaughtInEndpointHandler.inc() - if (!res.headersSent) { - childLogger.info('Responding with error in outer endpoint handler') - res.status(500).json({ - success: false, - error: ErrorMessage.UNKNOWN_ERROR, - }) - } else { - // Getting to this error likely indicates that the `perform` process - // does not terminate after sending a response, and then throws an error. - childLogger.error(ErrorMessage.ERROR_AFTER_RESPONSE_SENT) - Counters.errorsThrownAfterResponseSent.inc() - } - } - }) - - const pnpQuotaService = new OnChainPnpQuotaService(db, kit) - const legacyPnpQuotaService = new LegacyPnpQuotaService(db, kit) - const domainQuotaService = new DomainQuotaService(db) - - const pnpQuota = new Controller( - new PnpQuotaAction( - config, - pnpQuotaService, - new PnpQuotaIO( - config.api.phoneNumberPrivacy.enabled, - config.api.phoneNumberPrivacy.shouldFailOpen, // TODO (https://github.com/celo-org/celo-monorepo/issues/9862) consider refactoring config to make the code cleaner - config.fullNodeTimeoutMs, - kit - ) - ) - ) - const pnpSign = new Controller( - new OnChainPnpSignAction( - db, - config, - pnpQuotaService, - keyProvider, - new PnpSignIO( - config.api.phoneNumberPrivacy.enabled, - config.api.phoneNumberPrivacy.shouldFailOpen, - config.fullNodeTimeoutMs, - kit - ) - ) - ) - const legacyPnpSign = new Controller( - new LegacyPnpSignAction( - db, - config, - legacyPnpQuotaService, - keyProvider, - new LegacyPnpSignIO( - config.api.legacyPhoneNumberPrivacy.enabled, - config.api.legacyPhoneNumberPrivacy.shouldFailOpen, - config.fullNodeTimeoutMs, - kit - ) - ) - ) - const legacyPnpQuota = new Controller( - new PnpQuotaAction( - config, - legacyPnpQuotaService, - new LegacyPnpQuotaIO( - config.api.legacyPhoneNumberPrivacy.enabled, - config.api.legacyPhoneNumberPrivacy.shouldFailOpen, - config.fullNodeTimeoutMs, - kit - ) - ) - ) - const domainQuota = new Controller( - new DomainQuotaAction(config, domainQuotaService, new DomainQuotaIO(config.api.domains.enabled)) - ) - const domainSign = new Controller( - new DomainSignAction( - db, - config, - domainQuotaService, - keyProvider, - new DomainSignIO(config.api.domains.enabled) - ) - ) - const domainDisable = new Controller( - new DomainDisableAction(db, config, new DomainDisableIO(config.api.domains.enabled)) - ) - logger.info('Right before adding meteredSignerEndpoints') - addEndpoint(SignerEndpoint.PNP_SIGN, pnpSign.handle.bind(pnpSign)) - addEndpoint(SignerEndpoint.PNP_QUOTA, pnpQuota.handle.bind(pnpQuota)) - addEndpoint(SignerEndpoint.DOMAIN_QUOTA_STATUS, domainQuota.handle.bind(domainQuota)) - addEndpoint(SignerEndpoint.DOMAIN_SIGN, domainSign.handle.bind(domainSign)) - addEndpoint(SignerEndpoint.DISABLE_DOMAIN, domainDisable.handle.bind(domainDisable)) - - addEndpoint(SignerEndpoint.LEGACY_PNP_SIGN, legacyPnpSign.handle.bind(legacyPnpSign)) - addEndpoint(SignerEndpoint.LEGACY_PNP_QUOTA, legacyPnpQuota.handle.bind(legacyPnpQuota)) - - const sslOptions = getSslOptions(config) - if (sslOptions) { - return https.createServer(sslOptions, app) - } else { - return app - } -} - -function getSslOptions(config: SignerConfig) { - const logger = rootLogger(config.serviceName) - const { sslKeyPath, sslCertPath } = config.server - - if (!sslKeyPath || !sslCertPath) { - logger.info('No SSL configs specified') - return null - } - - if (!fs.existsSync(sslKeyPath) || !fs.existsSync(sslCertPath)) { - logger.error('SSL cert files not found') - return null - } - - return { - key: fs.readFileSync(sslKeyPath), - cert: fs.readFileSync(sslCertPath), - } -} diff --git a/packages/phone-number-privacy/signer/test/end-to-end/disabled-apis.test.ts b/packages/phone-number-privacy/signer/test/end-to-end/disabled-apis.test.ts deleted file mode 100644 index 735ae257ba4..00000000000 --- a/packages/phone-number-privacy/signer/test/end-to-end/disabled-apis.test.ts +++ /dev/null @@ -1,261 +0,0 @@ -import { - DisableDomainRequest, - disableDomainRequestEIP712, - DisableDomainResponse, - DomainIdentifiers, - DomainQuotaStatusRequest, - domainQuotaStatusRequestEIP712, - DomainQuotaStatusResponse, - DomainRequestTypeTag, - DomainRestrictedSignatureRequest, - domainRestrictedSignatureRequestEIP712, - genSessionID, - PnpQuotaRequest, - PnpQuotaResponse, - SequentialDelayDomain, - SignerEndpoint, - SignMessageRequest, - TestUtils, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import { defined, noBool, noNumber, noString } from '@celo/utils/lib/sign-typed-data-utils' -import { LocalWallet } from '@celo/wallet-local' -import 'isomorphic-fetch' - -require('dotenv').config() - -const { ACCOUNT_ADDRESS1, BLINDED_PHONE_NUMBER, PRIVATE_KEY1 } = TestUtils.Values -const { getPnpRequestAuthorization } = TestUtils.Utils - -const ODIS_SIGNER = process.env.ODIS_SIGNER_SERVICE_URL - -jest.setTimeout(30000) - -const expectedVersion = process.env.DEPLOYED_SIGNER_SERVICE_VERSION! - -// These tests should be run when the individual APIs are disabled. -// When run against enabled APIs, they should fail. -describe('Running against a deployed service with disabled APIs', () => { - beforeAll(() => { - console.log('ODIS_SIGNER: ' + ODIS_SIGNER) - }) - - it('Service is deployed at correct version', async () => { - const response = await fetch(ODIS_SIGNER + SignerEndpoint.STATUS, { method: 'GET' }) - const body = await response.json() - // This checks against local package.json version, change if necessary - expect(response.status).toBe(200) - expect(body.version).toBe(expectedVersion) - }) - - describe('when DOMAINS API is disabled', () => { - const wallet = new LocalWallet() - wallet.addAccount(PRIVATE_KEY1) - const walletAddress = wallet.getAccounts()[0] - - const authenticatedDomain: SequentialDelayDomain = { - name: DomainIdentifiers.SequentialDelay, - version: '1', - stages: [{ delay: 0, resetTimer: noBool, batchSize: defined(2), repetitions: defined(10) }], - address: defined(walletAddress), - salt: defined('himalayanPink'), - } - - it(`${SignerEndpoint.DISABLE_DOMAIN} should respond with 503`, async () => { - const req: DisableDomainRequest = { - type: DomainRequestTypeTag.DISABLE, - domain: authenticatedDomain, - options: { - signature: noString, - nonce: noNumber, - }, - sessionID: defined(genSessionID()), - } - req.options.signature = defined( - await wallet.signTypedData(walletAddress, disableDomainRequestEIP712(req)) - ) - const body = JSON.stringify(req) - const response = await fetch(ODIS_SIGNER + SignerEndpoint.DISABLE_DOMAIN, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - }, - body, - }) - expect(response.status).toBe(503) - const responseBody: DisableDomainResponse = await response.json() - expect(responseBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.API_UNAVAILABLE, - }) - }) - - it(`${SignerEndpoint.DOMAIN_QUOTA_STATUS} should respond with 503`, async () => { - const req: DomainQuotaStatusRequest = { - type: DomainRequestTypeTag.QUOTA, - domain: authenticatedDomain, - options: { - signature: noString, - nonce: noNumber, - }, - sessionID: defined(genSessionID()), - } - req.options.signature = defined( - await wallet.signTypedData(walletAddress, domainQuotaStatusRequestEIP712(req)) - ) - const body = JSON.stringify(req) - const response = await fetch(ODIS_SIGNER + SignerEndpoint.DOMAIN_QUOTA_STATUS, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - }, - body, - }) - expect(response.status).toBe(503) - const responseBody: DomainQuotaStatusResponse = await response.json() - expect(responseBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.API_UNAVAILABLE, - }) - }) - - it(`${SignerEndpoint.DOMAIN_SIGN} should respond with 503`, async () => { - const req: DomainRestrictedSignatureRequest = { - type: DomainRequestTypeTag.SIGN, - domain: authenticatedDomain, - options: { - signature: noString, - nonce: noNumber, - }, - // The message shouldn't actually matter here - blindedMessage: BLINDED_PHONE_NUMBER, - sessionID: defined(genSessionID()), - } - req.options.signature = defined( - await wallet.signTypedData(walletAddress, domainRestrictedSignatureRequestEIP712(req)) - ) - const body = JSON.stringify(req) - const response = await fetch(ODIS_SIGNER + SignerEndpoint.DOMAIN_SIGN, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - }, - body, - }) - expect(response.status).toBe(503) - const responseBody: DomainQuotaStatusResponse = await response.json() - expect(responseBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.API_UNAVAILABLE, - }) - }) - }) - - describe('when PNP API is disabled', () => { - it(`${SignerEndpoint.PNP_QUOTA} should respond with 503`, async () => { - const req: PnpQuotaRequest = { - account: ACCOUNT_ADDRESS1, - } - const body = JSON.stringify(req) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const response = await fetch(ODIS_SIGNER + SignerEndpoint.PNP_QUOTA, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - Authorization: authorization, - }, - body, - }) - expect(response.status).toBe(503) - const responseBody: PnpQuotaResponse = await response.json() - expect(responseBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.API_UNAVAILABLE, - }) - }) - - it(`${SignerEndpoint.PNP_SIGN} should respond with 503`, async () => { - const req: SignMessageRequest = { - account: ACCOUNT_ADDRESS1, - blindedQueryPhoneNumber: BLINDED_PHONE_NUMBER, - } - const body = JSON.stringify(req) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const response = await fetch(ODIS_SIGNER + SignerEndpoint.PNP_SIGN, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - Authorization: authorization, - }, - body, - }) - expect(response.status).toBe(503) - const responseBody: PnpQuotaResponse = await response.json() - expect(responseBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.API_UNAVAILABLE, - }) - }) - }) - - describe('when LEGACY_PNP API is disabled', () => { - it(`${SignerEndpoint.LEGACY_PNP_QUOTA} should respond with 503`, async () => { - const req: PnpQuotaRequest = { - account: ACCOUNT_ADDRESS1, - } - const body = JSON.stringify(req) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const response = await fetch(ODIS_SIGNER + SignerEndpoint.LEGACY_PNP_QUOTA, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - Authorization: authorization, - }, - body, - }) - expect(response.status).toBe(503) - const responseBody: PnpQuotaResponse = await response.json() - expect(responseBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.API_UNAVAILABLE, - }) - }) - - it(`${SignerEndpoint.LEGACY_PNP_SIGN} should respond with 503`, async () => { - const req: SignMessageRequest = { - account: ACCOUNT_ADDRESS1, - blindedQueryPhoneNumber: BLINDED_PHONE_NUMBER, - } - const body = JSON.stringify(req) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const response = await fetch(ODIS_SIGNER + SignerEndpoint.LEGACY_PNP_SIGN, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - Authorization: authorization, - }, - body, - }) - expect(response.status).toBe(503) - const responseBody: PnpQuotaResponse = await response.json() - expect(responseBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.API_UNAVAILABLE, - }) - }) - }) -}) diff --git a/packages/phone-number-privacy/signer/test/end-to-end/domain.test.ts b/packages/phone-number-privacy/signer/test/end-to-end/domain.test.ts deleted file mode 100644 index d467f2742e8..00000000000 --- a/packages/phone-number-privacy/signer/test/end-to-end/domain.test.ts +++ /dev/null @@ -1,620 +0,0 @@ -import { - DisableDomainRequest, - disableDomainRequestEIP712, - DisableDomainResponseFailure, - DisableDomainResponseSuccess, - DomainEndpoint, - domainHash, - DomainIdentifiers, - DomainQuotaStatusRequest, - domainQuotaStatusRequestEIP712, - DomainQuotaStatusResponseFailure, - DomainQuotaStatusResponseSuccess, - DomainRequestTypeTag, - DomainRestrictedSignatureRequest, - domainRestrictedSignatureRequestEIP712, - DomainRestrictedSignatureResponseFailure, - DomainRestrictedSignatureResponseSuccess, - genSessionID, - KEY_VERSION_HEADER, - SequentialDelayDomain, - SequentialDelayStage, - SignerEndpoint, - TestUtils, - ThresholdPoprfClient, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import { DomainRequest } from '@celo/phone-number-privacy-common/src' -import { defined, noBool, noNumber, noString } from '@celo/utils/lib/sign-typed-data-utils' -import { LocalWallet } from '@celo/wallet-local' -import 'isomorphic-fetch' -import { getTestParamsForContext } from './utils' -const { ACCOUNT_ADDRESS1, PRIVATE_KEY1 } = TestUtils.Values - -require('dotenv').config() - -jest.setTimeout(60000) - -const expectedVersion = process.env.DEPLOYED_SIGNER_SERVICE_VERSION! - -const ODIS_SIGNER_URL = process.env.ODIS_SIGNER_SERVICE_URL - -const contextSpecificParams = getTestParamsForContext() -console.log(`Blockchain provider: ${contextSpecificParams.blockchainProviderURL}`) -console.log(`Domains public polynomial: ${contextSpecificParams.domainsPolynomial}`) -console.log(`Domains pubKey: ${contextSpecificParams.domainsPubKey}`) - -describe(`Running against service deployed at ${ODIS_SIGNER_URL}`, () => { - const wallet = new LocalWallet() - wallet.addAccount(PRIVATE_KEY1) - - const disableSalt = 'himalayanPink-disable' - const quotaSalt = 'himalayanPink-quota' - const signSalt = 'himalayanPink-sign' - - it('Service is deployed at correct version', async () => { - const response = await fetch(ODIS_SIGNER_URL + SignerEndpoint.STATUS, { - method: 'GET', - }) - expect(response.status).toBe(200) - const body = await response.json() - // This checks against local package.json version, change if necessary - expect(body.version).toBe(expectedVersion) - }) - - describe(`${SignerEndpoint.DISABLE_DOMAIN}`, () => { - it('Should respond with 200 on valid request for new domain', async () => { - const req = await disableRequest(wallet, ACCOUNT_ADDRESS1, `${disableSalt}-${Date.now()}`) - const res = await queryDomainEndpoint(req, SignerEndpoint.DISABLE_DOMAIN) - expect(res.status).toBe(200) - const resBody: DisableDomainResponseSuccess = await res.json() - expect(resBody).toStrictEqual({ - success: true, - version: resBody.version, - status: { - disabled: true, - counter: 0, - timer: 0, - now: resBody.status.now, - }, - }) - }) - - it('Should respond with 200 on valid request for already disabled domain', async () => { - const req = await disableRequest(wallet, ACCOUNT_ADDRESS1, disableSalt) - const res = await queryDomainEndpoint(req, SignerEndpoint.DISABLE_DOMAIN) - expect(res.status).toBe(200) - const resBody: DisableDomainResponseSuccess = await res.json() - expect(resBody).toStrictEqual({ - success: true, - version: resBody.version, - status: { - disabled: true, - counter: 0, - timer: 0, - now: resBody.status.now, - }, - }) - }) - - it('Should respond with 400 on missing request fields', async () => { - const badRequest = await disableRequest(wallet, ACCOUNT_ADDRESS1, disableSalt) - // @ts-ignore Intentionally deleting required field - delete badRequest.domain.version - const res = await queryDomainEndpoint(badRequest, SignerEndpoint.DISABLE_DOMAIN) - expect(res.status).toBe(400) - const resBody: DisableDomainResponseFailure = await res.json() - expect(resBody).toStrictEqual({ - success: false, - version: resBody.version, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 on unknown domain', async () => { - const badRequest = await disableRequest(wallet, ACCOUNT_ADDRESS1, disableSalt) - // @ts-ignore UnknownDomain is (intentionally) not a valid domain identifier. - badRequest.domain.name = 'UnknownDomain' - const res = await queryDomainEndpoint(badRequest, SignerEndpoint.DISABLE_DOMAIN) - expect(res.status).toBe(400) - const resBody: DisableDomainResponseFailure = await res.json() - expect(resBody).toStrictEqual({ - success: false, - version: resBody.version, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 on bad encoding', async () => { - const badRequest = await disableRequest(wallet, ACCOUNT_ADDRESS1, disableSalt) - // @ts-ignore Intentionally not JSON - badRequest.domain = 'Freddy' - const res = await queryDomainEndpoint(badRequest, SignerEndpoint.DISABLE_DOMAIN) - expect(res.status).toBe(400) - const resBody: DisableDomainResponseFailure = await res.json() - expect(resBody).toStrictEqual({ - success: false, - version: resBody.version, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 401 on failed auth', async () => { - const badRequest = await disableRequest(wallet, ACCOUNT_ADDRESS1, disableSalt) - badRequest.domain.salt = defined('badSalt') - const res = await queryDomainEndpoint(badRequest, SignerEndpoint.DISABLE_DOMAIN) - expect(res.status).toBe(401) - const resBody: DisableDomainResponseFailure = await res.json() - expect(resBody).toStrictEqual({ - success: false, - version: resBody.version, - error: WarningMessage.UNAUTHENTICATED_USER, - }) - }) - }) - - describe(`${SignerEndpoint.DOMAIN_QUOTA_STATUS}`, () => { - // This request gets repeated over time - it('Should respond with 200 on valid request', async () => { - const req = await quotaRequest(wallet, ACCOUNT_ADDRESS1, quotaSalt) - const res = await queryDomainEndpoint(req, SignerEndpoint.DOMAIN_QUOTA_STATUS) - expect(res.status).toBe(200) - const resBody: DomainQuotaStatusResponseSuccess = await res.json() - expect(resBody).toStrictEqual({ - success: true, - version: expectedVersion, - status: { disabled: false, counter: 0, timer: 0, now: resBody.status.now }, - }) - }) - - it('Should respond with 200 on valid request for disabled domain', async () => { - const req = await quotaRequest(wallet, ACCOUNT_ADDRESS1, disableSalt) - const res = await queryDomainEndpoint(req, SignerEndpoint.DOMAIN_QUOTA_STATUS) - expect(res.status).toBe(200) - const resBody: DomainQuotaStatusResponseSuccess = await res.json() - expect(resBody).toStrictEqual({ - success: true, - version: expectedVersion, - status: { disabled: true, counter: 0, timer: 0, now: resBody.status.now }, - }) - }) - - it('Should respond with 400 on missing request fields', async () => { - const badRequest = await quotaRequest(wallet, ACCOUNT_ADDRESS1, quotaSalt) - // @ts-ignore Intentionally deleting required field - delete badRequest.domain.version - const res = await queryDomainEndpoint(badRequest, SignerEndpoint.DOMAIN_QUOTA_STATUS) - expect(res.status).toBe(400) - const resBody: DomainQuotaStatusResponseFailure = await res.json() - expect(resBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 on unknown domain', async () => { - const badRequest = await quotaRequest(wallet, ACCOUNT_ADDRESS1, quotaSalt) - // @ts-ignore UnknownDomain is (intentionally) not a valid domain identifier. - badRequest.domain.name = 'UnknownDomain' - const res = await queryDomainEndpoint(badRequest, SignerEndpoint.DOMAIN_QUOTA_STATUS) - expect(res.status).toBe(400) - const resBody: DomainQuotaStatusResponseFailure = await res.json() - expect(resBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 on bad encoding', async () => { - const badRequest = await quotaRequest(wallet, ACCOUNT_ADDRESS1, quotaSalt) - // @ts-ignore Intentionally not JSON - badRequest.domain = 'Freddy' - const res = await queryDomainEndpoint(badRequest, SignerEndpoint.DOMAIN_QUOTA_STATUS) - expect(res.status).toBe(400) - const resBody: DomainQuotaStatusResponseFailure = await res.json() - expect(resBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 401 on failed auth', async () => { - const badRequest = await quotaRequest(wallet, ACCOUNT_ADDRESS1, quotaSalt) - badRequest.domain.salt = defined('badSalt') - const res = await queryDomainEndpoint(badRequest, SignerEndpoint.DOMAIN_QUOTA_STATUS) - expect(res.status).toBe(401) - const resBody: DomainQuotaStatusResponseFailure = await res.json() - expect(resBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.UNAUTHENTICATED_USER, - }) - }) - }) - - describe(`${SignerEndpoint.DOMAIN_SIGN}`, () => { - const signSaltNew = `${signSalt}-${Date.now()}` - let req: DomainRestrictedSignatureRequest - let poprf: ThresholdPoprfClient - beforeAll(async () => { - ;[req, poprf] = await signatureRequest(wallet, ACCOUNT_ADDRESS1, signSaltNew) - }) - it('[Signer configuration test] Should respond with 200 on valid request for new domain', async () => { - const res = await queryDomainEndpoint(req, SignerEndpoint.DOMAIN_SIGN) - expect(res.status).toBe(200) - const resBody: DomainRestrictedSignatureResponseSuccess = await res.json() - expect(resBody).toStrictEqual({ - success: true, - version: expectedVersion, - signature: resBody.signature, - status: { - disabled: false, - counter: 1, - timer: resBody.status.timer, - now: resBody.status.now, - }, - }) - expect(res.headers.get(KEY_VERSION_HEADER)).toEqual(contextSpecificParams.domainsKeyVersion) - poprf.unblindPartialResponse( - // throws if verification fails - Buffer.from(resBody.signature, 'base64') - ) - }) - - it('Should respond with 401 on invalid nonce', async () => { - // Replay exactly the same first request - const res = await queryDomainEndpoint(req, SignerEndpoint.DOMAIN_SIGN) - expect(res.status).toBe(401) - const resBody: DomainRestrictedSignatureResponseFailure = await res.json() - expect(resBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_NONCE, - status: { - disabled: false, - counter: 1, - timer: resBody.status!.timer, - now: resBody.status!.now, - }, - }) - }) - - it('Should respond with 200 on repeated valid requests with nonce updated', async () => { - // submit identical request with nonce set to 1 - req.options.nonce = defined(1) - req.options.signature = noString - req.options.signature = defined( - await wallet.signTypedData(ACCOUNT_ADDRESS1, domainRestrictedSignatureRequestEIP712(req)) - ) - - // TODO(ODIS 2.0.0 e2e fix) clean up this duplicated logic - const headers: any = { - Accept: 'application/json', - 'Content-Type': 'application/json', - Authorization: 'ignore', - } - const res = await fetch(ODIS_SIGNER_URL + SignerEndpoint.DOMAIN_SIGN, { - method: 'POST', - headers, - body: JSON.stringify(req), - }) - expect(res.status).toBe(200) - const resBody: DomainRestrictedSignatureResponseSuccess = await res.json() - expect(resBody).toStrictEqual({ - success: true, - version: expectedVersion, - signature: resBody.signature, - status: { - disabled: false, - counter: 2, - timer: resBody.status.timer, - now: resBody.status.now, - }, - }) - poprf.unblindPartialResponse(Buffer.from(resBody.signature, 'base64')) - }) - - it('Should respond with 200 if nonce > domainState', async () => { - const [newReq, _poprf] = await signatureRequest( - wallet, - ACCOUNT_ADDRESS1, - `${signSalt}-${Date.now()}`, - undefined, - 5 - ) - const res = await queryDomainEndpoint(newReq, SignerEndpoint.DOMAIN_SIGN) - expect(res.status).toBe(200) - const resBody: DomainRestrictedSignatureResponseSuccess = await res.json() - expect(resBody).toStrictEqual({ - success: true, - version: expectedVersion, - signature: resBody.signature, - status: { - disabled: false, - counter: 1, // counter gets incremented, not set to nonce value - timer: resBody.status.timer, - now: resBody.status.now, - }, - }) - _poprf.unblindPartialResponse(Buffer.from(resBody.signature, 'base64')) - }) - - it('Should respond with 200 on valid request with key version header', async () => { - const [newReq, _poprf] = await signatureRequest( - wallet, - ACCOUNT_ADDRESS1, - `${signSalt}-${Date.now() + 1}` - ) - const res = await queryDomainEndpoint( - newReq, - SignerEndpoint.DOMAIN_SIGN, - contextSpecificParams.domainsKeyVersion - ) - expect(res.status).toBe(200) - const resBody: DomainRestrictedSignatureResponseSuccess = await res.json() - expect(resBody).toStrictEqual({ - success: true, - version: expectedVersion, - signature: resBody.signature, - status: { - disabled: false, - counter: 1, // counter gets incremented, not set to nonce value - timer: resBody.status.timer, - now: resBody.status.now, - }, - }) - expect(res.headers.get(KEY_VERSION_HEADER)).toEqual(contextSpecificParams.domainsKeyVersion) - _poprf.unblindPartialResponse(Buffer.from(resBody.signature, 'base64')) - }) - - it('Should respond with 400 on missing request fields', async () => { - const [badRequest, _] = await signatureRequest( - wallet, - ACCOUNT_ADDRESS1, - `${signSalt}-${Date.now()}` - ) - // @ts-ignore Intentionally deleting required field - delete badRequest.domain.version - const res = await queryDomainEndpoint(badRequest, SignerEndpoint.DOMAIN_SIGN) - expect(res.status).toBe(400) - const resBody: DomainRestrictedSignatureResponseFailure = await res.json() - expect(resBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 on missing request fields', async () => { - const [badRequest, _] = await signatureRequest( - wallet, - ACCOUNT_ADDRESS1, - `${signSalt}-${Date.now()}` - ) - // @ts-ignore UnknownDomain is (intentionally) not a valid domain identifier. - badRequest.domain.name = 'UnknownDomain' - const res = await queryDomainEndpoint(badRequest, SignerEndpoint.DOMAIN_SIGN) - expect(res.status).toBe(400) - const resBody: DomainRestrictedSignatureResponseFailure = await res.json() - expect(resBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 on bad encoding', async () => { - const [badRequest, _] = await signatureRequest( - wallet, - ACCOUNT_ADDRESS1, - `${signSalt}-${Date.now()}` - ) - // @ts-ignore Intentionally not JSON - badRequest.domain = 'Freddy' - const res = await queryDomainEndpoint(badRequest, SignerEndpoint.DOMAIN_SIGN) - expect(res.status).toBe(400) - const resBody: DomainRestrictedSignatureResponseFailure = await res.json() - expect(resBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 on bad encoding', async () => { - const [badRequest, _] = await signatureRequest( - wallet, - ACCOUNT_ADDRESS1, - `${signSalt}-${Date.now()}` - ) - const res = await queryDomainEndpoint(badRequest, SignerEndpoint.DOMAIN_SIGN, 'a') - expect(res.status).toBe(400) - const resBody: DomainRestrictedSignatureResponseFailure = await res.json() - expect(resBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_KEY_VERSION_REQUEST, - }) - }) - - it('Should respond with 401 on failed auth', async () => { - const [badRequest, _] = await signatureRequest( - wallet, - ACCOUNT_ADDRESS1, - `${signSalt}-${Date.now()}` - ) - badRequest.domain.salt = defined('badSalt') - const res = await queryDomainEndpoint(badRequest, SignerEndpoint.DOMAIN_SIGN) - expect(res.status).toBe(401) - const resBody: DomainRestrictedSignatureResponseFailure = await res.json() - expect(resBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.UNAUTHENTICATED_USER, - }) - }) - - it('Should respond with 429 on out of quota', async () => { - const salt = `${signSalt}-${Date.now()}` - const noQuotaDomain = authenticatedDomain(ACCOUNT_ADDRESS1, salt, [ - { delay: 0, resetTimer: noBool, batchSize: defined(0), repetitions: defined(0) }, - ]) - const [signReq, _] = await signatureRequest(wallet, ACCOUNT_ADDRESS1, salt, noQuotaDomain) - const res = await queryDomainEndpoint(signReq, SignerEndpoint.DOMAIN_SIGN) - expect(res.status).toBe(429) - const resBody = await res.json() - expect(resBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.EXCEEDED_QUOTA, - status: { - disabled: false, - counter: 0, - timer: 0, - now: resBody.status!.now, - }, - }) - }) - - it('Should respond with 429 on disabled domain', async () => { - const disableReq = await disableRequest(wallet, ACCOUNT_ADDRESS1, disableSalt) - const disableRes = await queryDomainEndpoint(disableReq, SignerEndpoint.DISABLE_DOMAIN) - expect(disableRes.status).toBe(200) - const [signReq, _] = await signatureRequest( - wallet, - ACCOUNT_ADDRESS1, - `${signSalt}-${Date.now()}`, - disableReq.domain - ) - const res = await queryDomainEndpoint(signReq, SignerEndpoint.DOMAIN_SIGN) - expect(res.status).toBe(429) - const resBody = await res.json() - expect(resBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.EXCEEDED_QUOTA, - status: { - disabled: true, - counter: 0, - timer: 0, - now: resBody.status!.now, - }, - }) - }) - }) -}) - -async function queryDomainEndpoint( - req: DomainRequest, - endpoint: DomainEndpoint, - keyVersion?: string -): Promise { - const body = JSON.stringify(req) - const headers: any = { - Accept: 'application/json', - 'Content-Type': 'application/json', - Authorization: 'ignore', - } - - if (keyVersion !== undefined) { - headers[KEY_VERSION_HEADER] = keyVersion - } - - const res = await fetch(ODIS_SIGNER_URL + endpoint, { - method: 'POST', - headers, - body, - }) - return res -} - -// TODO(ODIS 2.0.0 e2e fix) clean up duplicate code -const domainStages = (): SequentialDelayStage[] => [ - { delay: 0, resetTimer: noBool, batchSize: defined(2), repetitions: defined(10) }, -] - -const authenticatedDomain = ( - address: string, - salt: string, - _stages?: SequentialDelayStage[] -): SequentialDelayDomain => ({ - name: DomainIdentifiers.SequentialDelay, - version: '1', - stages: _stages ?? domainStages(), - address: defined(address), - salt: defined(salt), -}) - -const quotaRequest = async ( - wallet: LocalWallet, - address: string, - salt: string -): Promise> => { - const req: DomainQuotaStatusRequest = { - type: DomainRequestTypeTag.QUOTA, - domain: authenticatedDomain(address, salt), - options: { - signature: noString, - nonce: noNumber, - }, - sessionID: defined(genSessionID()), - } - req.options.signature = defined( - await wallet.signTypedData(address, domainQuotaStatusRequestEIP712(req)) - ) - return req -} - -const disableRequest = async ( - wallet: LocalWallet, - address: string, - salt: string -): Promise> => { - const req: DisableDomainRequest = { - type: DomainRequestTypeTag.DISABLE, - domain: authenticatedDomain(address, salt), - options: { - signature: noString, - nonce: noNumber, - }, - sessionID: defined(genSessionID()), - } - req.options.signature = defined( - await wallet.signTypedData(address, disableDomainRequestEIP712(req)) - ) - return req -} - -const signatureRequest = async ( - wallet: LocalWallet, - address: string, - salt: string, - _domain?: SequentialDelayDomain, - _nonce?: number -): Promise<[DomainRestrictedSignatureRequest, ThresholdPoprfClient]> => { - const domain = _domain ?? authenticatedDomain(address, salt) - const thresholdPoprfClient = new ThresholdPoprfClient( - Buffer.from(contextSpecificParams.domainsPubKey, 'base64'), - Buffer.from(contextSpecificParams.domainsPolynomial, 'hex'), - domainHash(domain), - Buffer.from('test message', 'utf8') - ) - - const req: DomainRestrictedSignatureRequest = { - type: DomainRequestTypeTag.SIGN, - domain: domain, - options: { - signature: noString, - nonce: defined(_nonce ?? 0), - }, - blindedMessage: thresholdPoprfClient.blindedMessage.toString('base64'), - sessionID: defined(genSessionID()), - } - req.options.signature = defined( - await wallet.signTypedData(address, domainRestrictedSignatureRequestEIP712(req)) - ) - return [req, thresholdPoprfClient] -} diff --git a/packages/phone-number-privacy/signer/test/end-to-end/get-blinded-sig.test.ts b/packages/phone-number-privacy/signer/test/end-to-end/get-blinded-sig.test.ts deleted file mode 100644 index 3586763a0ef..00000000000 --- a/packages/phone-number-privacy/signer/test/end-to-end/get-blinded-sig.test.ts +++ /dev/null @@ -1,350 +0,0 @@ -import { newKitFromWeb3 } from '@celo/contractkit' -import { - KEY_VERSION_HEADER, - PnpQuotaResponse, - rootLogger, - SignerEndpoint, - SignMessageResponseFailure, - SignMessageResponseSuccess, - TestUtils, -} from '@celo/phone-number-privacy-common' -import { serializeSignature, signMessage } from '@celo/utils/lib/signatureUtils' -import threshold_bls from 'blind-threshold-bls' -import { randomBytes } from 'crypto' -import 'isomorphic-fetch' -import Web3 from 'web3' -import { getWalletAddress } from '../../src/common/web3/contracts' -import { config } from '../../src/config' -import { getBlindedPhoneNumber, getTestParamsForContext } from './utils' - -require('dotenv').config() - -const { - ACCOUNT_ADDRESS1, - ACCOUNT_ADDRESS2, - ACCOUNT_ADDRESS3, - BLINDED_PHONE_NUMBER, - IDENTIFIER, - PHONE_NUMBER, - PRIVATE_KEY1, - PRIVATE_KEY2, - PRIVATE_KEY3, -} = TestUtils.Values -const { replenishQuota, registerWalletAddress } = TestUtils.Utils - -const ODIS_SIGNER = process.env.ODIS_SIGNER_SERVICE_URL -const expectedVersion = process.env.DEPLOYED_SIGNER_SERVICE_VERSION! - -// Keep these checks as is to ensure backwards compatibility -const SIGN_MESSAGE_ENDPOINT = '/getBlindedMessagePartialSig' -const GET_QUOTA_ENDPOINT = '/getQuota' - -const contextSpecificParams = getTestParamsForContext() - -const web3 = new Web3(new Web3.providers.HttpProvider(contextSpecificParams.blockchainProviderURL)) -const contractkit = newKitFromWeb3(web3) -contractkit.addAccount(PRIVATE_KEY1) -contractkit.addAccount(PRIVATE_KEY2) -contractkit.addAccount(PRIVATE_KEY3) - -jest.setTimeout(30000) - -const getRandomBlindedPhoneNumber = () => { - return getBlindedPhoneNumber(PHONE_NUMBER, randomBytes(32)) -} - -describe('Running against a deployed service', () => { - beforeAll(() => { - console.log('Blockchain Provider URL: ' + contextSpecificParams.blockchainProviderURL) - console.log('ODIS_SIGNER: ' + ODIS_SIGNER) - console.log('PNP Public Polynomial: ' + contextSpecificParams.pnpPolynomial) - console.log('Key Version:' + contextSpecificParams.pnpKeyVersion) - }) - - it('Service is deployed at correct version', async () => { - const response = await fetch(ODIS_SIGNER + SignerEndpoint.STATUS, { method: 'GET' }) - const body = await response.json() - // This checks against local package.json version, change if necessary - expect(response.status).toBe(200) - expect(body.version).toBe(expectedVersion) - }) - - describe('Returns status 400 with invalid input', () => { - it('With invalid address', async () => { - const response = await postToSignMessage(BLINDED_PHONE_NUMBER, '0x1234', Date.now(), 'ignore') - expect(response.status).toBe(400) - }) - - it('With missing blindedQueryPhoneNumber', async () => { - const response = await postToSignMessage('', ACCOUNT_ADDRESS1, Date.now()) - expect(response.status).toBe(400) - }) - - it('With invalid blindedQueryPhoneNumber', async () => { - const response = await postToSignMessage('invalid', ACCOUNT_ADDRESS1, Date.now()) - expect(response.status).toBe(400) - }) - }) - - describe('Returns status 401 with invalid authentication headers', () => { - it('With invalid auth header', async () => { - const response = await postToSignMessage( - BLINDED_PHONE_NUMBER, - ACCOUNT_ADDRESS1, - Date.now(), - 'invalid' - ) - expect(response.status).toBe(401) - }) - - it('With auth header signer mismatch', async () => { - // Sign body with different account - const body = JSON.stringify({ - hashedPhoneNumber: '+1455556600', - blindedQueryPhoneNumber: BLINDED_PHONE_NUMBER.trim(), - ACCOUNT_ADDRESS1, - }) - const signature = signMessage(JSON.stringify(body), PRIVATE_KEY2, ACCOUNT_ADDRESS2) - const authHeader = serializeSignature(signature) - - const response = await postToSignMessage( - BLINDED_PHONE_NUMBER, - ACCOUNT_ADDRESS1, - undefined, - authHeader - ) - expect(response.status).toBe(401) - }) - }) - - it('Returns 403 error when querying out of quota', async () => { - const response = await postToSignMessage( - getRandomBlindedPhoneNumber(), - ACCOUNT_ADDRESS1, - Date.now() - ) - expect(response.status).toBe(403) - }) - - describe('When account address has enough quota', () => { - // if these tests are failing, it may just be that the address needs to be fauceted: - // celotooljs account faucet --account ACCOUNT_ADDRESS2 --dollar 1 --gold 1 -e --verbose - - beforeAll(async () => { - console.log('ACCOUNT_ADDRESS1 ' + ACCOUNT_ADDRESS1) - console.log('ACCOUNT_ADDRESS2 ' + ACCOUNT_ADDRESS2) - console.log('ACCOUNT_ADDRESS3 ' + ACCOUNT_ADDRESS3) - - contractkit.defaultAccount = ACCOUNT_ADDRESS2 - }) - - it('Returns sig when querying succeeds', async () => { - await replenishQuota(ACCOUNT_ADDRESS2, contractkit) - const response = await postToSignMessage(BLINDED_PHONE_NUMBER, ACCOUNT_ADDRESS2) - expect(response.status).toBe(200) - }) - - // Backwards compatibility check - it('Returns sig when querying succeeds w/ expired timestamp', async () => { - await replenishQuota(ACCOUNT_ADDRESS2, contractkit) - const response = await postToSignMessage( - BLINDED_PHONE_NUMBER, - ACCOUNT_ADDRESS2, - Date.now() - 10 * 60 * 1000 - ) // 10 minutes ago - expect(response.status).toBe(200) - }) - - it('Increments query count when querying succeeds w/ unused request', async () => { - await replenishQuota(ACCOUNT_ADDRESS2, contractkit) - const initialQueryCount = await getQueryCount(ACCOUNT_ADDRESS2, IDENTIFIER) - await postToSignMessage(getRandomBlindedPhoneNumber(), ACCOUNT_ADDRESS2) - expect(initialQueryCount).toEqual((await getQueryCount(ACCOUNT_ADDRESS2, IDENTIFIER)) - 1) - }) - - // Backwards compatibility check - it('Increments query count when querying succeeds w/ timestamp', async () => { - await replenishQuota(ACCOUNT_ADDRESS2, contractkit) - const initialQueryCount = await getQueryCount(ACCOUNT_ADDRESS2, IDENTIFIER) - await postToSignMessage(getRandomBlindedPhoneNumber(), ACCOUNT_ADDRESS2, Date.now()) - expect(initialQueryCount).toEqual((await getQueryCount(ACCOUNT_ADDRESS2, IDENTIFIER)) - 1) - }) - - it('Returns sig when querying succeeds with replayed request without incrementing query count', async () => { - await replenishQuota(ACCOUNT_ADDRESS2, contractkit) - const blindedPhoneNumber = getRandomBlindedPhoneNumber() - const res1 = await postToSignMessage(blindedPhoneNumber, ACCOUNT_ADDRESS2) - expect(res1.status).toBe(200) - const queryCount = await getQueryCount(ACCOUNT_ADDRESS2, IDENTIFIER) - const res2 = await postToSignMessage(blindedPhoneNumber, ACCOUNT_ADDRESS2) - expect(res2.status).toBe(200) - expect(queryCount).toEqual(await getQueryCount(ACCOUNT_ADDRESS2, IDENTIFIER)) - }) - - // Backwards compatibility check - it('Returns sig when querying succeeds with replayed request without incrementing query count w/ timestamp', async () => { - await replenishQuota(ACCOUNT_ADDRESS2, contractkit) - const res1 = await postToSignMessage(BLINDED_PHONE_NUMBER, ACCOUNT_ADDRESS2, Date.now()) - expect(res1.status).toBe(200) - const queryCount = await getQueryCount(ACCOUNT_ADDRESS2, IDENTIFIER) - const res2 = await postToSignMessage(BLINDED_PHONE_NUMBER, ACCOUNT_ADDRESS2, Date.now()) - expect(res2.status).toBe(200) - expect(queryCount).toEqual(await getQueryCount(ACCOUNT_ADDRESS2, IDENTIFIER)) - }) - }) - - describe('When walletAddress has enough quota', () => { - // if these tests are failing, it may just be that the address needs to be fauceted: - // celotooljs account faucet --account ACCOUNT_ADDRESS2 --dollar 1 --gold 1 -e --verbose - // NOTE: DO NOT FAUCET ACCOUNT_ADDRESS3 - let initialQuota: number - let initialQueryCount: number - beforeAll(async () => { - contractkit.defaultAccount = ACCOUNT_ADDRESS3 - await registerWalletAddress(ACCOUNT_ADDRESS3, ACCOUNT_ADDRESS2, PRIVATE_KEY2, contractkit) - // ACCOUNT_ADDRESS2 is now the wallet address (has quota) - // and ACCOUNT_ADDRESS3 is account address (does not have quota on it's own, only bc of walletAddress) - initialQuota = await getQuota(ACCOUNT_ADDRESS3, IDENTIFIER) - initialQueryCount = await getQueryCount(ACCOUNT_ADDRESS3, IDENTIFIER) - }) - - it('Check that accounts are set up correctly', async () => { - expect(await getQuota(ACCOUNT_ADDRESS2, IDENTIFIER)).toBeLessThan(initialQuota) - expect( - await getWalletAddress( - contractkit, - rootLogger(config.serviceName), - ACCOUNT_ADDRESS3, - SignerEndpoint.LEGACY_PNP_SIGN - ) - ).toBe(ACCOUNT_ADDRESS2) - }) - - // Note: Use this test to check the signers' key configuration. Modify .env to try out different - // key/version combinations - it('[Signer configuration test] Returns sig when querying succeeds with unused request', async () => { - await replenishQuota(ACCOUNT_ADDRESS2, contractkit) - const blindedPhoneNumber = getRandomBlindedPhoneNumber() - const response = await postToSignMessage(blindedPhoneNumber, ACCOUNT_ADDRESS3) - expect(response.status).toBe(200) - - // Validate signature - type SignerResponse = SignMessageResponseSuccess | SignMessageResponseFailure - const data = await response.text() - const signResponse = JSON.parse(data) as SignerResponse - expect(signResponse.success).toBeTruthy() - if (signResponse.success) { - const sigBuffer = Buffer.from(signResponse.signature as string, 'base64') - const isValid = isValidSignature( - sigBuffer, - blindedPhoneNumber, - contextSpecificParams.pnpPolynomial - ) - expect(isValid).toBeTruthy() - } - }) - - it('Returns count when querying with unused request increments query count', async () => { - const queryCount = await getQueryCount(ACCOUNT_ADDRESS3, IDENTIFIER) - expect(queryCount).toEqual(initialQueryCount + 1) - }) - - it('Returns sig when querying succeeds with used request', async () => { - await replenishQuota(ACCOUNT_ADDRESS2, contractkit) - const response = await postToSignMessage(BLINDED_PHONE_NUMBER, ACCOUNT_ADDRESS3) - expect(response.status).toBe(200) - }) - - it('Returns count when querying with used request does not increment query count', async () => { - const queryCount = await getQueryCount(ACCOUNT_ADDRESS3, IDENTIFIER) - expect(queryCount).toEqual(initialQueryCount + 1) - }) - }) -}) - -async function getQuota( - account: string, - hashedPhoneNumber?: string, - authHeader?: string -): Promise { - const res = await queryQuotaEndpoint(account, hashedPhoneNumber, authHeader) - return res.success ? res.totalQuota ?? 0 : 0 -} - -async function getQueryCount( - account: string, - hashedPhoneNumber?: string, - authHeader?: string -): Promise { - const res = await queryQuotaEndpoint(account, hashedPhoneNumber, authHeader) - return res.success ? res.performedQueryCount ?? 0 : 0 -} - -async function queryQuotaEndpoint( - account: string, - hashedPhoneNumber?: string, - authHeader?: string -): Promise { - const body = JSON.stringify({ - account, - hashedPhoneNumber, - }) - - const authorization = authHeader || (await contractkit.connection.sign(body, account)) - - const res = await fetch(ODIS_SIGNER + GET_QUOTA_ENDPOINT, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - Authorization: authorization, - }, - body, - }) - - return res.json() -} - -async function postToSignMessage( - base64BlindedMessage: string, - account: string, - timestamp?: number, - authHeader?: string, - keyVersion: string = contextSpecificParams.pnpKeyVersion -): Promise { - const body = JSON.stringify({ - hashedPhoneNumber: IDENTIFIER, - blindedQueryPhoneNumber: base64BlindedMessage.trim(), - account, - timestamp, - }) - - const authorization = authHeader || (await contractkit.connection.sign(body, account)) - - const res = await fetch(ODIS_SIGNER + SIGN_MESSAGE_ENDPOINT, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - Authorization: authorization, - [KEY_VERSION_HEADER]: keyVersion, - }, - body, - }) - - return res -} - -function isValidSignature(signature: Buffer, blindedMessage: string, polynomial: string) { - try { - threshold_bls.partialVerifyBlindSignature( - Buffer.from(polynomial, 'hex'), - Buffer.from(blindedMessage, 'base64'), - signature - ) - return true - } catch (err) { - console.log(err) - return false - } -} diff --git a/packages/phone-number-privacy/signer/test/end-to-end/pnp.test.ts b/packages/phone-number-privacy/signer/test/end-to-end/pnp.test.ts deleted file mode 100644 index 0a36158c807..00000000000 --- a/packages/phone-number-privacy/signer/test/end-to-end/pnp.test.ts +++ /dev/null @@ -1,502 +0,0 @@ -import { newKit, StableToken } from '@celo/contractkit' -import { - AuthenticationMethod, - KEY_VERSION_HEADER, - PnpQuotaRequest, - PnpQuotaResponseFailure, - PnpQuotaResponseSuccess, - SignerEndpoint, - SignMessageRequest, - SignMessageResponseFailure, - SignMessageResponseSuccess, - TestUtils, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import threshold_bls from 'blind-threshold-bls' -import { randomBytes } from 'crypto' -import 'isomorphic-fetch' -import { config } from '../../src/config' -import { getBlindedPhoneNumber, getTestParamsForContext } from './utils' - -require('dotenv').config() - -const { - ACCOUNT_ADDRESS1, // zero OdisPayments balance/quota - ACCOUNT_ADDRESS2, // non-zero OdisPayments balance/quota - DEK_PRIVATE_KEY, - DEK_PUBLIC_KEY, - PHONE_NUMBER, - PRIVATE_KEY1, - PRIVATE_KEY2, - PRIVATE_KEY3, -} = TestUtils.Values -const { getPnpQuotaRequest, getPnpRequestAuthorization, getPnpSignRequest } = TestUtils.Utils - -const ODIS_SIGNER_URL = process.env.ODIS_SIGNER_SERVICE_URL -const contextSpecificParams = getTestParamsForContext() - -const kit = newKit(contextSpecificParams.blockchainProviderURL) -kit.addAccount(PRIVATE_KEY1) -kit.addAccount(PRIVATE_KEY2) -kit.addAccount(PRIVATE_KEY3) - -jest.setTimeout(60000) - -const expectedVersion = process.env.DEPLOYED_SIGNER_SERVICE_VERSION! - -describe(`Running against service deployed at ${ODIS_SIGNER_URL}`, () => { - const singleQueryCost = config.quota.queryPriceInCUSD.times(1e18).toString() - - beforeAll(async () => { - const accountsWrapper = await kit.contracts.getAccounts() - if ((await accountsWrapper.getDataEncryptionKey(ACCOUNT_ADDRESS2)) !== DEK_PUBLIC_KEY) { - await accountsWrapper - .setAccountDataEncryptionKey(DEK_PUBLIC_KEY) - .sendAndWaitForReceipt({ from: ACCOUNT_ADDRESS2 }) - } - }) - - it('Service is deployed at correct version', async () => { - const response = await fetch(ODIS_SIGNER_URL + SignerEndpoint.STATUS, { - method: 'GET', - }) - expect(response.status).toBe(200) - const body = await response.json() - // This checks against local package.json version, change if necessary - expect(body.version).toBe(expectedVersion) - }) - - describe(`${SignerEndpoint.PNP_QUOTA}`, () => { - it('Should respond with 200 on valid request', async () => { - const req = getPnpQuotaRequest(ACCOUNT_ADDRESS1) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await queryPnpQuotaEndpoint(req, authorization) - expect(res.status).toBe(200) - const resBody: PnpQuotaResponseSuccess = await res.json() - expect(resBody).toStrictEqual({ - success: true, - version: expectedVersion, - performedQueryCount: 0, - totalQuota: 0, - blockNumber: resBody.blockNumber, - warnings: [], - }) - }) - - it('Should respond with 200 on valid request when authenticated with DEK', async () => { - const req = getPnpQuotaRequest(ACCOUNT_ADDRESS2, AuthenticationMethod.ENCRYPTION_KEY) - const authorization = getPnpRequestAuthorization(req, DEK_PRIVATE_KEY) - const res = await queryPnpQuotaEndpoint(req, authorization) - expect(res.status).toBe(200) - const resBody: PnpQuotaResponseSuccess = await res.json() - expect(resBody).toStrictEqual({ - success: true, - version: expectedVersion, - performedQueryCount: resBody.performedQueryCount, - totalQuota: resBody.totalQuota, - blockNumber: resBody.blockNumber, - warnings: [], - }) - expect(resBody.totalQuota).toBeGreaterThan(0) - }) - - it('Should respond with 200 and more quota after payment sent to OdisPayments.sol', async () => { - const req = getPnpQuotaRequest(ACCOUNT_ADDRESS2) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY2) - const res = await queryPnpQuotaEndpoint(req, authorization) - expect(res.status).toBe(200) - const resBody: PnpQuotaResponseSuccess = await res.json() - await sendCUSDToOdisPayments(singleQueryCost, ACCOUNT_ADDRESS2, ACCOUNT_ADDRESS2) - - const res2 = await queryPnpQuotaEndpoint(req, authorization) - expect(res2.status).toBe(200) - const res2Body: PnpQuotaResponseSuccess = await res2.json() - expect(res2Body).toStrictEqual({ - success: true, - version: expectedVersion, - performedQueryCount: resBody.performedQueryCount, - totalQuota: resBody.totalQuota + 1, - blockNumber: res2Body.blockNumber, - warnings: [], - }) - }) - - it('Should respond with 400 on missing request fields', async () => { - const badRequest = getPnpQuotaRequest(ACCOUNT_ADDRESS1) - // @ts-ignore Intentionally deleting required field - delete badRequest.account - const authorization = getPnpRequestAuthorization(badRequest, PRIVATE_KEY1) - const res = await queryPnpQuotaEndpoint(badRequest, authorization) - expect(res.status).toBe(400) - const resBody: PnpQuotaResponseFailure = await res.json() - expect(resBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 401 on failed WALLET_KEY auth', async () => { - const badRequest = getPnpQuotaRequest(ACCOUNT_ADDRESS2, AuthenticationMethod.WALLET_KEY) - const authorization = getPnpRequestAuthorization(badRequest, PRIVATE_KEY1) - const res = await queryPnpQuotaEndpoint(badRequest, authorization) - expect(res.status).toBe(401) - const resBody: PnpQuotaResponseFailure = await res.json() - expect(resBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.UNAUTHENTICATED_USER, - }) - }) - - it('Should respond with 401 on failed DEK auth when DEK is set for account', async () => { - const badRequest = getPnpQuotaRequest(ACCOUNT_ADDRESS2, AuthenticationMethod.ENCRYPTION_KEY) - const authorization = getPnpRequestAuthorization(badRequest, PRIVATE_KEY2) - const res = await queryPnpQuotaEndpoint(badRequest, authorization) - expect(res.status).toBe(401) - const resBody: PnpQuotaResponseFailure = await res.json() - expect(resBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.UNAUTHENTICATED_USER, - }) - }) - - it('Should respond with 401 on failed DEK auth when DEK is not set for account', async () => { - const badRequest = getPnpQuotaRequest(ACCOUNT_ADDRESS1, AuthenticationMethod.ENCRYPTION_KEY) - const authorization = getPnpRequestAuthorization(badRequest, PRIVATE_KEY1) - const res = await queryPnpQuotaEndpoint(badRequest, authorization) - expect(res.status).toBe(401) - const resBody: PnpQuotaResponseFailure = await res.json() - expect(resBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.UNAUTHENTICATED_USER, - }) - }) - }) - - describe(`${SignerEndpoint.PNP_SIGN}`, () => { - describe('success cases', () => { - let startingPerformedQueryCount: number - - beforeEach(async () => { - const req = getPnpQuotaRequest(ACCOUNT_ADDRESS2) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY2) - const res = await queryPnpQuotaEndpoint(req, authorization) - expect(res.status).toBe(200) - const resBody: PnpQuotaResponseSuccess = await res.json() - startingPerformedQueryCount = resBody.performedQueryCount - }) - - it('[Signer configuration test] Should respond with 200 on valid request', async () => { - const blindedMessage = getBlindedPhoneNumber(PHONE_NUMBER, randomBytes(32)) - const req = getPnpSignRequest( - ACCOUNT_ADDRESS2, - blindedMessage, - AuthenticationMethod.WALLET_KEY - ) - await sendCUSDToOdisPayments(singleQueryCost, ACCOUNT_ADDRESS2, ACCOUNT_ADDRESS2) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY2) - const res = await queryPnpSignEndpoint(req, authorization) - expect(res.status).toBe(200) - const resBody: SignMessageResponseSuccess = await res.json() - expect(resBody).toStrictEqual({ - success: true, - version: expectedVersion, - signature: resBody.signature, - performedQueryCount: startingPerformedQueryCount + 1, - totalQuota: resBody.totalQuota, - blockNumber: resBody.blockNumber, - warnings: [], - }) - expect(res.headers.get(KEY_VERSION_HEADER)).toEqual(contextSpecificParams.pnpKeyVersion) - expect( - threshold_bls.partialVerifyBlindSignature( - Buffer.from(contextSpecificParams.pnpPolynomial, 'hex'), - Buffer.from(blindedMessage, 'base64'), - Buffer.from(resBody.signature, 'base64') - ) - ) - }) - - it(`Should respond with 200 on valid request with key version ${contextSpecificParams.pnpKeyVersion}`, async () => { - // This value can also be modified but needs to be manually inspected in the signer logs - // (on staging) since a valid key version that does not exist in the keystore - // will default to the secretName stored in `KEYSTORE_AZURE_SECRET_NAME` - const keyVersion = contextSpecificParams.pnpKeyVersion - const blindedMessage = getBlindedPhoneNumber(PHONE_NUMBER, randomBytes(32)) - const req = getPnpSignRequest( - ACCOUNT_ADDRESS2, - blindedMessage, - AuthenticationMethod.WALLET_KEY - ) - await sendCUSDToOdisPayments(singleQueryCost, ACCOUNT_ADDRESS2, ACCOUNT_ADDRESS2) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY2) - const res = await queryPnpSignEndpoint(req, authorization, keyVersion) - expect(res.status).toBe(200) - const resBody: SignMessageResponseSuccess = await res.json() - expect(resBody).toStrictEqual({ - success: true, - version: expectedVersion, - signature: resBody.signature, - performedQueryCount: startingPerformedQueryCount + 1, - totalQuota: resBody.totalQuota, - blockNumber: resBody.blockNumber, - warnings: [], - }) - expect(res.headers.get(KEY_VERSION_HEADER)).toEqual(keyVersion) - expect( - threshold_bls.partialVerifyBlindSignature( - Buffer.from(contextSpecificParams.pnpPolynomial, 'hex'), - Buffer.from(blindedMessage, 'base64'), - Buffer.from(resBody.signature, 'base64') - ) - ) - }) - - it('Should respond with 200 and warning on repeated valid requests', async () => { - await sendCUSDToOdisPayments(singleQueryCost, ACCOUNT_ADDRESS2, ACCOUNT_ADDRESS2) - const blindedMessage = getBlindedPhoneNumber(PHONE_NUMBER, randomBytes(32)) - const req = getPnpSignRequest( - ACCOUNT_ADDRESS2, - blindedMessage, - AuthenticationMethod.WALLET_KEY - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY2) - const res = await queryPnpSignEndpoint(req, authorization) - expect(res.status).toBe(200) - const resBody: SignMessageResponseSuccess = await res.json() - expect(resBody).toStrictEqual({ - success: true, - version: expectedVersion, - signature: resBody.signature, - performedQueryCount: startingPerformedQueryCount + 1, - totalQuota: resBody.totalQuota, - blockNumber: resBody.blockNumber, - warnings: [], - }) - expect(res.headers.get(KEY_VERSION_HEADER)).toEqual(contextSpecificParams.pnpKeyVersion) - expect( - threshold_bls.partialVerifyBlindSignature( - Buffer.from(contextSpecificParams.pnpPolynomial, 'hex'), - Buffer.from(blindedMessage, 'base64'), - Buffer.from(resBody.signature, 'base64') - ) - ) - const res2 = await queryPnpSignEndpoint(req, authorization) - expect(res2.status).toBe(200) - const res2Body: SignMessageResponseSuccess = await res2.json() - expect(res2Body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: resBody.signature, - performedQueryCount: resBody.performedQueryCount, // Not incremented - totalQuota: resBody.totalQuota, - blockNumber: res2Body.blockNumber, - warnings: [WarningMessage.DUPLICATE_REQUEST_TO_GET_PARTIAL_SIG], - }) - }) - }) - - describe('failure cases', () => { - const blindedMessage = getBlindedPhoneNumber(PHONE_NUMBER, randomBytes(32)) - - it('Should respond with 400 on missing request fields', async () => { - const badRequest = getPnpSignRequest( - ACCOUNT_ADDRESS2, - blindedMessage, - AuthenticationMethod.WALLET_KEY - ) - // @ts-ignore Intentionally deleting required field - delete badRequest.blindedQueryPhoneNumber - const authorization = getPnpRequestAuthorization(badRequest, PRIVATE_KEY1) - const res = await queryPnpSignEndpoint(badRequest, authorization) - expect(res.status).toBe(400) - const resBody: SignMessageResponseFailure = await res.json() - expect(resBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 on on invalid key version', async () => { - const badRequest = getPnpSignRequest( - ACCOUNT_ADDRESS2, - blindedMessage, - AuthenticationMethod.WALLET_KEY - ) - const authorization = getPnpRequestAuthorization(badRequest, PRIVATE_KEY1) - const res = await queryPnpSignEndpoint(badRequest, authorization, 'asd') - expect(res.status).toBe(400) - const resBody: SignMessageResponseFailure = await res.json() - expect(resBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_KEY_VERSION_REQUEST, - }) - }) - - it('Should respond with 400 on on invalid blinded message', async () => { - const badRequest = getPnpSignRequest( - ACCOUNT_ADDRESS2, - PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY - ) - const authorization = getPnpRequestAuthorization(badRequest, PRIVATE_KEY1) - const res = await queryPnpSignEndpoint(badRequest, authorization) - expect(res.status).toBe(400) - const resBody: SignMessageResponseFailure = await res.json() - expect(resBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 on invalid address', async () => { - const badRequest = getPnpSignRequest( - '0xnotanaddress', - blindedMessage, - AuthenticationMethod.WALLET_KEY - ) - const authorization = getPnpRequestAuthorization(badRequest, PRIVATE_KEY1) - const res = await queryPnpSignEndpoint(badRequest, authorization) - expect(res.status).toBe(400) - const resBody: SignMessageResponseFailure = await res.json() - expect(resBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 401 on failed WALLET_KEY auth', async () => { - const badRequest = getPnpSignRequest( - ACCOUNT_ADDRESS2, - blindedMessage, - AuthenticationMethod.WALLET_KEY - ) - const authorization = getPnpRequestAuthorization(badRequest, PRIVATE_KEY1) - const res = await queryPnpSignEndpoint(badRequest, authorization) - expect(res.status).toBe(401) - const resBody: SignMessageResponseFailure = await res.json() - expect(resBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.UNAUTHENTICATED_USER, - }) - }) - - it('Should respond with 401 on failed DEK auth when DEK is set for account', async () => { - const badRequest = getPnpSignRequest( - ACCOUNT_ADDRESS2, - blindedMessage, - AuthenticationMethod.ENCRYPTION_KEY - ) - const authorization = getPnpRequestAuthorization(badRequest, PRIVATE_KEY2) - const res = await queryPnpSignEndpoint(badRequest, authorization) - expect(res.status).toBe(401) - const resBody: SignMessageResponseFailure = await res.json() - expect(resBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.UNAUTHENTICATED_USER, - }) - }) - - it('Should respond with 401 on failed DEK auth when DEK is not set for account', async () => { - const badRequest = getPnpSignRequest( - ACCOUNT_ADDRESS1, - blindedMessage, - AuthenticationMethod.ENCRYPTION_KEY - ) - const authorization = getPnpRequestAuthorization(badRequest, PRIVATE_KEY1) - const res = await queryPnpSignEndpoint(badRequest, authorization) - expect(res.status).toBe(401) - const resBody: SignMessageResponseFailure = await res.json() - expect(resBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.UNAUTHENTICATED_USER, - }) - }) - - it('Should respond with 403 on out of quota', async () => { - const quotaReq = getPnpQuotaRequest(ACCOUNT_ADDRESS1) - const quotaAuthorization = getPnpRequestAuthorization(quotaReq, PRIVATE_KEY1) - const quotaRes = await queryPnpQuotaEndpoint(quotaReq, quotaAuthorization) - expect(quotaRes.status).toBe(200) - const quotaResBody: PnpQuotaResponseSuccess = await quotaRes.json() - // Sanity check - expect(quotaResBody.performedQueryCount).toEqual(quotaResBody.totalQuota) - - const req = getPnpSignRequest(ACCOUNT_ADDRESS1, blindedMessage) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await queryPnpSignEndpoint(req, authorization) - expect(res.status).toBe(403) - const resBody: SignMessageResponseFailure = await res.json() - expect(resBody).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.EXCEEDED_QUOTA, - totalQuota: quotaResBody.totalQuota, - performedQueryCount: quotaResBody.performedQueryCount, - blockNumber: resBody.blockNumber, - }) - }) - }) - }) -}) - -async function queryPnpQuotaEndpoint( - req: PnpQuotaRequest, - authorization: string -): Promise { - const body = JSON.stringify(req) - return fetch(ODIS_SIGNER_URL + SignerEndpoint.PNP_QUOTA, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - Authorization: authorization, - }, - body, - }) -} - -async function queryPnpSignEndpoint( - req: SignMessageRequest, - authorization: string, - keyVersion?: string -): Promise { - const body = JSON.stringify(req) - const headers: any = { - Accept: 'application/json', - 'Content-Type': 'application/json', - Authorization: authorization, - } - if (keyVersion !== undefined) { - headers[KEY_VERSION_HEADER] = keyVersion - } - const res = await fetch(ODIS_SIGNER_URL + SignerEndpoint.PNP_SIGN, { - method: 'POST', - headers, - body, - }) - return res -} - -async function sendCUSDToOdisPayments( - amountInWei: string | number, - recipient: string, - sender: string -) { - const stableToken = await kit.contracts.getStableToken(StableToken.cUSD) - const odisPayments = await kit.contracts.getOdisPayments() - await stableToken - .approve(odisPayments.address, amountInWei) - .sendAndWaitForReceipt({ from: sender }) - await odisPayments.payInCUSD(recipient, amountInWei).sendAndWaitForReceipt({ from: sender }) -} diff --git a/packages/phone-number-privacy/signer/test/end-to-end/utils.ts b/packages/phone-number-privacy/signer/test/end-to-end/utils.ts deleted file mode 100644 index 59b9972715c..00000000000 --- a/packages/phone-number-privacy/signer/test/end-to-end/utils.ts +++ /dev/null @@ -1,53 +0,0 @@ -import threshold_bls from 'blind-threshold-bls' - -require('dotenv').config() - -export function getBlindedPhoneNumber(phoneNumber: string, blindingFactor: Buffer): string { - const blindedPhoneNumber = threshold_bls.blind(Buffer.from(phoneNumber), blindingFactor).message - return Buffer.from(blindedPhoneNumber).toString('base64') -} - -export type E2ETestParams = { - blockchainProviderURL: string - pnpPolynomial: string - pnpKeyVersion: string - domainsPolynomial: string - domainsPubKey: string - domainsKeyVersion: string -} - -// Once context-specific params are located in the common package, -// consider using that instead of redefining the specifics here. -export function getTestParamsForContext(): E2ETestParams { - switch (process.env.CONTEXT_NAME) { - case 'staging': - return { - blockchainProviderURL: process.env.STAGING_ODIS_BLOCKCHAIN_PROVIDER!, - pnpPolynomial: process.env.STAGING_POLYNOMIAL!, - pnpKeyVersion: process.env.ODIS_PNP_TEST_KEY_VERSION!, - domainsPolynomial: process.env.STAGING_POLYNOMIAL!, - domainsPubKey: process.env.STAGING_DOMAINS_PUBKEY!, - domainsKeyVersion: process.env.ODIS_DOMAINS_TEST_KEY_VERSION!, - } - case 'alfajores': - return { - blockchainProviderURL: process.env.ALFAJORES_ODIS_BLOCKCHAIN_PROVIDER!, - pnpPolynomial: process.env.ALFAJORES_PHONE_NUMBER_PRIVACY_POLYNOMIAL!, - pnpKeyVersion: process.env.ODIS_PNP_TEST_KEY_VERSION!, - domainsPolynomial: process.env.ALFAJORES_DOMAINS_POLYNOMIAL!, - domainsPubKey: process.env.ALFAJORES_DOMAINS_PUBKEY!, - domainsKeyVersion: process.env.ODIS_DOMAINS_TEST_KEY_VERSION!, - } - case 'mainnet': - return { - blockchainProviderURL: process.env.MAINNET_ODIS_BLOCKCHAIN_PROVIDER!, - pnpPolynomial: process.env.MAINNET_PHONE_NUMBER_PRIVACY_POLYNOMIAL!, - pnpKeyVersion: process.env.ODIS_PNP_TEST_KEY_VERSION!, - domainsPolynomial: process.env.MAINNET_DOMAINS_POLYNOMIAL!, - domainsPubKey: process.env.MAINNET_DOMAINS_PUBKEY!, - domainsKeyVersion: process.env.ODIS_DOMAINS_TEST_KEY_VERSION!, - } - default: - throw new Error(`No parameter settings stored for context: ${process.env.CONTEXT_NAME}`) - } -} diff --git a/packages/phone-number-privacy/signer/test/integration/domain.test.ts b/packages/phone-number-privacy/signer/test/integration/domain.test.ts deleted file mode 100644 index 9299916a37c..00000000000 --- a/packages/phone-number-privacy/signer/test/integration/domain.test.ts +++ /dev/null @@ -1,1044 +0,0 @@ -import { - DisableDomainRequest, - disableDomainRequestEIP712, - DisableDomainResponse, - DisableDomainResponseSuccess, - domainHash, - DomainIdentifiers, - DomainQuotaStatusRequest, - domainQuotaStatusRequestEIP712, - DomainQuotaStatusResponse, - DomainRequestTypeTag, - DomainRestrictedSignatureRequest, - domainRestrictedSignatureRequestEIP712, - DomainRestrictedSignatureResponse, - ErrorMessage, - genSessionID, - KEY_VERSION_HEADER, - rootLogger, - SequentialDelayDomain, - SequentialDelayStage, - SignerEndpoint, - TestUtils, - ThresholdPoprfClient, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import { defined, noBool, noNumber, noString } from '@celo/utils/lib/sign-typed-data-utils' -import { LocalWallet } from '@celo/wallet-local' -import { Knex } from 'knex' -import request from 'supertest' -import { initDatabase } from '../../src/common/database/database' -import { countAndThrowDBError } from '../../src/common/database/utils' -import { - createEmptyDomainStateRecord, - getDomainStateRecord, -} from '../../src/common/database/wrappers/domain-state' -import { initKeyProvider } from '../../src/common/key-management/key-provider' -import { KeyProvider } from '../../src/common/key-management/key-provider-base' -import { config, getSignerVersion, SupportedDatabase, SupportedKeystore } from '../../src/config' -import { startSigner } from '../../src/server' - -jest.setTimeout(20000) - -describe('domain', () => { - const wallet = new LocalWallet() - wallet.addAccount('0x00000000000000000000000000000000000000000000000000000000deadbeef') - const walletAddress = wallet.getAccounts()[0]! - - const expectedVersion = getSignerVersion() - - const domainStages = (): SequentialDelayStage[] => [ - { delay: 0, resetTimer: noBool, batchSize: defined(2), repetitions: defined(10) }, - ] - - const authenticatedDomain = (_stages?: SequentialDelayStage[]): SequentialDelayDomain => ({ - name: DomainIdentifiers.SequentialDelay, - version: '1', - stages: _stages ?? domainStages(), - address: defined(walletAddress), - salt: defined('himalayanPink'), - }) - - const signatureRequest = async ( - _domain?: SequentialDelayDomain, - _nonce?: number, - keyVersion: number = config.keystore.keys.domains.latest - ): Promise<[DomainRestrictedSignatureRequest, ThresholdPoprfClient]> => { - const domain = _domain ?? authenticatedDomain() - const thresholdPoprfClient = new ThresholdPoprfClient( - Buffer.from(TestUtils.Values.DOMAINS_THRESHOLD_DEV_PUBKEYS[keyVersion - 1], 'base64'), - Buffer.from(TestUtils.Values.DOMAINS_THRESHOLD_DEV_POLYNOMIALS[keyVersion - 1], 'hex'), - domainHash(domain), - Buffer.from('test message', 'utf8') - ) - - const req: DomainRestrictedSignatureRequest = { - type: DomainRequestTypeTag.SIGN, - domain: domain, - options: { - signature: noString, - nonce: defined(_nonce ?? 0), - }, - blindedMessage: thresholdPoprfClient.blindedMessage.toString('base64'), - sessionID: defined(genSessionID()), - } - req.options.signature = defined( - await wallet.signTypedData(walletAddress, domainRestrictedSignatureRequestEIP712(req)) - ) - return [req, thresholdPoprfClient] - } - - const quotaRequest = async (): Promise> => { - const req: DomainQuotaStatusRequest = { - type: DomainRequestTypeTag.QUOTA, - domain: authenticatedDomain(), - options: { - signature: noString, - nonce: noNumber, - }, - sessionID: defined(genSessionID()), - } - req.options.signature = defined( - await wallet.signTypedData(walletAddress, domainQuotaStatusRequestEIP712(req)) - ) - return req - } - - // Build and sign an example disable domain request. - const disableRequest = async (): Promise> => { - const req: DisableDomainRequest = { - type: DomainRequestTypeTag.DISABLE, - domain: authenticatedDomain(), - options: { - signature: noString, - nonce: noNumber, - }, - sessionID: defined(genSessionID()), - } - req.options.signature = defined( - await wallet.signTypedData(walletAddress, disableDomainRequestEIP712(req)) - ) - return req - } - - let keyProvider: KeyProvider - let app: any - let db: Knex - - // create deep copy - const _config: typeof config = JSON.parse(JSON.stringify(config)) - _config.db.type = SupportedDatabase.Sqlite - _config.keystore.type = SupportedKeystore.MOCK_SECRET_MANAGER - _config.api.domains.enabled = true - - beforeAll(async () => { - keyProvider = await initKeyProvider(_config) - }) - - beforeEach(async () => { - // Create a new in-memory database for each test. - db = await initDatabase(_config) - app = startSigner(_config, db, keyProvider) - }) - - afterEach(async () => { - // Close and destroy the in-memory database. - // Note: If tests start to be too slow, this could be replaced with more complicated logic to - // reset the database state without destroying and recreating it for each test. - - await db?.destroy() - }) - - describe(`${SignerEndpoint.STATUS}`, () => { - it('Should return 200 and correct version', async () => { - const res = await request(app).get(SignerEndpoint.STATUS) - expect(res.status).toBe(200) - expect(res.body.version).toBe(expectedVersion) - }) - }) - - describe(`${SignerEndpoint.DISABLE_DOMAIN}`, () => { - it('Should respond with 200 on valid request', async () => { - const res = await request(app) - .post(SignerEndpoint.DISABLE_DOMAIN) - .send(await disableRequest()) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: res.body.version, - status: { - disabled: true, - counter: 0, - timer: 0, - now: res.body.status.now, - }, - }) - }) - - it('Should respond with 200 on repeated valid requests', async () => { - const req = await disableRequest() - const res1 = await request(app).post(SignerEndpoint.DISABLE_DOMAIN).send(req) - expect(res1.status).toBe(200) - const expectedResponse: DisableDomainResponseSuccess = { - success: true, - version: res1.body.version, - status: { - disabled: true, - counter: 0, - timer: 0, - now: res1.body.status.now, - }, - } - expect(res1.body).toStrictEqual(expectedResponse) - const res2 = await request(app).post(SignerEndpoint.DISABLE_DOMAIN).send(req) - expect(res2.status).toBe(200) - // Avoid flakiness due to mismatching times between res1 & res2 - expectedResponse.status.now = res2.body.status.now - expect(res2.body).toStrictEqual(expectedResponse) - }) - - it('Should respond with 200 on extra request fields', async () => { - const req = await disableRequest() - // @ts-ignore Intentionally adding an extra field to the request type - req.options.extraField = noString - - const res = await request(app).post(SignerEndpoint.DISABLE_DOMAIN).send(req) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: res.body.version, - status: { - disabled: true, - counter: 0, - timer: 0, - now: res.body.status.now, - }, - }) - }) - - it('Should respond with 400 on missing request fields', async () => { - const badRequest = await disableRequest() - // @ts-ignore Intentionally deleting required field - delete badRequest.domain.version - - const res = await request(app).post(SignerEndpoint.DISABLE_DOMAIN).send(badRequest) - - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 on unknown domain', async () => { - // Create a request with an invalid domain identifier. - const unknownRequest = await disableRequest() - // @ts-ignore UnknownDomain is (intentionally) not a valid domain identifier. - unknownRequest.domain.name = 'UnknownDomain' - - const res = await request(app).post(SignerEndpoint.DISABLE_DOMAIN).send(unknownRequest) - - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 on bad encoding', async () => { - const badRequest1 = await disableRequest() - // @ts-ignore Intentionally not JSON - badRequest1.domain = 'Freddy' - - const res1 = await request(app).post(SignerEndpoint.DISABLE_DOMAIN).send(badRequest1) - - expect(res1.status).toBe(400) - expect(res1.body).toStrictEqual({ - success: false, - version: res1.body.version, - error: WarningMessage.INVALID_INPUT, - }) - - const badRequest2 = '' - - const res2 = await request(app).post(SignerEndpoint.DISABLE_DOMAIN).send(badRequest2) - - expect(res2.status).toBe(400) - expect(res2.body).toStrictEqual({ - success: false, - version: res2.body.version, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 401 on failed auth', async () => { - // Create a manipulated request, which will have a bad signature. - const badRequest = await disableRequest() - badRequest.domain.salt = defined('badSalt') - - const res = await request(app).post(SignerEndpoint.DISABLE_DOMAIN).send(badRequest) - - expect(res.status).toBe(401) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.UNAUTHENTICATED_USER, - }) - }) - - it('Should respond with 503 on disabled api', async () => { - const configWithApiDisabled: typeof _config = JSON.parse(JSON.stringify(_config)) - configWithApiDisabled.api.domains.enabled = false - const appWithApiDisabled = startSigner(configWithApiDisabled, db, keyProvider) - - const req = await disableRequest() - - const res = await request(appWithApiDisabled).post(SignerEndpoint.DISABLE_DOMAIN).send(req) - - expect(res.status).toBe(503) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.API_UNAVAILABLE, - }) - }) - - describe('functionality in case of errors', () => { - it('Should respond with 500 on DB insertDomainStateRecord failure', async () => { - const req = await disableRequest() - const spy = jest - .spyOn( - jest.requireActual('../../src/common/database/wrappers/domain-state'), - 'insertDomainStateRecord' - ) - .mockImplementationOnce(() => { - // Handle errors in the same way as in insertDomainStateRecord - countAndThrowDBError( - new Error(), - rootLogger(_config.serviceName), - ErrorMessage.DATABASE_INSERT_FAILURE - ) - }) - const res = await request(app).post(SignerEndpoint.DISABLE_DOMAIN).send(req) - spy.mockRestore() - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: ErrorMessage.DATABASE_INSERT_FAILURE, - }) - expect(await getDomainStateRecord(db, req.domain, rootLogger(_config.serviceName))).toBe( - null - ) - }) - - it('Should respond with 500 on signer timeout', async () => { - const testTimeoutMS = 0 - const delay = 200 - - const configWithShortTimeout = JSON.parse(JSON.stringify(_config)) - configWithShortTimeout.timeout = testTimeoutMS - const appWithShortTimeout = startSigner(configWithShortTimeout, db, keyProvider) - - const req = await disableRequest() - const spy = jest - .spyOn( - jest.requireActual('../../src/common/database/wrappers/domain-state'), - 'getDomainStateRecord' - ) - .mockImplementationOnce(async () => { - await new Promise((resolve) => setTimeout(resolve, testTimeoutMS + delay)) - return null - }) - - const res = await request(appWithShortTimeout).post(SignerEndpoint.DISABLE_DOMAIN).send(req) - spy.mockRestore() - - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - error: ErrorMessage.TIMEOUT_FROM_SIGNER, - version: expectedVersion, - }) - // Allow time for non-killed processes to finish - await new Promise((resolve) => setTimeout(resolve, delay)) - // Check that DB state was not updated on timeout - expect(await getDomainStateRecord(db, req.domain, rootLogger(_config.serviceName))).toBe( - null - ) - }) - }) - }) - - describe(`${SignerEndpoint.DOMAIN_QUOTA_STATUS}`, () => { - it('Should respond with 200 on valid request', async () => { - const res = await request(app) - .post(SignerEndpoint.DOMAIN_QUOTA_STATUS) - .send(await quotaRequest()) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: res.body.version, - status: { disabled: false, counter: 0, timer: 0, now: res.body.status.now }, - }) - }) - - it('Should respond with 200 on repeated valid requests', async () => { - const res1 = await request(app) - .post(SignerEndpoint.DOMAIN_QUOTA_STATUS) - .send(await quotaRequest()) - expect(res1.status).toBe(200) - expect(res1.body).toStrictEqual({ - success: true, - version: res1.body.version, - status: { disabled: false, counter: 0, timer: 0, now: res1.body.status.now }, - }) - - const res2 = await request(app) - .post(SignerEndpoint.DOMAIN_QUOTA_STATUS) - .send(await quotaRequest()) - expect(res2.status).toBe(200) - expect(res2.body).toStrictEqual({ - success: true, - version: res2.body.version, - status: { disabled: false, counter: 0, timer: 0, now: res2.body.status.now }, - }) - }) - - it('Should respond with 200 on extra request fields', async () => { - const req = await quotaRequest() - // @ts-ignore Intentionally adding an extra field to the request type - req.options.extraField = noString - - const res = await request(app).post(SignerEndpoint.DOMAIN_QUOTA_STATUS).send(req) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: res.body.version, - status: { disabled: false, counter: 0, timer: 0, now: res.body.status.now }, - }) - }) - - it('Should respond with 400 on missing request fields', async () => { - const badRequest = await quotaRequest() - // @ts-ignore Intentionally deleting required field - delete badRequest.domain.version - - const res = await request(app).post(SignerEndpoint.DOMAIN_QUOTA_STATUS).send(badRequest) - - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 on unknown domain', async () => { - // Create a request with an invalid domain identifier. - const unknownRequest = await quotaRequest() - // @ts-ignore UnknownDomain is (intentionally) not a valid domain identifier. - unknownRequest.domain.name = 'UnknownDomain' - - const res = await request(app).post(SignerEndpoint.DOMAIN_QUOTA_STATUS).send(unknownRequest) - - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 on bad encoding', async () => { - const badRequest1 = await quotaRequest() - // @ts-ignore Intentionally not JSON - badRequest1.domain = 'Freddy' - - const res1 = await request(app).post(SignerEndpoint.DOMAIN_QUOTA_STATUS).send(badRequest1) - - expect(res1.status).toBe(400) - expect(res1.body).toStrictEqual({ - success: false, - version: res1.body.version, - error: WarningMessage.INVALID_INPUT, - }) - - const badRequest2 = '' - - const res2 = await request(app).post(SignerEndpoint.DOMAIN_QUOTA_STATUS).send(badRequest2) - - expect(res2.status).toBe(400) - expect(res2.body).toStrictEqual({ - success: false, - version: res2.body.version, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 401 on failed auth', async () => { - // Create a manipulated request, which will have a bad signature. - const badRequest = await quotaRequest() - badRequest.domain.salt = defined('badSalt') - - const res = await request(app).post(SignerEndpoint.DOMAIN_QUOTA_STATUS).send(badRequest) - - expect(res.status).toBe(401) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.UNAUTHENTICATED_USER, - }) - }) - - it('Should respond with 503 on disabled api', async () => { - const configWithApiDisabled: typeof _config = JSON.parse(JSON.stringify(_config)) - configWithApiDisabled.api.domains.enabled = false - const appWithApiDisabled = startSigner(configWithApiDisabled, db, keyProvider) - - const req = await quotaRequest() - - const res = await request(appWithApiDisabled) - .post(SignerEndpoint.DOMAIN_QUOTA_STATUS) - .send(req) - - expect(res.status).toBe(503) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.API_UNAVAILABLE, - }) - }) - - describe('functionality in case of errors', () => { - it('Should respond with 500 on DB getDomainStateRecordOrEmpty failure', async () => { - const req = await quotaRequest() - // Mocking getDomainStateRecord directly but requiring the real version of - // getDomainStateRecordOrEmpty does not easily work, - // which is why we mock the outer call here & use the countAndThrowDBError - // helper to get as close as possible to testing a real error. - const spy = jest - .spyOn( - jest.requireActual('../../src/common/database/wrappers/domain-state'), - 'getDomainStateRecordOrEmpty' - ) - .mockImplementationOnce(() => { - countAndThrowDBError( - new Error(), - rootLogger(_config.serviceName), - ErrorMessage.DATABASE_GET_FAILURE - ) - }) - const res = await request(app).post(SignerEndpoint.DOMAIN_QUOTA_STATUS).send(req) - spy.mockRestore() - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: ErrorMessage.DATABASE_GET_FAILURE, - }) - expect(await getDomainStateRecord(db, req.domain, rootLogger(_config.serviceName))).toBe( - null - ) - }) - }) - - it('Should respond with 500 on signer timeout', async () => { - const testTimeoutMS = 0 - const delay = 200 - - const configWithShortTimeout = JSON.parse(JSON.stringify(_config)) - configWithShortTimeout.timeout = testTimeoutMS - const appWithShortTimeout = startSigner(configWithShortTimeout, db, keyProvider) - - const req = await quotaRequest() - const spy = jest - .spyOn( - jest.requireActual('../../src/common/database/wrappers/domain-state'), - 'getDomainStateRecordOrEmpty' - ) - .mockImplementationOnce(async () => { - await new Promise((resolve) => setTimeout(resolve, testTimeoutMS + delay)) - return createEmptyDomainStateRecord(req.domain) - }) - - const res = await request(appWithShortTimeout) - .post(SignerEndpoint.DOMAIN_QUOTA_STATUS) - .send(req) - - spy.mockRestore() - - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - error: ErrorMessage.TIMEOUT_FROM_SIGNER, - version: expectedVersion, - }) - // Allow time for non-killed processes to finish - await new Promise((resolve) => setTimeout(resolve, delay)) - }) - }) - - describe(`${SignerEndpoint.DOMAIN_SIGN}`, () => { - const expectedEvals: string[] = [ - 'AQAAALSWngdNIqyApv+AGj50OJxj9fSFPjvGlNZ+oAMykmgfVZd0o59MugofDPrBrUm9AFrr2uPXxKwL6PR2Uo3ch2jfOhRBE9amUTQV9U2gV8b1fFy2uFqnaT6ahV/GE956Aa4n8hiyRD36YL62YELtmGnNo4ODMl98ovirR6BoWp0yOhm42vq2SVRh3O69GYmHAd35Q/jYH9cOXnpNyUf1Pw4WmcbsTc+kwVe+226QJYMGtqafMIFR2AGnTiZji5SOAM7TTCDfZWKj+vtvrlFs3nSRI7AKFBzyx6KIyboljHvtBjhA1EGEzrqEJHLLU+iFASY3vqctLoONWcn6t8puaT5g4bmL3WqHiP+pF/0paLrHyQlxt3NJBcgWXv4GWMh7APDNFXpQ9O/skdlBPED433vMj7ZjXnybkdq7LFuMOua5rY8MEuTtoWizBtoErzBnADb5kWQCYgog94pCuYOYxCoK/+cl2DxVVnt0tarkG4mGK2BY+N6cwHhhYppED3GJAP70+R/nrjWhTp2xwKOd/uJByi/9ORHU16USrVsgka+LrGl/fy3P6BEtoK7cu6JfAXcx54Ojo0eUVsD5W6iHfrgllFk3jSgAvWUJ3I3IG8pTPuKX5eO6c9yL4/PVDY4/AAEkyf5vTG5f5dRd9akptsnVz2dmegSTAcj4md0gDugXzLEitduXF5lsqH6BFo9/AWDTgx2JzBSnSr8HSgSWk0ZKni2UIl1F1Eyb+CN45+5TQDiDv1fsl/0tumMikom1AA==', - 'AQAAAFd54JZz5xv1zf7+U8ZpjfLQQ4kZr5jl8R0/nWUqTcQyiO25awk09nh3elLvd6VkAOxnY0oiHASh7uzDJsi/XuDBJrp1oKoZ85eoCP90/RzGTuGDsHEmKtgDk/lAesQvAeGPjrhyjVleFcdnFq+czoT3aoOrYhdAin2lGU6nWAehbJwUyj5uvI+uEfcvk0KGAVbhZzC1L7jjGaLj+3SmhMfP71kqS5DeaSuyzu0byXum548HT1NRoO96icdfDtUBAfSw/FDzGfrFYf6WdUAObehnGt49AMpkVtGaMOsnETPL/nlbMizK4vMas+NlwyqgAZdCQaGX/FixHYpTDJ6NlBvWHwryoSJFse9XVLg5OizlEYh2ASLxFsZKqMCs7c6TAOYhwSWBeIkJCb0PQBsEMk2/vvnTY5RDAcBYW8aJ118IX/hcO9gO+lLathT+CKlDADGMQTVn1wqFapYZ677Gcsb4JDhUOaQjJCYu7JXlcyhLOe3/0AwI1LjI+ClA+TupADL+qQWTxyfcCfBb9XdcD6klzzFNs0aUfEwgjDyBUVbGrMBSQyE8ErDJYII7j1J4AeacepfWb54q2SLFBIIaoQiWhgeh527QOVbDBYQy0KutYBxEdp/RmJ3vIJL/u/PBAB/5gedsqPzRxIH3yTYoHX5U0bOL6XTmZVNaNZ9rnXn58IXiWORNudbwdA4DGZMOAPUtu1ze8sTUE/PsjNzVPNwhwJz8cQbt4tGC3luRUctkx26K+nZgn8GkCQV9AtByAQ==', - 'AQAAADri1djYjhPpolB6aRwD6ptKRz4EGNAYWba0TYfM/TgQxeoSTupAfJLIJdYEWAEdATyCz9VWW7lC1InwUUCq7vARCWClyAoBQ8LdMAi9bYFy4MrEj+urzTmUgmZL1r39AJOhT9H+SuGv1uBET1Hv49aWZReTo0NhK4U6Oh6y8tHou+P3LC155ZZHLRrmcyGDARnOhBs25CHMjrvkLwcLsJNnK7Y0QXO4/6YEVTBBcsN+F/BGLgtP5GaiPdtDXuYEAFhZW+a0pZIlUzaYZaiXFMQ6pJJbCsMJTK+khfWBSAFuVVkG2wIKTGiTqOkw+o8SAeooTBoO0NJJZcpP+jY++zRziX+X7fyixmBlcStbmVU4gwA1kG/4kvEsrIh+kEygAWvxw/2JZcIDZRRAkhHu+uZwSflSwFFW8omtI36t7YmYmOMpXxTHFNdJyh2mMS29AP7dzScfrKa4NObq/UN85PjIvBTR5otWCFrsT0gNSDnEiGO6cXFIHMexyPRTLYSpAVJra/z283B8DjejVN1qyQFRi9upU5M1vxVLJo5y48IDJM8q+ZKDvokwY2icPxewAJZ2OtyGW55weDMTousWVEJoJ9oBiaXCb/ZOROJ8+Oyv8cR4Xbc8AZV3Ec4tusAcAFYoE7YCOwkSj7Beq7B3p16bfFcso8nA3GgGXx16qTCmEeCCS4alWFPE73AHlWknAaetWLlMMZIw6SURpkwSoALXe8DkvelkROc/uFlo2wypEswzLVW/dYpbHrU0U92OAQ==', - ] - const expectedEval = expectedEvals[_config.keystore.keys.domains.latest - 1] - - it('Should respond with 200 on valid request', async () => { - const [req, thresholdPoprfClient] = await signatureRequest() - - const res = await request(app).post(SignerEndpoint.DOMAIN_SIGN).send(req) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: res.body.version, - signature: res.body.signature, - status: { - disabled: false, - counter: 1, - timer: res.body.status.timer, - now: res.body.status.now, - }, - }) - const evaluation = thresholdPoprfClient.unblindPartialResponse( - Buffer.from(res.body.signature, 'base64') - ) - expect(evaluation.toString('base64')).toEqual(expectedEval) - expect(res.get(KEY_VERSION_HEADER)).toEqual(_config.keystore.keys.domains.latest.toString()) - }) - - for (let i = 1; i <= 3; i++) { - it(`Should respond with 200 on valid request with key version header ${i}`, async () => { - const [req, thresholdPoprfClient] = await signatureRequest(undefined, undefined, i) - - const res = await request(app) - .post(SignerEndpoint.DOMAIN_SIGN) - .set(KEY_VERSION_HEADER, i.toString()) - .send(req) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: res.body.version, - signature: res.body.signature, - status: { - disabled: false, - counter: 1, - timer: res.body.status.timer, - now: res.body.status.now, - }, - }) - const evaluation = thresholdPoprfClient.unblindPartialResponse( - Buffer.from(res.body.signature, 'base64') - ) - expect(evaluation.toString('base64')).toEqual(expectedEvals[i - 1]) - expect(res.get(KEY_VERSION_HEADER)).toEqual(i.toString()) - }) - } - - it('Should respond with 200 on repeated valid requests with nonce updated', async () => { - const [req, thresholdPoprfClient] = await signatureRequest() - - const res1 = await request(app).post(SignerEndpoint.DOMAIN_SIGN).send(req) - - expect(res1.status).toBe(200) - expect(res1.body).toStrictEqual({ - success: true, - version: res1.body.version, - signature: res1.body.signature, - status: { - disabled: false, - counter: 1, - timer: res1.body.status.timer, - now: res1.body.status.now, - }, - }) - const eval1 = thresholdPoprfClient.unblindPartialResponse( - Buffer.from(res1.body.signature, 'base64') - ) - expect(eval1.toString('base64')).toEqual(expectedEval) - - // submit identical request with nonce set to 1 - req.options.nonce = defined(1) - // This is how - req.options.signature = noString - req.options.signature = defined( - await wallet.signTypedData(walletAddress, domainRestrictedSignatureRequestEIP712(req)) - ) - const res2 = await request(app).post(SignerEndpoint.DOMAIN_SIGN).send(req) - expect(res2.status).toBe(200) - expect(res2.body).toStrictEqual({ - success: true, - version: res2.body.version, - signature: res2.body.signature, - status: { - disabled: false, - counter: 2, - timer: res2.body.status.timer, - now: res2.body.status.now, - }, - }) - const eval2 = thresholdPoprfClient.unblindPartialResponse( - Buffer.from(res2.body.signature, 'base64') - ) - expect(eval2).toEqual(eval1) - }) - - it('Should respond with 200 if nonce > domainState', async () => { - const [req, thresholdPoprfClient] = await signatureRequest(undefined, 2) - const res = await request(app).post(SignerEndpoint.DOMAIN_SIGN).send(req) - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: res.body.version, - signature: res.body.signature, - status: { - disabled: false, - counter: 1, // counter gets incremented, not set to nonce value - timer: res.body.status.timer, - now: res.body.status.now, - }, - }) - const evaluation = thresholdPoprfClient.unblindPartialResponse( - Buffer.from(res.body.signature, 'base64') - ) - expect(evaluation.toString('base64')).toEqual(expectedEval) - }) - - it('Should respond with 200 on extra request fields', async () => { - const [req, thresholdPoprfClient] = await signatureRequest() - // @ts-ignore Intentionally adding an extra field to the request type - req.options.extraField = noString - - const res = await request(app).post(SignerEndpoint.DOMAIN_SIGN).send(req) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: res.body.version, - signature: res.body.signature, - status: { - disabled: false, - counter: 1, - timer: res.body.status.timer, - now: res.body.status.now, - }, - }) - const evaluation = thresholdPoprfClient.unblindPartialResponse( - Buffer.from(res.body.signature, 'base64') - ) - expect(evaluation.toString('base64')).toEqual(expectedEval) - }) - - it('Should respond with 400 on missing request fields', async () => { - const [badRequest, _] = await signatureRequest() - // @ts-ignore Intentionally deleting required field - delete badRequest.domain.version - - const res = await request(app).post(SignerEndpoint.DOMAIN_SIGN).send(badRequest) - - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 on unknown domain', async () => { - // Create a request with an invalid domain identifier. - const [unknownRequest, _] = await signatureRequest() - // @ts-ignore UnknownDomain is (intentionally) not a valid domain identifier. - unknownRequest.domain.name = 'UnknownDomain' - - const res = await request(app).post(SignerEndpoint.DOMAIN_SIGN).send(unknownRequest) - - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 on bad encoding', async () => { - const [badRequest1, _] = await signatureRequest() - // @ts-ignore Intentionally not JSON - badRequest1.domain = 'Freddy' - - const res1 = await request(app).post(SignerEndpoint.DOMAIN_SIGN).send(badRequest1) - - expect(res1.status).toBe(400) - expect(res1.body).toStrictEqual({ - success: false, - version: res1.body.version, - error: WarningMessage.INVALID_INPUT, - }) - - const badRequest2 = '' - - const res2 = await request(app).post(SignerEndpoint.DOMAIN_SIGN).send(badRequest2) - - expect(res2.status).toBe(400) - expect(res2.body).toStrictEqual({ - success: false, - version: res2.body.version, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 on invalid key version', async () => { - const [badRequest, _] = await signatureRequest() - - const res = await request(app) - .post(SignerEndpoint.DOMAIN_SIGN) - .set(KEY_VERSION_HEADER, 'a') - .send(badRequest) - - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.INVALID_KEY_VERSION_REQUEST, - }) - }) - - it('Should respond with 401 on failed auth', async () => { - // Create a manipulated request, which will have a bad signature. - const [badRequest, _] = await signatureRequest() - badRequest.domain.salt = defined('badSalt') - - const res = await request(app).post(SignerEndpoint.DOMAIN_SIGN).send(badRequest) - - expect(res.status).toBe(401) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.UNAUTHENTICATED_USER, - }) - }) - - it('Should respond 401 on invalid nonce', async () => { - // Request must be sent first since nonce check is >= 0 - const [req1, _] = await signatureRequest() - const res1 = await request(app).post(SignerEndpoint.DOMAIN_SIGN).send(req1) - expect(res1.status).toBe(200) - expect(res1.body).toStrictEqual({ - success: true, - version: res1.body.version, - signature: res1.body.signature, - status: { - disabled: false, - counter: 1, - timer: res1.body.status.timer, - now: res1.body.status.now, - }, - }) - const res2 = await request(app).post(SignerEndpoint.DOMAIN_SIGN).send(req1) - expect(res2.status).toBe(401) - - expect(res2.body).toStrictEqual({ - success: false, - version: res2.body.version, - error: WarningMessage.INVALID_NONCE, - status: { - disabled: false, - counter: 1, - timer: res1.body.status.timer, // Timer should be same as from first request - now: res2.body.status.now, - }, - }) - }) - - it('Should respond with 429 on out of quota', async () => { - const noQuotaDomain = authenticatedDomain([ - { delay: 0, resetTimer: noBool, batchSize: defined(0), repetitions: defined(0) }, - ]) - const [badRequest, _] = await signatureRequest(noQuotaDomain) - - const res = await request(app).post(SignerEndpoint.DOMAIN_SIGN).send(badRequest) - - expect(res.status).toBe(429) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.EXCEEDED_QUOTA, - status: { - disabled: false, - counter: 0, - timer: 0, - now: res.body.status.now, - }, - }) - }) - - it('Should respond with 429 on request too early', async () => { - // This domain won't accept requests until ~10 seconds after test execution - const noQuotaDomain = authenticatedDomain([ - { - delay: Math.floor(Date.now() / 1000) + 10, - resetTimer: noBool, - batchSize: defined(2), - repetitions: defined(1), - }, - ]) - const [badRequest, _] = await signatureRequest(noQuotaDomain) - - const res = await request(app).post(SignerEndpoint.DOMAIN_SIGN).send(badRequest) - - expect(res.status).toBe(429) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.EXCEEDED_QUOTA, - status: { - disabled: false, - counter: 0, - timer: 0, - now: res.body.status.now, - }, - }) - }) - - it('Should respond with 500 on unsupported key version', async () => { - const [req, _] = await signatureRequest(undefined, undefined, 4) - - const res = await request(app) - .post(SignerEndpoint.DOMAIN_SIGN) - .set(KEY_VERSION_HEADER, '4') - .send(req) - - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.INVALID_KEY_VERSION_REQUEST, - }) - }) - - it('Should respond with 503 on disabled api', async () => { - const configWithApiDisabled: typeof _config = JSON.parse(JSON.stringify(_config)) - configWithApiDisabled.api.domains.enabled = false - const appWithApiDisabled = startSigner(configWithApiDisabled, db, keyProvider) - - const [req, _] = await signatureRequest() - - const res = await request(appWithApiDisabled).post(SignerEndpoint.DOMAIN_SIGN).send(req) - - expect(res.status).toBe(503) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.API_UNAVAILABLE, - }) - }) - - describe('functionality in case of errors', () => { - it('Should respond with 500 on DB getDomainStateRecord query failure', async () => { - const [req, _] = await signatureRequest() - // Mocking getDomainStateRecord directly but requiring the real version of - // getDomainStateRecordOrEmpty does not easily work, - // which is why we mock the outer call here & use the countAndThrowDBError - // helper to get as close as possible to testing a real error. - const spy = jest - .spyOn( - jest.requireActual('../../src/common/database/wrappers/domain-state'), - 'getDomainStateRecordOrEmpty' - ) - .mockImplementationOnce(() => { - countAndThrowDBError( - new Error(), - rootLogger(_config.serviceName), - ErrorMessage.DATABASE_GET_FAILURE - ) - }) - const res = await request(app).post(SignerEndpoint.DOMAIN_SIGN).send(req) - spy.mockRestore() - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: ErrorMessage.DATABASE_GET_FAILURE, - }) - expect(await getDomainStateRecord(db, req.domain, rootLogger(_config.serviceName))).toBe( - null - ) - }) - - it('Should respond with 500 on DB updateDomainStateRecord failure', async () => { - const [req, _] = await signatureRequest() - // Same as above (re: getDomainStateRecord, but with insertDomainStateRecord) - // which is why we mock the outer call here & use the countAndThrowDBError - // helper to get as close as possible to testing a real error. - const spy = jest - .spyOn( - jest.requireActual('../../src/common/database/wrappers/domain-state'), - 'updateDomainStateRecord' - ) - .mockImplementationOnce(() => { - countAndThrowDBError( - new Error(), - rootLogger(_config.serviceName), - ErrorMessage.DATABASE_UPDATE_FAILURE - ) - }) - const res = await request(app).post(SignerEndpoint.DOMAIN_SIGN).send(req) - spy.mockRestore() - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: ErrorMessage.DATABASE_UPDATE_FAILURE, - }) - expect(await getDomainStateRecord(db, req.domain, rootLogger(_config.serviceName))).toBe( - null - ) - }) - - it('Should respond with 500 on signer timeout', async () => { - const [req, _] = await signatureRequest() - const testTimeoutMS = 0 - const delay = 200 - - const spy = jest - .spyOn( - jest.requireActual('../../src/common/database/wrappers/domain-state'), - 'getDomainStateRecordOrEmpty' - ) - .mockImplementationOnce(async () => { - await new Promise((resolve) => setTimeout(resolve, testTimeoutMS + delay)) - return createEmptyDomainStateRecord(req.domain) - }) - - const configWithShortTimeout = JSON.parse(JSON.stringify(_config)) - configWithShortTimeout.timeout = testTimeoutMS - const appWithShortTimeout = startSigner(configWithShortTimeout, db, keyProvider) - - const res = await request(appWithShortTimeout).post(SignerEndpoint.DOMAIN_SIGN).send(req) - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - error: ErrorMessage.TIMEOUT_FROM_SIGNER, - version: expectedVersion, - }) - spy.mockRestore() - // Allow time for non-killed processes to finish - await new Promise((resolve) => setTimeout(resolve, delay)) - // Check that DB state was not updated on timeout - expect(await getDomainStateRecord(db, req.domain, rootLogger(_config.serviceName))).toBe( - null - ) - }) - }) - }) -}) diff --git a/packages/phone-number-privacy/signer/test/integration/legacypnp.test.ts b/packages/phone-number-privacy/signer/test/integration/legacypnp.test.ts deleted file mode 100644 index e72bc1f7a7d..00000000000 --- a/packages/phone-number-privacy/signer/test/integration/legacypnp.test.ts +++ /dev/null @@ -1,1795 +0,0 @@ -import { newKit, StableToken } from '@celo/contractkit' -import { - AuthenticationMethod, - ErrorMessage, - KEY_VERSION_HEADER, - PhoneNumberPrivacyRequest, - PnpQuotaResponseFailure, - PnpQuotaResponseSuccess, - rootLogger, - SignerEndpoint, - SignMessageResponseFailure, - SignMessageResponseSuccess, - TestUtils, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import { - AttestationsStatus, - createMockOdisPayments, - getPnpSignRequest, -} from '@celo/phone-number-privacy-common/lib/test/utils' -import BigNumber from 'bignumber.js' -import { Knex } from 'knex' -import request from 'supertest' -import { initDatabase } from '../../src/common/database/database' -import { ACCOUNTS_TABLE } from '../../src/common/database/models/account' -import { REQUESTS_TABLE } from '../../src/common/database/models/request' -import { countAndThrowDBError } from '../../src/common/database/utils' -import { - getPerformedQueryCount, - incrementQueryCount, -} from '../../src/common/database/wrappers/account' -import { getRequestExists } from '../../src/common/database/wrappers/request' -import { initKeyProvider } from '../../src/common/key-management/key-provider' -import { KeyProvider } from '../../src/common/key-management/key-provider-base' -import { config, getSignerVersion, SupportedDatabase, SupportedKeystore } from '../../src/config' -import { startSigner } from '../../src/server' - -const { - ContractRetrieval, - createMockContractKit, - createMockAccounts, - createMockToken, - createMockWeb3, - getLegacyPnpQuotaRequest, - getPnpRequestAuthorization, - createMockAttestation, - getLegacyPnpSignRequest, -} = TestUtils.Utils -const { - IDENTIFIER, - PRIVATE_KEY1, - ACCOUNT_ADDRESS1, - mockAccount, - BLINDED_PHONE_NUMBER, - DEK_PRIVATE_KEY, - DEK_PUBLIC_KEY, -} = TestUtils.Values - -jest.setTimeout(20000) - -const testBlockNumber = 1000000 - -const mockBalanceOfCUSD = jest.fn() -const mockBalanceOfCEUR = jest.fn() -const mockBalanceOfCELO = jest.fn() -const mockGetVerifiedStatus = jest.fn() -const mockGetWalletAddress = jest.fn() -const mockGetDataEncryptionKey = jest.fn() -const mockOdisPaymentsTotalPaidCUSD = jest.fn() - -const mockContractKit = createMockContractKit( - { - // getWalletAddress stays constant across all old query-quota.test.ts unit tests - [ContractRetrieval.getAccounts]: createMockAccounts( - mockGetWalletAddress, - mockGetDataEncryptionKey - ), - [ContractRetrieval.getStableToken]: jest.fn(), - [ContractRetrieval.getGoldToken]: createMockToken(mockBalanceOfCELO), - [ContractRetrieval.getAttestations]: createMockAttestation(mockGetVerifiedStatus), - [ContractRetrieval.getOdisPayments]: createMockOdisPayments(mockOdisPaymentsTotalPaidCUSD), - }, - createMockWeb3(5, testBlockNumber) -) - -// Necessary for distinguishing between mocked stable tokens -mockContractKit.contracts[ContractRetrieval.getStableToken] = jest.fn( - (stableToken: StableToken) => { - switch (stableToken) { - case StableToken.cUSD: - return createMockToken(mockBalanceOfCUSD) - case StableToken.cEUR: - return createMockToken(mockBalanceOfCEUR) - default: - return createMockToken(jest.fn().mockReturnValue(new BigNumber(0))) - } - } -) - -jest.mock('@celo/contractkit', () => ({ - ...jest.requireActual('@celo/contractkit'), - newKit: jest.fn().mockImplementation(() => mockContractKit), -})) - -// Indexes correspond to keyVersion - 1 -const expectedSignatures: string[] = [ - 'MAAAAAAAAACEVdw1ULDwAiTcZuPnZxHHh38PNa+/g997JgV10QnEq9yeuLxbM9l7vk0EAicV7IAAAAAA', - 'MAAAAAAAAAAmUJY0s9p7fMfs7GIoSiGJoObAN8ZpA7kRqeC9j/Q23TBrG3Jtxc8xWibhNVZhbYEAAAAA', - 'MAAAAAAAAAC4aBbzhHvt6l/b+8F7cILmWxZZ5Q7S6R4RZ/IgZR7Pfb9B1Wg9fsDybgxVTSv5BYEAAAAA', -] - -describe('legacyPNP', () => { - let keyProvider: KeyProvider - let app: any - let db: Knex - - const expectedVersion = getSignerVersion() - - // create deep copy - const _config: typeof config = JSON.parse(JSON.stringify(config)) - _config.db.type = SupportedDatabase.Sqlite - _config.keystore.type = SupportedKeystore.MOCK_SECRET_MANAGER - _config.api.legacyPhoneNumberPrivacy.enabled = true - - const expectedSignature = expectedSignatures[_config.keystore.keys.phoneNumberPrivacy.latest - 1] - - beforeAll(async () => { - keyProvider = await initKeyProvider(_config) - }) - - beforeEach(async () => { - // Create a new in-memory database for each test. - db = await initDatabase(_config) - app = startSigner(_config, db, keyProvider, newKit('dummyKit')) - }) - - afterEach(async () => { - // Close and destroy the in-memory database. - // Note: If tests start to be too slow, this could be replaced with more complicated logic to - // reset the database state without destroying and recreating it for each test. - await db?.destroy() - }) - - describe(`${SignerEndpoint.STATUS}`, () => { - it('Should return 200 and correct version', async () => { - const res = await request(app).get(SignerEndpoint.STATUS) - expect(res.status).toBe(200) - expect(res.body.version).toBe(expectedVersion) - }) - }) - - const zeroBalance = new BigNumber(0) - const twentyCents = new BigNumber(200000000000000000) - - type legacyPnpQuotaCalculationTestCase = { - it: string - account: string - performedQueryCount: number - transactionCount: number - balanceCUSD: BigNumber - balanceCEUR: BigNumber - balanceCELO: BigNumber - isVerified: boolean - identifier: string | undefined - expectedPerformedQueryCount: number - expectedTotalQuota: number - } // To be re-used against both the signature and quota endpoints - const quotaCalculationTestCases: legacyPnpQuotaCalculationTestCase[] = [ - { - it: 'should calculate correct quota for verified account', - account: ACCOUNT_ADDRESS1, - performedQueryCount: 2, - transactionCount: 5, - balanceCUSD: zeroBalance, - balanceCEUR: zeroBalance, - balanceCELO: zeroBalance, - isVerified: true, - identifier: IDENTIFIER, - expectedPerformedQueryCount: 2, - expectedTotalQuota: 60, - }, - { - it: 'should calculate correct quota for unverified account with no transactions or balance', - account: ACCOUNT_ADDRESS1, - performedQueryCount: 0, - transactionCount: 0, - balanceCUSD: zeroBalance, - balanceCEUR: zeroBalance, - balanceCELO: zeroBalance, - isVerified: false, - identifier: IDENTIFIER, - expectedPerformedQueryCount: 0, - expectedTotalQuota: 0, - }, - { - it: 'should calculate correct quota for unverified account with balance but no transactions', - account: ACCOUNT_ADDRESS1, - performedQueryCount: 1, - transactionCount: 0, - balanceCUSD: twentyCents, - balanceCEUR: twentyCents, - balanceCELO: twentyCents, - isVerified: false, - identifier: IDENTIFIER, - expectedPerformedQueryCount: 1, - expectedTotalQuota: 10, - }, - { - it: 'should calculate correct quota for verified account with many txs and balance', - account: ACCOUNT_ADDRESS1, - performedQueryCount: 10, - transactionCount: 100, - balanceCUSD: twentyCents, - balanceCEUR: twentyCents, - balanceCELO: twentyCents, - isVerified: true, - identifier: IDENTIFIER, - expectedPerformedQueryCount: 10, - expectedTotalQuota: 440, - }, - { - it: 'should calculate correct quota for unverified account with many txs and balance', - account: ACCOUNT_ADDRESS1, - performedQueryCount: 0, - transactionCount: 100, - balanceCUSD: twentyCents, - balanceCEUR: twentyCents, - balanceCELO: twentyCents, - isVerified: false, - identifier: IDENTIFIER, - expectedPerformedQueryCount: 0, - expectedTotalQuota: 410, - }, - { - it: 'should calculate correct quota for unverified account without any balance (with txs)', - account: ACCOUNT_ADDRESS1, - performedQueryCount: 0, - transactionCount: 100, - balanceCUSD: zeroBalance, - balanceCEUR: zeroBalance, - balanceCELO: zeroBalance, - isVerified: false, - identifier: IDENTIFIER, - expectedPerformedQueryCount: 0, - expectedTotalQuota: 0, - }, - { - it: 'should calculate correct quota for unverified account with only cUSD balance (no txs)', - account: ACCOUNT_ADDRESS1, - performedQueryCount: 1, - transactionCount: 0, - balanceCUSD: twentyCents, - balanceCEUR: zeroBalance, - balanceCELO: zeroBalance, - isVerified: false, - identifier: IDENTIFIER, - expectedPerformedQueryCount: 1, - expectedTotalQuota: 10, - }, - { - it: 'should calculate correct quota for unverified account with only cEUR balance (no txs)', - account: ACCOUNT_ADDRESS1, - performedQueryCount: 1, - transactionCount: 0, - balanceCUSD: zeroBalance, - balanceCEUR: twentyCents, - balanceCELO: zeroBalance, - isVerified: false, - identifier: IDENTIFIER, - expectedPerformedQueryCount: 1, - expectedTotalQuota: 10, - }, - { - it: 'should calculate correct quota for unverified account with only CELO balance (no txs)', - account: ACCOUNT_ADDRESS1, - performedQueryCount: 1, - transactionCount: 0, - balanceCUSD: zeroBalance, - balanceCEUR: zeroBalance, - balanceCELO: twentyCents, - isVerified: false, - identifier: IDENTIFIER, - expectedPerformedQueryCount: 1, - expectedTotalQuota: 10, - }, - { - it: 'should calculate correct quota for account with min balance when no phone number hash is provided', - account: ACCOUNT_ADDRESS1, - performedQueryCount: 1, - transactionCount: 0, - balanceCUSD: twentyCents, - balanceCEUR: twentyCents, - balanceCELO: twentyCents, - isVerified: false, - identifier: IDENTIFIER, - expectedPerformedQueryCount: 1, - expectedTotalQuota: 10, - }, - ] - - const prepMocks = async ( - account: string, - performedQueryCount: number, - transactionCount: number, - isVerified: boolean, - balanceCUSD: BigNumber, - balanceCEUR: BigNumber, - balanceCELO: BigNumber, - dekPubKey: string = DEK_PUBLIC_KEY, - walletAddress: string = mockAccount - ) => { - ;[ - mockContractKit.connection.getTransactionCount, - mockGetVerifiedStatus, - mockBalanceOfCUSD, - mockBalanceOfCEUR, - mockBalanceOfCELO, - mockGetWalletAddress, - mockGetDataEncryptionKey, - ].forEach((mockFn) => mockFn.mockReset()) - - await db.transaction(async (trx) => { - for (let i = 0; i < performedQueryCount; i++) { - await incrementQueryCount( - db, - ACCOUNTS_TABLE.LEGACY, - account, - rootLogger(_config.serviceName), - trx - ) - } - }) - - mockContractKit.connection.getTransactionCount.mockReturnValue(transactionCount) - mockGetVerifiedStatus.mockReturnValue( - // only the isVerified value below matters - { isVerified, completed: 1, total: 1, numAttestationsRemaining: 1 } - ) - mockBalanceOfCUSD.mockReturnValue(balanceCUSD) - mockBalanceOfCEUR.mockReturnValue(balanceCEUR) - mockBalanceOfCELO.mockReturnValue(balanceCELO) - mockGetWalletAddress.mockReturnValue(walletAddress) - mockGetDataEncryptionKey.mockReturnValue(dekPubKey) - } - - const sendRequest = async ( - req: PhoneNumberPrivacyRequest, - authorization: string, - endpoint: SignerEndpoint, - keyVersionHeader?: string, - signerApp: any = app - ) => { - const _req = request(signerApp).post(endpoint).set('Authorization', authorization) - - if (keyVersionHeader !== undefined) { - _req.set(KEY_VERSION_HEADER, keyVersionHeader) - } - - return _req.send(req) - } - - describe(`${SignerEndpoint.LEGACY_PNP_QUOTA}`, () => { - describe('quota calculation logic', () => { - const runLegacyQuotaTestCase = async (testCase: legacyPnpQuotaCalculationTestCase) => { - await prepMocks( - testCase.account, - testCase.performedQueryCount, - testCase.transactionCount, - testCase.isVerified, - testCase.balanceCUSD, - testCase.balanceCEUR, - testCase.balanceCELO - ) - - const req = getLegacyPnpQuotaRequest( - testCase.account, - AuthenticationMethod.WALLET_KEY, - testCase.identifier - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.LEGACY_PNP_QUOTA) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - performedQueryCount: testCase.expectedPerformedQueryCount, - totalQuota: testCase.expectedTotalQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - } - - quotaCalculationTestCases.forEach((testCase) => { - it(testCase.it, async () => { - await runLegacyQuotaTestCase(testCase) - }) - }) - }) - - describe('endpoint functionality', () => { - // Use values from 'unverified account with no transactions' logic test case - const performedQueryCount = 1 - const expectedQuota = 10 - - beforeEach(async () => { - await prepMocks( - ACCOUNT_ADDRESS1, - performedQueryCount, - 0, - false, - twentyCents, - twentyCents, - twentyCents - ) - }) - - it('Should respond with 200 on valid request', async () => { - const req = getLegacyPnpQuotaRequest(ACCOUNT_ADDRESS1, IDENTIFIER) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - - const res = await sendRequest(req, authorization, SignerEndpoint.LEGACY_PNP_QUOTA) - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: res.body.version, - performedQueryCount: performedQueryCount, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - }) - - it('Should respond with 200 on valid request when authenticated with DEK', async () => { - const req = getLegacyPnpQuotaRequest( - ACCOUNT_ADDRESS1, - AuthenticationMethod.ENCRYPTION_KEY, - IDENTIFIER - ) - const authorization = getPnpRequestAuthorization(req, DEK_PRIVATE_KEY) - - const res = await sendRequest(req, authorization, SignerEndpoint.LEGACY_PNP_QUOTA) - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: res.body.version, - performedQueryCount: performedQueryCount, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - }) - - it('Should respond with 200 on repeated valid requests', async () => { - const req = getLegacyPnpQuotaRequest(ACCOUNT_ADDRESS1, IDENTIFIER) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - - const res1 = await sendRequest(req, authorization, SignerEndpoint.LEGACY_PNP_QUOTA) - expect(res1.status).toBe(200) - expect(res1.body).toStrictEqual({ - success: true, - version: res1.body.version, - performedQueryCount: performedQueryCount, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - const res2 = await sendRequest(req, authorization, SignerEndpoint.LEGACY_PNP_QUOTA) - expect(res2.status).toBe(200) - expect(res2.body).toStrictEqual(res1.body) - }) - - it('Should respond with 200 on extra request fields', async () => { - const req = getLegacyPnpQuotaRequest(ACCOUNT_ADDRESS1, IDENTIFIER) - // @ts-ignore Intentionally adding an extra field to the request type - req.extraField = 'dummyString' - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.LEGACY_PNP_QUOTA) - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - performedQueryCount: performedQueryCount, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - }) - - it('Should respond with 200 if performedQueryCount is greater than totalQuota', async () => { - const expectedRemainingQuota = expectedQuota - performedQueryCount - await db.transaction(async (trx) => { - for (let i = 0; i <= expectedRemainingQuota; i++) { - await incrementQueryCount( - db, - ACCOUNTS_TABLE.LEGACY, - ACCOUNT_ADDRESS1, - rootLogger(_config.serviceName), - trx - ) - } - }) - const req = getLegacyPnpQuotaRequest(ACCOUNT_ADDRESS1) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.LEGACY_PNP_QUOTA) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: res.body.version, - performedQueryCount: expectedQuota + 1, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - }) - - it('Should respond with 400 on missing request fields', async () => { - const badRequest = getLegacyPnpQuotaRequest(ACCOUNT_ADDRESS1, IDENTIFIER) - // @ts-ignore Intentionally deleting required field - delete badRequest.account - const authorization = getPnpRequestAuthorization(badRequest, PRIVATE_KEY1) - const res = await sendRequest(badRequest, authorization, SignerEndpoint.LEGACY_PNP_QUOTA) - - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 401 on failed WALLET_KEY auth', async () => { - const badRequest = getLegacyPnpQuotaRequest( - ACCOUNT_ADDRESS1, - AuthenticationMethod.WALLET_KEY, - IDENTIFIER - ) - const differentPk = '0x00000000000000000000000000000000000000000000000000000000ddddbbbb' - const authorization = getPnpRequestAuthorization(badRequest, differentPk) - const res = await sendRequest(badRequest, authorization, SignerEndpoint.LEGACY_PNP_QUOTA) - - expect(res.status).toBe(401) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.UNAUTHENTICATED_USER, - }) - }) - - it('Should respond with 401 on failed DEK auth', async () => { - const badRequest = getLegacyPnpQuotaRequest( - ACCOUNT_ADDRESS1, - AuthenticationMethod.ENCRYPTION_KEY, - IDENTIFIER - ) - const differentPk = '0x00000000000000000000000000000000000000000000000000000000ddddbbbb' - const authorization = getPnpRequestAuthorization(badRequest, differentPk) - const res = await sendRequest(badRequest, authorization, SignerEndpoint.LEGACY_PNP_QUOTA) - - expect(res.status).toBe(401) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.UNAUTHENTICATED_USER, - }) - }) - - it('Should respond with 503 on disabled api', async () => { - const configWithApiDisabled: typeof _config = JSON.parse(JSON.stringify(_config)) - configWithApiDisabled.api.legacyPhoneNumberPrivacy.enabled = false - const appWithApiDisabled = startSigner( - configWithApiDisabled, - db, - keyProvider, - newKit('dummyKit') - ) - const req = getLegacyPnpQuotaRequest(ACCOUNT_ADDRESS1, IDENTIFIER) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest( - req, - authorization, - SignerEndpoint.LEGACY_PNP_QUOTA, - undefined, - appWithApiDisabled - ) - expect.assertions(2) - expect(res.status).toBe(503) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.API_UNAVAILABLE, - }) - }) - - describe('functionality in case of errors', () => { - it('Should respond with 200 on failure to fetch DEK when shouldFailOpen is true', async () => { - const configWithFailOpenEnabled: typeof _config = JSON.parse(JSON.stringify(_config)) - configWithFailOpenEnabled.api.legacyPhoneNumberPrivacy.shouldFailOpen = true - const appWithFailOpenEnabled = startSigner( - configWithFailOpenEnabled, - db, - keyProvider, - newKit('dummyKit') - ) - mockGetDataEncryptionKey.mockImplementation(() => { - throw new Error() - }) - - const req = getLegacyPnpQuotaRequest( - ACCOUNT_ADDRESS1, - AuthenticationMethod.ENCRYPTION_KEY, - IDENTIFIER - ) - - // NOT the dek private key, so authentication would fail if getDataEncryptionKey succeeded - const differentPk = '0x00000000000000000000000000000000000000000000000000000000ddddbbbb' - const authorization = getPnpRequestAuthorization(req, differentPk) - const res = await sendRequest( - req, - authorization, - SignerEndpoint.LEGACY_PNP_QUOTA, - '1', - appWithFailOpenEnabled - ) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: res.body.version, - performedQueryCount: performedQueryCount, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [ErrorMessage.FAILURE_TO_GET_DEK, ErrorMessage.FAILING_OPEN], - }) - }) - - it('Should respond with 500 on DB performedQueryCount query failure', async () => { - const spy = jest - .spyOn( - jest.requireActual('../../src/common/database/wrappers/account'), - 'getPerformedQueryCount' - ) - .mockRejectedValueOnce(new Error()) - - const req = getLegacyPnpQuotaRequest(ACCOUNT_ADDRESS1, IDENTIFIER) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.LEGACY_PNP_QUOTA) - - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: ErrorMessage.FAILURE_TO_GET_PERFORMED_QUERY_COUNT, - }) - - spy.mockRestore() - }) - - it('Should respond with 500 on blockchain totalQuota query failure', async () => { - mockContractKit.connection.getTransactionCount.mockRejectedValue(new Error()) - - const req = getLegacyPnpQuotaRequest(ACCOUNT_ADDRESS1, IDENTIFIER) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.LEGACY_PNP_QUOTA) - - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: ErrorMessage.FAILURE_TO_GET_TOTAL_QUOTA, - }) - }) - - it('Should respond with 500 on signer timeout', async () => { - const testTimeoutMS = 0 - const delay = 100 - const spy = jest - .spyOn( - jest.requireActual('../../src/common/database/wrappers/account'), - 'getPerformedQueryCount' - ) - .mockImplementation(async () => { - await new Promise((resolve) => setTimeout(resolve, testTimeoutMS + delay)) - return expectedQuota - }) - - const configWithShortTimeout = JSON.parse(JSON.stringify(_config)) - configWithShortTimeout.timeout = testTimeoutMS - const appWithShortTimeout = startSigner( - configWithShortTimeout, - db, - keyProvider, - newKit('dummyKit') - ) - const req = getLegacyPnpQuotaRequest(ACCOUNT_ADDRESS1) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest( - req, - authorization, - SignerEndpoint.LEGACY_PNP_QUOTA, - undefined, - appWithShortTimeout - ) - // Ensure that this is restored before test can fail on assertions - // to prevent failures in other tests - spy.mockRestore() - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - error: ErrorMessage.TIMEOUT_FROM_SIGNER, - version: expectedVersion, - }) - // Allow time for non-killed processes to finish - await new Promise((resolve) => setTimeout(resolve, delay)) - }) - }) - }) - }) - - describe(`${SignerEndpoint.LEGACY_PNP_SIGN}`, () => { - describe('quota calculation logic', () => { - const runLegacyPnpSignQuotaTestCase = async (testCase: legacyPnpQuotaCalculationTestCase) => { - await prepMocks( - testCase.account, - testCase.performedQueryCount, - testCase.transactionCount, - testCase.isVerified, - testCase.balanceCUSD, - testCase.balanceCEUR, - testCase.balanceCELO - ) - - const req = getLegacyPnpSignRequest( - testCase.account, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY, - testCase.identifier - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.LEGACY_PNP_SIGN) - - const { expectedPerformedQueryCount, expectedTotalQuota } = testCase - const shouldSucceed = expectedPerformedQueryCount < expectedTotalQuota - - if (shouldSucceed) { - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: expectedPerformedQueryCount + 1, // incremented for signature request - totalQuota: expectedTotalQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - } else { - expect(res.status).toBe(403) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - performedQueryCount: expectedPerformedQueryCount, - totalQuota: expectedTotalQuota, - blockNumber: testBlockNumber, - error: WarningMessage.EXCEEDED_QUOTA, - }) - } - } - - quotaCalculationTestCases.forEach((testCase) => { - it(testCase.it, async () => { - await runLegacyPnpSignQuotaTestCase(testCase) - }) - }) - }) - - describe('endpoint functionality', () => { - // Use values from 'unverified account with balance but no transactions' logic test case - const performedQueryCount = 1 - const expectedQuota = 10 - - beforeEach(async () => { - await prepMocks( - ACCOUNT_ADDRESS1, - performedQueryCount, - 0, - false, - twentyCents, - twentyCents, - twentyCents - ) - }) - - it('Should respond with 200 on valid request', async () => { - const req = getLegacyPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY, - IDENTIFIER - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.LEGACY_PNP_SIGN) - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: performedQueryCount + 1, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - expect(res.get(KEY_VERSION_HEADER)).toEqual( - _config.keystore.keys.phoneNumberPrivacy.latest.toString() - ) - }) - - it('Should respond with 200 on valid request when authenticated with DEK', async () => { - const req = getLegacyPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.ENCRYPTION_KEY, - IDENTIFIER - ) - const authorization = getPnpRequestAuthorization(req, DEK_PRIVATE_KEY) - const res = await sendRequest(req, authorization, SignerEndpoint.LEGACY_PNP_SIGN) - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: performedQueryCount + 1, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - }) - - for (let i = 1; i <= 3; i++) { - it(`Should respond with 200 on valid request with key version header ${i}`, async () => { - const req = getLegacyPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY, - IDENTIFIER - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest( - req, - authorization, - SignerEndpoint.LEGACY_PNP_SIGN, - i.toString() - ) - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignatures[i - 1], - performedQueryCount: performedQueryCount + 1, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - expect(res.get(KEY_VERSION_HEADER)).toEqual(i.toString()) - }) - } - - it('Should respond with 200 and warning on repeated valid requests', async () => { - const req = getLegacyPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY, - IDENTIFIER - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res1 = await sendRequest(req, authorization, SignerEndpoint.LEGACY_PNP_SIGN) - expect(res1.status).toBe(200) - expect(res1.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: performedQueryCount + 1, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - const res2 = await sendRequest(req, authorization, SignerEndpoint.LEGACY_PNP_SIGN) - expect(res2.status).toBe(200) - res1.body.warnings.push(WarningMessage.DUPLICATE_REQUEST_TO_GET_PARTIAL_SIG) - expect(res2.body).toStrictEqual(res1.body) - }) - - it('Should respond with 200 on extra request fields', async () => { - const req = getLegacyPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY, - IDENTIFIER - ) - // @ts-ignore Intentionally adding an extra field to the request type - req.extraField = 'dummyString' - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.LEGACY_PNP_SIGN) - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: performedQueryCount + 1, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - }) - - it('Should respond with 400 on missing request fields', async () => { - const badRequest = getLegacyPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY, - IDENTIFIER - ) - // @ts-ignore Intentionally deleting required field - delete badRequest.account - const authorization = getPnpRequestAuthorization(badRequest, PRIVATE_KEY1) - const res = await sendRequest(badRequest, authorization, SignerEndpoint.LEGACY_PNP_SIGN) - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 on invalid key version', async () => { - const badRequest = getLegacyPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY, - IDENTIFIER - ) - const authorization = getPnpRequestAuthorization(badRequest, PRIVATE_KEY1) - const res = await sendRequest( - badRequest, - authorization, - SignerEndpoint.LEGACY_PNP_SIGN, - 'a' - ) - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_KEY_VERSION_REQUEST, - }) - }) - - it('Should respond with 400 on invalid identifier', async () => { - const badRequest = getLegacyPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY, - '+1234567890' - ) - const authorization = getPnpRequestAuthorization(badRequest, PRIVATE_KEY1) - const res = await sendRequest(badRequest, authorization, SignerEndpoint.LEGACY_PNP_SIGN) - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 on invalid blinded message', async () => { - const badRequest = getLegacyPnpSignRequest( - ACCOUNT_ADDRESS1, - '+1234567890', - AuthenticationMethod.WALLET_KEY, - IDENTIFIER - ) - const authorization = getPnpRequestAuthorization(badRequest, PRIVATE_KEY1) - const res = await sendRequest(badRequest, authorization, SignerEndpoint.LEGACY_PNP_SIGN) - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 on invalid address', async () => { - const badRequest = getLegacyPnpSignRequest( - '0xnotanaddress', - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY, - IDENTIFIER - ) - const authorization = getPnpRequestAuthorization(badRequest, PRIVATE_KEY1) - const res = await sendRequest(badRequest, authorization, SignerEndpoint.LEGACY_PNP_SIGN) - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 401 on failed WALLET_KEY auth', async () => { - const badRequest = getLegacyPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY, - IDENTIFIER - ) - const differentPk = '0x00000000000000000000000000000000000000000000000000000000ddddbbbb' - const authorization = getPnpRequestAuthorization(badRequest, differentPk) - const res = await sendRequest(badRequest, authorization, SignerEndpoint.LEGACY_PNP_SIGN) - expect(res.status).toBe(401) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.UNAUTHENTICATED_USER, - }) - }) - - it('Should respond with 401 on failed DEK auth', async () => { - const badRequest = getLegacyPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.ENCRYPTION_KEY, - IDENTIFIER - ) - const differentPk = '0x00000000000000000000000000000000000000000000000000000000ddddbbbb' - const authorization = getPnpRequestAuthorization(badRequest, differentPk) - const res = await sendRequest(badRequest, authorization, SignerEndpoint.LEGACY_PNP_SIGN) - expect(res.status).toBe(401) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.UNAUTHENTICATED_USER, - }) - }) - - it('Should respond with 403 on out of quota', async () => { - // deplete user's quota - const remainingQuota = expectedQuota - performedQueryCount - await db.transaction(async (trx) => { - for (let i = 0; i < remainingQuota; i++) { - await incrementQueryCount( - db, - ACCOUNTS_TABLE.LEGACY, - ACCOUNT_ADDRESS1, - rootLogger(_config.serviceName), - trx - ) - } - }) - const req = getLegacyPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY, - IDENTIFIER - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.LEGACY_PNP_SIGN) - expect(res.status).toBe(403) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - performedQueryCount: expectedQuota, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - error: WarningMessage.EXCEEDED_QUOTA, - }) - }) - - it('Should respond with 403 if totalQuota and performedQueryCount are zero', async () => { - await prepMocks(ACCOUNT_ADDRESS1, 0, 0, false, zeroBalance, zeroBalance, zeroBalance) - - const spy = jest // for convenience so we don't have to refactor or reset the db just for this test - .spyOn( - jest.requireActual('../../src/common/database/wrappers/account'), - 'getPerformedQueryCount' - ) - .mockResolvedValueOnce(0) - - const req = getLegacyPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY, - IDENTIFIER - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.LEGACY_PNP_SIGN) - expect(res.status).toBe(403) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - performedQueryCount: 0, - totalQuota: 0, - blockNumber: testBlockNumber, - error: WarningMessage.EXCEEDED_QUOTA, - }) - - spy.mockRestore() - }) - - it('Should respond with 403 if performedQueryCount is greater than totalQuota', async () => { - const expectedRemainingQuota = expectedQuota - performedQueryCount - await db.transaction(async (trx) => { - for (let i = 0; i <= expectedRemainingQuota; i++) { - await incrementQueryCount( - db, - ACCOUNTS_TABLE.LEGACY, - ACCOUNT_ADDRESS1, - rootLogger(_config.serviceName), - trx - ) - } - }) - - // It is possible to reach this state due to our fail-open logic - - const req = getLegacyPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY, - IDENTIFIER - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.LEGACY_PNP_SIGN) - expect(res.status).toBe(403) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - performedQueryCount: expectedQuota + 1, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - error: WarningMessage.EXCEEDED_QUOTA, - }) - }) - - it('Should respond with 500 on unsupported key version', async () => { - const badRequest = getLegacyPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY - ) - const authorization = getPnpRequestAuthorization(badRequest, PRIVATE_KEY1) - const res = await sendRequest( - badRequest, - authorization, - SignerEndpoint.LEGACY_PNP_SIGN, - '4' - ) - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - performedQueryCount: performedQueryCount, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - error: ErrorMessage.SIGNATURE_COMPUTATION_FAILURE, - }) - }) - - it('Should respond with 503 on disabled api', async () => { - const configWithApiDisabled: typeof _config = JSON.parse(JSON.stringify(_config)) - configWithApiDisabled.api.legacyPhoneNumberPrivacy.enabled = false - const appWithApiDisabled = startSigner( - configWithApiDisabled, - db, - keyProvider, - newKit('dummyKit') - ) - - const req = getLegacyPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY, - IDENTIFIER - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest( - req, - authorization, - SignerEndpoint.LEGACY_PNP_SIGN, - undefined, - appWithApiDisabled - ) - expect(res.status).toBe(503) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.API_UNAVAILABLE, - }) - }) - - describe('interactions between legacy and new endpoints', () => { - const configWithPNPEnabled: typeof _config = JSON.parse(JSON.stringify(_config)) - configWithPNPEnabled.api.phoneNumberPrivacy.enabled = true - let appWithPNPEnabled: any - - beforeEach(() => { - appWithPNPEnabled = startSigner(configWithPNPEnabled, db, keyProvider, newKit('dummyKit')) - }) - - // Keep both of these cases with the legacy test suite - // since once this endpoint is deprecated, these tests will no longer be needed - it('Should not be affected by requests and queries from the new endpoint', async () => { - mockOdisPaymentsTotalPaidCUSD.mockReturnValue(new BigNumber(1e18)) - const expectedQuotaOnChain = 1000 - const req = getPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest( - req, - authorization, - SignerEndpoint.PNP_SIGN, - undefined, - appWithPNPEnabled - ) - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: 1, - totalQuota: expectedQuotaOnChain, - blockNumber: testBlockNumber, - warnings: [], - }) - expect(res.get(KEY_VERSION_HEADER)).toEqual( - _config.keystore.keys.phoneNumberPrivacy.latest.toString() - ) - const legacyReq = getLegacyPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY, - IDENTIFIER - ) - const legacyAuthorization = getPnpRequestAuthorization(legacyReq, PRIVATE_KEY1) - const legacyRes = await sendRequest( - legacyReq, - legacyAuthorization, - SignerEndpoint.LEGACY_PNP_SIGN, - undefined, - appWithPNPEnabled - ) - expect(legacyRes.status).toBe(200) - expect(legacyRes.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: performedQueryCount + 1, - totalQuota: legacyRes.body.totalQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - expect(legacyRes.get(KEY_VERSION_HEADER)).toEqual( - _config.keystore.keys.phoneNumberPrivacy.latest.toString() - ) - }) - - it('Should not affect the requests and queries to the new endpoint', async () => { - const legacyReq = getLegacyPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY, - IDENTIFIER - ) - const legacyAuthorization = getPnpRequestAuthorization(legacyReq, PRIVATE_KEY1) - const legacyRes = await sendRequest( - legacyReq, - legacyAuthorization, - SignerEndpoint.LEGACY_PNP_SIGN, - undefined, - appWithPNPEnabled - ) - expect(legacyRes.status).toBe(200) - expect(legacyRes.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: performedQueryCount + 1, - totalQuota: legacyRes.body.totalQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - expect(legacyRes.get(KEY_VERSION_HEADER)).toEqual( - _config.keystore.keys.phoneNumberPrivacy.latest.toString() - ) - mockOdisPaymentsTotalPaidCUSD.mockReturnValue(new BigNumber(1e18)) - const expectedQuotaOnChain = 1000 - const req = getPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest( - req, - authorization, - SignerEndpoint.PNP_SIGN, - undefined, - appWithPNPEnabled - ) - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: 1, - totalQuota: expectedQuotaOnChain, - blockNumber: testBlockNumber, - warnings: [], - }) - expect(res.get(KEY_VERSION_HEADER)).toEqual( - _config.keystore.keys.phoneNumberPrivacy.latest.toString() - ) - }) - }) - - describe('functionality in case of errors', () => { - it('Should return 500 on DB performedQueryCount query failure', async () => { - // deplete user's quota - const remainingQuota = expectedQuota - performedQueryCount - await db.transaction(async (trx) => { - for (let i = 0; i < remainingQuota; i++) { - await incrementQueryCount( - db, - ACCOUNTS_TABLE.LEGACY, - ACCOUNT_ADDRESS1, - rootLogger(_config.serviceName), - trx - ) - } - }) - // sanity check - expect( - await getPerformedQueryCount( - db, - ACCOUNTS_TABLE.LEGACY, - ACCOUNT_ADDRESS1, - rootLogger(_config.serviceName) - ) - ).toBe(expectedQuota) - - const spy = jest - .spyOn( - jest.requireActual('../../src/common/database/wrappers/account'), - 'getPerformedQueryCount' - ) - .mockRejectedValueOnce(new Error()) - - const req = getLegacyPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY, - IDENTIFIER - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.LEGACY_PNP_SIGN) - - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - performedQueryCount: -1, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - error: ErrorMessage.DATABASE_GET_FAILURE, - }) - - spy.mockRestore() - }) - - it('Should return 200 w/ warning on blockchain totalQuota query failure when shouldFailOpen is true', async () => { - const configWithFailOpenEnabled: typeof _config = JSON.parse(JSON.stringify(_config)) - configWithFailOpenEnabled.api.legacyPhoneNumberPrivacy.shouldFailOpen = true - const appWithFailOpenEnabled = startSigner( - configWithFailOpenEnabled, - db, - keyProvider, - newKit('dummyKit') - ) - - // deplete user's quota - const remainingQuota = expectedQuota - performedQueryCount - await db.transaction(async (trx) => { - for (let i = 0; i < remainingQuota; i++) { - await incrementQueryCount( - db, - ACCOUNTS_TABLE.LEGACY, - ACCOUNT_ADDRESS1, - rootLogger(_config.serviceName), - trx - ) - } - }) - // sanity check - expect( - await getPerformedQueryCount( - db, - ACCOUNTS_TABLE.LEGACY, - ACCOUNT_ADDRESS1, - rootLogger(_config.serviceName) - ) - ).toBe(expectedQuota) - - mockContractKit.connection.getTransactionCount.mockRejectedValue(new Error()) - - const req = getLegacyPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY, - IDENTIFIER - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest( - req, - authorization, - SignerEndpoint.LEGACY_PNP_SIGN, - '1', - appWithFailOpenEnabled - ) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: expectedQuota + 1, // bc we depleted the user's quota above - totalQuota: Number.MAX_SAFE_INTEGER, - blockNumber: testBlockNumber, - warnings: [ErrorMessage.FAILURE_TO_GET_TOTAL_QUOTA, ErrorMessage.FULL_NODE_ERROR], - }) - - // check DB state: performedQueryCount was incremented and request was stored - expect( - await getPerformedQueryCount( - db, - ACCOUNTS_TABLE.LEGACY, - ACCOUNT_ADDRESS1, - rootLogger(_config.serviceName) - ) - ).toBe(expectedQuota + 1) - expect( - await getRequestExists( - db, - REQUESTS_TABLE.LEGACY, - req.account, - req.blindedQueryPhoneNumber, - rootLogger(_config.serviceName) - ) - ).toBe(true) - }) - - it('Should return 500 on blockchain totalQuota query failure when shouldFailOpen is false', async () => { - mockContractKit.connection.getTransactionCount.mockRejectedValue(new Error()) - - const configWithFailOpenDisabled: typeof _config = JSON.parse(JSON.stringify(_config)) - configWithFailOpenDisabled.api.legacyPhoneNumberPrivacy.shouldFailOpen = false - const appWithFailOpenDisabled = startSigner( - configWithFailOpenDisabled, - db, - keyProvider, - newKit('dummyKit') - ) - - const req = getLegacyPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY, - IDENTIFIER - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest( - req, - authorization, - SignerEndpoint.LEGACY_PNP_SIGN, - '1', - appWithFailOpenDisabled - ) - - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - performedQueryCount: performedQueryCount, - totalQuota: -1, - blockNumber: testBlockNumber, - error: ErrorMessage.FULL_NODE_ERROR, - }) - }) - - it('Should return 500 on failure to increment query count', async () => { - const logger = rootLogger(_config.serviceName) - const spy = jest - .spyOn( - jest.requireActual('../../src/common/database/wrappers/account'), - 'incrementQueryCount' - ) - .mockImplementationOnce(() => { - countAndThrowDBError(new Error(), logger, ErrorMessage.DATABASE_UPDATE_FAILURE) - }) - - const req = getLegacyPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY, - IDENTIFIER - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.LEGACY_PNP_SIGN) - - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: ErrorMessage.DATABASE_UPDATE_FAILURE, - }) - - spy.mockRestore() - - // check DB state: performedQueryCount was not incremented and request was not stored - expect( - await getPerformedQueryCount(db, ACCOUNTS_TABLE.LEGACY, ACCOUNT_ADDRESS1, logger) - ).toBe(performedQueryCount) - expect( - await getRequestExists( - db, - REQUESTS_TABLE.LEGACY, - req.account, - req.blindedQueryPhoneNumber, - logger - ) - ).toBe(false) - }) - - it('Should return 500 on failure to store request', async () => { - const logger = rootLogger(_config.serviceName) - const spy = jest - .spyOn(jest.requireActual('../../src/common/database/wrappers/request'), 'storeRequest') - .mockImplementationOnce(() => { - countAndThrowDBError(new Error(), logger, ErrorMessage.DATABASE_INSERT_FAILURE) - }) - - const req = getLegacyPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY, - IDENTIFIER - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.LEGACY_PNP_SIGN) - - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: ErrorMessage.DATABASE_INSERT_FAILURE, - }) - - spy.mockRestore() - - // check DB state: performedQueryCount was not incremented and request was not stored - expect( - await getPerformedQueryCount(db, ACCOUNTS_TABLE.LEGACY, ACCOUNT_ADDRESS1, logger) - ).toBe(performedQueryCount) - expect( - await getRequestExists( - db, - REQUESTS_TABLE.LEGACY, - req.account, - req.blindedQueryPhoneNumber, - logger - ) - ).toBe(false) - }) - - it('Should return 200 on failure to fetch DEK when shouldFailOpen is true', async () => { - mockGetDataEncryptionKey.mockImplementation(() => { - throw new Error() - }) - - const req = getLegacyPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.ENCRYPTION_KEY, - IDENTIFIER - ) - - const configWithFailOpenEnabled: typeof _config = JSON.parse(JSON.stringify(_config)) - configWithFailOpenEnabled.api.legacyPhoneNumberPrivacy.shouldFailOpen = true - const appWithFailOpenEnabled = startSigner( - configWithFailOpenEnabled, - db, - keyProvider, - newKit('dummyKit') - ) - - // NOT the dek private key, so authentication would fail if getDataEncryptionKey succeeded - const differentPk = '0x00000000000000000000000000000000000000000000000000000000ddddbbbb' - const authorization = getPnpRequestAuthorization(req, differentPk) - const res = await sendRequest( - req, - authorization, - SignerEndpoint.LEGACY_PNP_SIGN, - '1', - appWithFailOpenEnabled - ) - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: performedQueryCount + 1, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [ErrorMessage.FAILURE_TO_GET_DEK, ErrorMessage.FAILING_OPEN], - }) - }) - - it('Should return 500 on bls signing error', async () => { - const spy = jest - .spyOn(jest.requireActual('blind-threshold-bls'), 'partialSignBlindedMessage') - .mockImplementationOnce(() => { - throw new Error() - }) - - const req = getLegacyPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY, - IDENTIFIER - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.LEGACY_PNP_SIGN) - - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - performedQueryCount: performedQueryCount, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - error: ErrorMessage.SIGNATURE_COMPUTATION_FAILURE, - }) - - spy.mockRestore() - - // check DB state: performedQueryCount was not incremented and request was not stored - expect( - await getPerformedQueryCount( - db, - ACCOUNTS_TABLE.LEGACY, - ACCOUNT_ADDRESS1, - rootLogger(_config.serviceName) - ) - ).toBe(performedQueryCount) - expect( - await getRequestExists( - db, - REQUESTS_TABLE.LEGACY, - req.account, - req.blindedQueryPhoneNumber, - rootLogger(_config.serviceName) - ) - ).toBe(false) - }) - - it('Should return 500 on generic error in sign', async () => { - const spy = jest - .spyOn( - jest.requireActual('../../src/common/bls/bls-cryptography-client'), - 'computeBlindedSignature' - ) - .mockImplementationOnce(() => { - // Trigger a generic error in .sign to trigger the default error returned. - throw new Error() - }) - - const req = getLegacyPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY, - IDENTIFIER - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.LEGACY_PNP_SIGN) - - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - performedQueryCount: performedQueryCount, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - error: ErrorMessage.SIGNATURE_COMPUTATION_FAILURE, - }) - - spy.mockRestore() - - // check DB state: performedQueryCount was not incremented and request was not stored - expect( - await getPerformedQueryCount( - db, - ACCOUNTS_TABLE.LEGACY, - ACCOUNT_ADDRESS1, - rootLogger(config.serviceName) - ) - ).toBe(performedQueryCount) - expect( - await getRequestExists( - db, - REQUESTS_TABLE.LEGACY, - req.account, - req.blindedQueryPhoneNumber, - rootLogger(config.serviceName) - ) - ).toBe(false) - }) - - it('Should respond with 500 on signer timeout', async () => { - const testTimeoutMS = 0 - const delay = 200 - const spy = jest - .spyOn( - jest.requireActual('../../src/common/database/wrappers/account'), - 'getPerformedQueryCount' - ) - .mockImplementationOnce(async () => { - await new Promise((resolve) => setTimeout(resolve, testTimeoutMS + delay)) - return performedQueryCount - }) - - const configWithShortTimeout = JSON.parse(JSON.stringify(_config)) - configWithShortTimeout.timeout = testTimeoutMS - const appWithShortTimeout = startSigner( - configWithShortTimeout, - db, - keyProvider, - newKit('dummyKit') - ) - - const req = getLegacyPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest( - req, - authorization, - SignerEndpoint.LEGACY_PNP_SIGN, - undefined, - appWithShortTimeout - ) - - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - error: ErrorMessage.TIMEOUT_FROM_SIGNER, - version: expectedVersion, - }) - spy.mockRestore() - // Allow time for non-killed processes to finish - await new Promise((resolve) => setTimeout(resolve, delay)) - // Check that DB was not updated - expect( - await getPerformedQueryCount( - db, - ACCOUNTS_TABLE.LEGACY, - ACCOUNT_ADDRESS1, - rootLogger(config.serviceName) - ) - ).toBe(performedQueryCount) - expect( - await getRequestExists( - db, - REQUESTS_TABLE.LEGACY, - req.account, - req.blindedQueryPhoneNumber, - rootLogger(config.serviceName) - ) - ).toBe(false) - }) - }) - }) - }) -}) diff --git a/packages/phone-number-privacy/signer/test/integration/pnp.test.ts b/packages/phone-number-privacy/signer/test/integration/pnp.test.ts deleted file mode 100644 index 59012e0d0a5..00000000000 --- a/packages/phone-number-privacy/signer/test/integration/pnp.test.ts +++ /dev/null @@ -1,1368 +0,0 @@ -import { newKit } from '@celo/contractkit' -import { - AuthenticationMethod, - ErrorMessage, - KEY_VERSION_HEADER, - PhoneNumberPrivacyRequest, - PnpQuotaResponseFailure, - PnpQuotaResponseSuccess, - rootLogger, - SignerEndpoint, - SignMessageResponseFailure, - SignMessageResponseSuccess, - TestUtils, - WarningMessage, -} from '@celo/phone-number-privacy-common' -import { BLINDED_PHONE_NUMBER } from '@celo/phone-number-privacy-common/lib/test/values' -import BigNumber from 'bignumber.js' -import { Knex } from 'knex' -import request from 'supertest' -import { initDatabase } from '../../src/common/database/database' -import { ACCOUNTS_TABLE } from '../../src/common/database/models/account' -import { REQUESTS_TABLE } from '../../src/common/database/models/request' -import { countAndThrowDBError } from '../../src/common/database/utils' -import { - getPerformedQueryCount, - incrementQueryCount, -} from '../../src/common/database/wrappers/account' -import { getRequestExists } from '../../src/common/database/wrappers/request' -import { initKeyProvider } from '../../src/common/key-management/key-provider' -import { KeyProvider } from '../../src/common/key-management/key-provider-base' -import { config, getSignerVersion, SupportedDatabase, SupportedKeystore } from '../../src/config' -import { startSigner } from '../../src/server' - -const { - ContractRetrieval, - createMockContractKit, - createMockAccounts, - createMockOdisPayments, - createMockWeb3, - getPnpQuotaRequest, - getPnpRequestAuthorization, - getPnpSignRequest, -} = TestUtils.Utils -const { PRIVATE_KEY1, ACCOUNT_ADDRESS1, mockAccount, DEK_PRIVATE_KEY, DEK_PUBLIC_KEY } = - TestUtils.Values - -jest.setTimeout(20000) - -const testBlockNumber = 1000000 -const zeroBalance = new BigNumber(0) - -const mockOdisPaymentsTotalPaidCUSD = jest.fn() -const mockGetWalletAddress = jest.fn() -const mockGetDataEncryptionKey = jest.fn() - -const mockContractKit = createMockContractKit( - { - [ContractRetrieval.getAccounts]: createMockAccounts( - mockGetWalletAddress, - mockGetDataEncryptionKey - ), - [ContractRetrieval.getOdisPayments]: createMockOdisPayments(mockOdisPaymentsTotalPaidCUSD), - }, - createMockWeb3(5, testBlockNumber) -) -jest.mock('@celo/contractkit', () => ({ - ...jest.requireActual('@celo/contractkit'), - newKit: jest.fn().mockImplementation(() => mockContractKit), -})) - -// Indexes correspond to keyVersion - 1 -const expectedSignatures: string[] = [ - 'MAAAAAAAAACEVdw1ULDwAiTcZuPnZxHHh38PNa+/g997JgV10QnEq9yeuLxbM9l7vk0EAicV7IAAAAAA', - 'MAAAAAAAAAAmUJY0s9p7fMfs7GIoSiGJoObAN8ZpA7kRqeC9j/Q23TBrG3Jtxc8xWibhNVZhbYEAAAAA', - 'MAAAAAAAAAC4aBbzhHvt6l/b+8F7cILmWxZZ5Q7S6R4RZ/IgZR7Pfb9B1Wg9fsDybgxVTSv5BYEAAAAA', -] - -describe('pnp', () => { - let keyProvider: KeyProvider - let app: any - let db: Knex - - const onChainBalance = new BigNumber(1e18) - const expectedQuota = 1000 - const expectedVersion = getSignerVersion() - - // create deep copy - const _config: typeof config = JSON.parse(JSON.stringify(config)) - _config.db.type = SupportedDatabase.Sqlite - _config.keystore.type = SupportedKeystore.MOCK_SECRET_MANAGER - _config.api.phoneNumberPrivacy.enabled = true - - const expectedSignature = expectedSignatures[_config.keystore.keys.phoneNumberPrivacy.latest - 1] - - beforeAll(async () => { - keyProvider = await initKeyProvider(_config) - }) - - beforeEach(async () => { - // Create a new in-memory database for each test. - db = await initDatabase(_config) - app = startSigner(_config, db, keyProvider, newKit('dummyKit')) - mockOdisPaymentsTotalPaidCUSD.mockReset() - mockGetDataEncryptionKey.mockReset().mockReturnValue(DEK_PUBLIC_KEY) - mockGetWalletAddress.mockReset().mockReturnValue(mockAccount) - }) - - afterEach(async () => { - // Close and destroy the in-memory database. - // Note: If tests start to be too slow, this could be replaced with more complicated logic to - // reset the database state without destroying and recreting it for each test. - await db?.destroy() - }) - - describe(`${SignerEndpoint.STATUS}`, () => { - it('Should return 200 and correct version', async () => { - const res = await request(app).get(SignerEndpoint.STATUS) - expect(res.status).toBe(200) - expect(res.body.version).toBe(expectedVersion) - }) - }) - - const sendRequest = async ( - req: PhoneNumberPrivacyRequest, - authorization: string, - endpoint: SignerEndpoint, - keyVersionHeader?: string, - signerApp: any = app - ) => { - const _req = request(signerApp).post(endpoint).set('Authorization', authorization) - - if (keyVersionHeader !== undefined) { - _req.set(KEY_VERSION_HEADER, keyVersionHeader) - } - - return _req.send(req) - } - - type pnpQuotaTestCase = { - cusdOdisPaymentInWei: BigNumber - expectedTotalQuota: number - } - const quotaCalculationTestCases: pnpQuotaTestCase[] = [ - { - cusdOdisPaymentInWei: new BigNumber(0), - expectedTotalQuota: 0, - }, - { - cusdOdisPaymentInWei: new BigNumber(1), - expectedTotalQuota: 0, - }, - { - cusdOdisPaymentInWei: new BigNumber(1.56e18), - expectedTotalQuota: 1560, - }, - { - // Sanity check for the default values to be used in endpoint setup tests - cusdOdisPaymentInWei: onChainBalance, - expectedTotalQuota: expectedQuota, - }, - { - // Unrealistically large amount paid for ODIS quota - cusdOdisPaymentInWei: new BigNumber(1.23456789e26), - expectedTotalQuota: 123456789000, - }, - ] - - describe(`${SignerEndpoint.PNP_QUOTA}`, () => { - describe('quota calculation logic', () => { - quotaCalculationTestCases.forEach(({ cusdOdisPaymentInWei, expectedTotalQuota }) => { - it(`Should get totalQuota=${expectedTotalQuota} - for cUSD (wei) payment=${cusdOdisPaymentInWei.toString()}`, async () => { - mockOdisPaymentsTotalPaidCUSD.mockReturnValue(cusdOdisPaymentInWei) - const req = getPnpQuotaRequest(ACCOUNT_ADDRESS1) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.PNP_QUOTA) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - performedQueryCount: 0, - totalQuota: expectedTotalQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - }) - }) - }) - - describe('endpoint functionality', () => { - // Use values already tested in quota logic tests, [onChainBalance, expectedQuota] - beforeEach(async () => { - mockOdisPaymentsTotalPaidCUSD.mockReturnValue(onChainBalance) - }) - - it('Should respond with 200 on valid request', async () => { - const req = getPnpQuotaRequest(ACCOUNT_ADDRESS1) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - - const res = await sendRequest(req, authorization, SignerEndpoint.PNP_QUOTA) - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: res.body.version, - performedQueryCount: 0, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - }) - - it('Should respond with 200 on repeated valid requests', async () => { - const req = getPnpQuotaRequest(ACCOUNT_ADDRESS1) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - - const res1 = await sendRequest(req, authorization, SignerEndpoint.PNP_QUOTA) - expect(res1.status).toBe(200) - expect(res1.body).toStrictEqual({ - success: true, - version: res1.body.version, - performedQueryCount: 0, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - const res2 = await sendRequest(req, authorization, SignerEndpoint.PNP_QUOTA) - expect(res2.status).toBe(200) - expect(res2.body).toStrictEqual(res1.body) - }) - - it('Should respond with 200 on valid request when authenticated with DEK', async () => { - const req = getPnpQuotaRequest(ACCOUNT_ADDRESS1, AuthenticationMethod.ENCRYPTION_KEY) - const authorization = getPnpRequestAuthorization(req, DEK_PRIVATE_KEY) - - const res = await sendRequest(req, authorization, SignerEndpoint.PNP_QUOTA) - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: res.body.version, - performedQueryCount: 0, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - }) - - it('Should respond with 200 on extra request fields', async () => { - const req = getPnpQuotaRequest(ACCOUNT_ADDRESS1) - // @ts-ignore Intentionally adding an extra field to the request type - req.extraField = 'dummyString' - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.PNP_QUOTA) - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - performedQueryCount: 0, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - }) - - it('Should respond with 200 if performedQueryCount is greater than totalQuota', async () => { - await db.transaction(async (trx) => { - for (let i = 0; i <= expectedQuota; i++) { - await incrementQueryCount( - db, - ACCOUNTS_TABLE.ONCHAIN, - ACCOUNT_ADDRESS1, - rootLogger(config.serviceName), - trx - ) - } - }) - const req = getPnpQuotaRequest(ACCOUNT_ADDRESS1) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.PNP_QUOTA) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: res.body.version, - performedQueryCount: expectedQuota + 1, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - }) - - it('Should respond with 400 on missing request fields', async () => { - const badRequest = getPnpQuotaRequest(ACCOUNT_ADDRESS1) - // @ts-ignore Intentionally deleting required field - delete badRequest.account - const authorization = getPnpRequestAuthorization(badRequest, PRIVATE_KEY1) - const res = await sendRequest(badRequest, authorization, SignerEndpoint.PNP_QUOTA) - - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 401 on failed WALLET_KEY auth', async () => { - // Request from one account, signed by another account - const badRequest = getPnpQuotaRequest(mockAccount, AuthenticationMethod.WALLET_KEY) - const authorization = getPnpRequestAuthorization(badRequest, PRIVATE_KEY1) - const res = await sendRequest(badRequest, authorization, SignerEndpoint.PNP_QUOTA) - - expect(res.status).toBe(401) - expect(res.body).toStrictEqual({ - success: false, - version: res.body.version, - error: WarningMessage.UNAUTHENTICATED_USER, - }) - }) - - it('Should respond with 401 on failed DEK auth', async () => { - const badRequest = getPnpQuotaRequest(ACCOUNT_ADDRESS1, AuthenticationMethod.ENCRYPTION_KEY) - const differentPk = '0x00000000000000000000000000000000000000000000000000000000ddddbbbb' - const authorization = getPnpRequestAuthorization(badRequest, differentPk) - const res = await sendRequest(badRequest, authorization, SignerEndpoint.PNP_QUOTA) - - expect(res.status).toBe(401) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.UNAUTHENTICATED_USER, - }) - }) - - it('Should respond with 503 on disabled api', async () => { - const configWithApiDisabled: typeof _config = JSON.parse(JSON.stringify(_config)) - configWithApiDisabled.api.phoneNumberPrivacy.enabled = false - const appWithApiDisabled = startSigner( - configWithApiDisabled, - db, - keyProvider, - newKit('dummyKit') - ) - - const req = getPnpQuotaRequest(ACCOUNT_ADDRESS1) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest( - req, - authorization, - SignerEndpoint.PNP_QUOTA, - undefined, - appWithApiDisabled - ) - expect(res.status).toBe(503) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.API_UNAVAILABLE, - }) - }) - - describe('functionality in case of errors', () => { - it('Should respond with 200 on failure to fetch DEK when shouldFailOpen is true', async () => { - mockGetDataEncryptionKey.mockImplementation(() => { - throw new Error() - }) - - const req = getPnpQuotaRequest(ACCOUNT_ADDRESS1, AuthenticationMethod.ENCRYPTION_KEY) - - const configWithFailOpenEnabled: typeof _config = JSON.parse(JSON.stringify(_config)) - configWithFailOpenEnabled.api.phoneNumberPrivacy.shouldFailOpen = true - const appWithFailOpenEnabled = startSigner( - configWithFailOpenEnabled, - db, - keyProvider, - newKit('dummyKit') - ) - - // NOT the dek private key, so authentication would fail if getDataEncryptionKey succeeded - const differentPk = '0x00000000000000000000000000000000000000000000000000000000ddddbbbb' - const authorization = getPnpRequestAuthorization(req, differentPk) - const res = await sendRequest( - req, - authorization, - SignerEndpoint.PNP_QUOTA, - '1', - appWithFailOpenEnabled - ) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - performedQueryCount: 0, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [ErrorMessage.FAILURE_TO_GET_DEK, ErrorMessage.FAILING_OPEN], - }) - }) - - it('Should respond with 500 on DB performedQueryCount query failure', async () => { - const spy = jest - .spyOn( - jest.requireActual('../../src/common/database/wrappers/account'), - 'getPerformedQueryCount' - ) - .mockRejectedValueOnce(new Error()) - - const req = getPnpQuotaRequest(ACCOUNT_ADDRESS1) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.PNP_QUOTA) - - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: ErrorMessage.FAILURE_TO_GET_PERFORMED_QUERY_COUNT, - }) - - spy.mockRestore() - }) - - it('Should respond with 500 on blockchain totalQuota query failure', async () => { - mockOdisPaymentsTotalPaidCUSD.mockImplementation(() => { - throw new Error('dummy error') - }) - const req = getPnpQuotaRequest(ACCOUNT_ADDRESS1) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.PNP_QUOTA) - - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: ErrorMessage.FAILURE_TO_GET_TOTAL_QUOTA, - }) - }) - - it('Should respond with 500 on signer timeout', async () => { - const testTimeoutMS = 0 - const delay = 100 - const spy = jest - .spyOn( - jest.requireActual('../../src/common/database/wrappers/account'), - 'getPerformedQueryCount' - ) - .mockImplementation(async () => { - await new Promise((resolve) => setTimeout(resolve, testTimeoutMS + delay)) - return expectedQuota - }) - - const configWithShortTimeout = JSON.parse(JSON.stringify(_config)) - configWithShortTimeout.timeout = testTimeoutMS - const appWithShortTimeout = startSigner( - configWithShortTimeout, - db, - keyProvider, - newKit('dummyKit') - ) - const req = getPnpQuotaRequest(ACCOUNT_ADDRESS1) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest( - req, - authorization, - SignerEndpoint.PNP_QUOTA, - undefined, - appWithShortTimeout - ) - // Ensure that this is restored before test can fail on assertions - // to prevent failures in other tests - spy.mockRestore() - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - error: ErrorMessage.TIMEOUT_FROM_SIGNER, - version: expectedVersion, - }) - // Allow time for non-killed processes to finish - await new Promise((resolve) => setTimeout(resolve, delay)) - }) - }) - }) - }) - - describe(`${SignerEndpoint.PNP_SIGN}`, () => { - describe('quota calculation logic', () => { - quotaCalculationTestCases.forEach(({ expectedTotalQuota, cusdOdisPaymentInWei }) => { - it(`Should get totalQuota=${expectedTotalQuota} - for cUSD (wei) payment=${cusdOdisPaymentInWei.toString()}`, async () => { - mockOdisPaymentsTotalPaidCUSD.mockReturnValue(cusdOdisPaymentInWei) - - const req = getPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.PNP_SIGN) - - const shouldSucceed = expectedTotalQuota > 0 - - if (shouldSucceed) { - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: 1, // incremented for signature request - totalQuota: expectedTotalQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - } else { - expect(res.status).toBe(403) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - performedQueryCount: 0, - totalQuota: expectedTotalQuota, - blockNumber: testBlockNumber, - error: WarningMessage.EXCEEDED_QUOTA, - }) - } - }) - }) - }) - - describe('endpoint functionality', () => { - // Use values already tested in quota logic tests, [onChainBalance, expectedQuota] - const performedQueryCount = 2 - - beforeEach(async () => { - mockOdisPaymentsTotalPaidCUSD.mockReturnValue(onChainBalance) - await db.transaction(async (trx) => { - for (let i = 0; i < performedQueryCount; i++) { - await incrementQueryCount( - db, - ACCOUNTS_TABLE.ONCHAIN, - ACCOUNT_ADDRESS1, - rootLogger(_config.serviceName), - trx - ) - } - }) - }) - - it('Should respond with 200 on valid request', async () => { - const req = getPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.PNP_SIGN) - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: performedQueryCount + 1, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - expect(res.get(KEY_VERSION_HEADER)).toEqual( - _config.keystore.keys.phoneNumberPrivacy.latest.toString() - ) - }) - - it('Should respond with 200 on valid request when authenticated with DEK', async () => { - const req = getPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.ENCRYPTION_KEY - ) - const authorization = getPnpRequestAuthorization(req, DEK_PRIVATE_KEY) - const res = await sendRequest(req, authorization, SignerEndpoint.PNP_SIGN) - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: performedQueryCount + 1, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - }) - - for (let i = 1; i <= 3; i++) { - it(`Should respond with 200 on valid request with key version header ${i}`, async () => { - const req = getPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.PNP_SIGN, i.toString()) - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignatures[i - 1], - performedQueryCount: performedQueryCount + 1, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - expect(res.get(KEY_VERSION_HEADER)).toEqual(i.toString()) - }) - } - - it('Should respond with 200 and warning on repeated valid requests', async () => { - const req = getPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res1 = await sendRequest(req, authorization, SignerEndpoint.PNP_SIGN) - expect(res1.status).toBe(200) - expect(res1.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: performedQueryCount + 1, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - const res2 = await sendRequest(req, authorization, SignerEndpoint.PNP_SIGN) - expect(res2.status).toBe(200) - res1.body.warnings.push(WarningMessage.DUPLICATE_REQUEST_TO_GET_PARTIAL_SIG) - expect(res2.body).toStrictEqual(res1.body) - }) - - it('Should respond with 200 on extra request fields', async () => { - const req = getPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY - ) - // @ts-ignore Intentionally adding an extra field to the request type - req.extraField = 'dummyString' - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.PNP_SIGN) - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: performedQueryCount + 1, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [], - }) - }) - - it('Should respond with 400 on missing request fields', async () => { - const badRequest = getPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY - ) - // @ts-ignore Intentionally deleting required field - delete badRequest.account - const authorization = getPnpRequestAuthorization(badRequest, PRIVATE_KEY1) - const res = await sendRequest(badRequest, authorization, SignerEndpoint.PNP_SIGN) - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 on invalid key version', async () => { - const badRequest = getPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY - ) - const authorization = getPnpRequestAuthorization(badRequest, PRIVATE_KEY1) - const res = await sendRequest(badRequest, authorization, SignerEndpoint.PNP_SIGN, 'a') - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_KEY_VERSION_REQUEST, - }) - }) - - it('Should respond with 400 on invalid blinded message', async () => { - const badRequest = getPnpSignRequest( - ACCOUNT_ADDRESS1, - '+1234567890', - AuthenticationMethod.WALLET_KEY - ) - const authorization = getPnpRequestAuthorization(badRequest, PRIVATE_KEY1) - const res = await sendRequest(badRequest, authorization, SignerEndpoint.PNP_SIGN) - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 400 on invalid address', async () => { - const badRequest = getPnpSignRequest( - '0xnotanaddress', - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY - ) - const authorization = getPnpRequestAuthorization(badRequest, PRIVATE_KEY1) - const res = await sendRequest(badRequest, authorization, SignerEndpoint.PNP_SIGN) - expect(res.status).toBe(400) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.INVALID_INPUT, - }) - }) - - it('Should respond with 401 on failed WALLET_KEY auth', async () => { - const badRequest = getPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY - ) - const differentPk = '0x00000000000000000000000000000000000000000000000000000000ddddbbbb' - const authorization = getPnpRequestAuthorization(badRequest, differentPk) - const res = await sendRequest(badRequest, authorization, SignerEndpoint.PNP_SIGN) - expect(res.status).toBe(401) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.UNAUTHENTICATED_USER, - }) - }) - - it('Should respond with 401 on failed DEK auth', async () => { - const badRequest = getPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.ENCRYPTION_KEY - ) - const differentPk = '0x00000000000000000000000000000000000000000000000000000000ddddbbbb' - const authorization = getPnpRequestAuthorization(badRequest, differentPk) - const res = await sendRequest(badRequest, authorization, SignerEndpoint.PNP_SIGN) - expect(res.status).toBe(401) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.UNAUTHENTICATED_USER, - }) - }) - - it('Should respond with 403 on out of quota', async () => { - // deplete user's quota - const remainingQuota = expectedQuota - performedQueryCount - await db.transaction(async (trx) => { - for (let i = 0; i < remainingQuota; i++) { - await incrementQueryCount( - db, - ACCOUNTS_TABLE.ONCHAIN, - ACCOUNT_ADDRESS1, - rootLogger(_config.serviceName), - trx - ) - } - }) - const req = getPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.PNP_SIGN) - expect(res.status).toBe(403) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - performedQueryCount: expectedQuota, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - error: WarningMessage.EXCEEDED_QUOTA, - }) - }) - - it('Should respond with 403 if totalQuota and performedQueryCount are zero', async () => { - mockOdisPaymentsTotalPaidCUSD.mockReturnValue(zeroBalance) - const spy = jest // for convenience so we don't have to refactor or reset the db just for this test - .spyOn( - jest.requireActual('../../src/common/database/wrappers/account'), - 'getPerformedQueryCount' - ) - .mockResolvedValueOnce(0) - - const req = getPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.PNP_SIGN) - expect(res.status).toBe(403) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - performedQueryCount: 0, - totalQuota: 0, - blockNumber: testBlockNumber, - error: WarningMessage.EXCEEDED_QUOTA, - }) - - spy.mockRestore() - }) - - it('Should respond with 403 if performedQueryCount is greater than totalQuota', async () => { - const expectedRemainingQuota = expectedQuota - performedQueryCount - await db.transaction(async (trx) => { - for (let i = 0; i <= expectedRemainingQuota; i++) { - await incrementQueryCount( - db, - ACCOUNTS_TABLE.ONCHAIN, - ACCOUNT_ADDRESS1, - rootLogger(_config.serviceName), - trx - ) - } - }) - - // It is possible to reach this state due to our fail-open logic - - const req = getPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.PNP_SIGN) - expect(res.status).toBe(403) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - performedQueryCount: expectedQuota + 1, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - error: WarningMessage.EXCEEDED_QUOTA, - }) - }) - - it('Should respond with 500 on unsupported key version', async () => { - const badRequest = getPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY - ) - const authorization = getPnpRequestAuthorization(badRequest, PRIVATE_KEY1) - const res = await sendRequest(badRequest, authorization, SignerEndpoint.PNP_SIGN, '4') - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - performedQueryCount: performedQueryCount, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - error: ErrorMessage.SIGNATURE_COMPUTATION_FAILURE, - }) - }) - - it('Should respond with 503 on disabled api', async () => { - const configWithApiDisabled: typeof _config = JSON.parse(JSON.stringify(_config)) - configWithApiDisabled.api.phoneNumberPrivacy.enabled = false - const appWithApiDisabled = startSigner( - configWithApiDisabled, - db, - keyProvider, - newKit('dummyKit') - ) - - const req = getPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest( - req, - authorization, - SignerEndpoint.PNP_SIGN, - '1', - appWithApiDisabled - ) - expect(res.status).toBe(503) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: WarningMessage.API_UNAVAILABLE, - }) - }) - - describe('functionality in case of errors', () => { - it('Should return 500 on DB performedQueryCount query failure', async () => { - // deplete user's quota - const remainingQuota = expectedQuota - performedQueryCount - await db.transaction(async (trx) => { - for (let i = 0; i < remainingQuota; i++) { - await incrementQueryCount( - db, - ACCOUNTS_TABLE.ONCHAIN, - ACCOUNT_ADDRESS1, - rootLogger(_config.serviceName), - trx - ) - } - }) - // sanity check - expect( - await getPerformedQueryCount( - db, - ACCOUNTS_TABLE.ONCHAIN, - ACCOUNT_ADDRESS1, - rootLogger(_config.serviceName) - ) - ).toBe(expectedQuota) - - const spy = jest - .spyOn( - jest.requireActual('../../src/common/database/wrappers/account'), - 'getPerformedQueryCount' - ) - .mockRejectedValueOnce(new Error()) - - const req = getPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.PNP_SIGN) - - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - performedQueryCount: -1, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - error: ErrorMessage.DATABASE_GET_FAILURE, - }) - - spy.mockRestore() - }) - - it('Should respond with 500 on signer timeout', async () => { - const testTimeoutMS = 0 - const delay = 200 - const spy = jest - .spyOn( - jest.requireActual('../../src/common/database/wrappers/account'), - 'getPerformedQueryCount' - ) - .mockImplementationOnce(async () => { - await new Promise((resolve) => setTimeout(resolve, testTimeoutMS + delay)) - return performedQueryCount - }) - - const configWithShortTimeout = JSON.parse(JSON.stringify(_config)) - configWithShortTimeout.timeout = testTimeoutMS - const appWithShortTimeout = startSigner( - configWithShortTimeout, - db, - keyProvider, - newKit('dummyKit') - ) - - const req = getPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest( - req, - authorization, - SignerEndpoint.PNP_SIGN, - undefined, - appWithShortTimeout - ) - - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - error: ErrorMessage.TIMEOUT_FROM_SIGNER, - version: expectedVersion, - }) - spy.mockRestore() - // Allow time for non-killed processes to finish - await new Promise((resolve) => setTimeout(resolve, delay)) - // Check that DB was not updated - expect( - await getPerformedQueryCount( - db, - ACCOUNTS_TABLE.ONCHAIN, - ACCOUNT_ADDRESS1, - rootLogger(config.serviceName) - ) - ).toBe(performedQueryCount) - expect( - await getRequestExists( - db, - REQUESTS_TABLE.ONCHAIN, - req.account, - req.blindedQueryPhoneNumber, - rootLogger(config.serviceName) - ) - ).toBe(false) - }) - - it('Should return 200 w/ warning on blockchain totalQuota query failure when shouldFailOpen is true', async () => { - const configWithFailOpenEnabled: typeof _config = JSON.parse(JSON.stringify(_config)) - configWithFailOpenEnabled.api.phoneNumberPrivacy.shouldFailOpen = true - const appWithFailOpenEnabled = startSigner( - configWithFailOpenEnabled, - db, - keyProvider, - newKit('dummyKit') - ) - - // deplete user's quota - const remainingQuota = expectedQuota - performedQueryCount - await db.transaction(async (trx) => { - for (let i = 0; i < remainingQuota; i++) { - await incrementQueryCount( - db, - ACCOUNTS_TABLE.ONCHAIN, - ACCOUNT_ADDRESS1, - rootLogger(_config.serviceName), - trx - ) - } - }) - // sanity check - expect( - await getPerformedQueryCount( - db, - ACCOUNTS_TABLE.ONCHAIN, - ACCOUNT_ADDRESS1, - rootLogger(_config.serviceName) - ) - ).toBe(expectedQuota) - - mockOdisPaymentsTotalPaidCUSD.mockImplementation(() => { - throw new Error('dummy error') - }) - - const req = getPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest( - req, - authorization, - SignerEndpoint.PNP_SIGN, - '1', - appWithFailOpenEnabled - ) - - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: expectedQuota + 1, // bc we depleted the user's quota above - totalQuota: Number.MAX_SAFE_INTEGER, - blockNumber: testBlockNumber, - warnings: [ErrorMessage.FAILURE_TO_GET_TOTAL_QUOTA, ErrorMessage.FULL_NODE_ERROR], - }) - - // check DB state: performedQueryCount was incremented and request was stored - expect( - await getPerformedQueryCount( - db, - ACCOUNTS_TABLE.ONCHAIN, - ACCOUNT_ADDRESS1, - rootLogger(config.serviceName) - ) - ).toBe(expectedQuota + 1) - expect( - await getRequestExists( - db, - REQUESTS_TABLE.ONCHAIN, - req.account, - req.blindedQueryPhoneNumber, - rootLogger(config.serviceName) - ) - ).toBe(true) - }) - - it('Should return 500 on blockchain totalQuota query failure when shouldFailOpen is false', async () => { - mockOdisPaymentsTotalPaidCUSD.mockImplementation(() => { - throw new Error('dummy error') - }) - - const req = getPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY - ) - - const configWithFailOpenDisabled: typeof _config = JSON.parse(JSON.stringify(_config)) - configWithFailOpenDisabled.api.phoneNumberPrivacy.shouldFailOpen = false - const appWithFailOpenDisabled = startSigner( - configWithFailOpenDisabled, - db, - keyProvider, - newKit('dummyKit') - ) - - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest( - req, - authorization, - SignerEndpoint.PNP_SIGN, - '1', - appWithFailOpenDisabled - ) - - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - performedQueryCount: performedQueryCount, - totalQuota: -1, - blockNumber: testBlockNumber, - error: ErrorMessage.FULL_NODE_ERROR, - }) - }) - - it('Should return 500 on failure to increment query count', async () => { - const logger = rootLogger(_config.serviceName) - const spy = jest - .spyOn( - jest.requireActual('../../src/common/database/wrappers/account'), - 'incrementQueryCount' - ) - .mockImplementationOnce(() => { - countAndThrowDBError(new Error(), logger, ErrorMessage.DATABASE_UPDATE_FAILURE) - }) - - const req = getPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.PNP_SIGN) - spy.mockRestore() - - expect.assertions(4) - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: ErrorMessage.DATABASE_UPDATE_FAILURE, - }) - - // check DB state: performedQueryCount was not incremented and request was not stored - expect( - await getPerformedQueryCount(db, ACCOUNTS_TABLE.ONCHAIN, ACCOUNT_ADDRESS1, logger) - ).toBe(performedQueryCount) - expect( - await getRequestExists( - db, - REQUESTS_TABLE.ONCHAIN, - req.account, - req.blindedQueryPhoneNumber, - logger - ) - ).toBe(false) - }) - - it('Should return 500 on failure to store request', async () => { - const logger = rootLogger(_config.serviceName) - const spy = jest - .spyOn(jest.requireActual('../../src/common/database/wrappers/request'), 'storeRequest') - .mockImplementationOnce(() => { - countAndThrowDBError(new Error(), logger, ErrorMessage.DATABASE_INSERT_FAILURE) - }) - - const req = getPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.PNP_SIGN) - spy.mockRestore() - - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - error: ErrorMessage.DATABASE_INSERT_FAILURE, - }) - - // check DB state: performedQueryCount was not incremented and request was not stored - expect( - await getPerformedQueryCount(db, ACCOUNTS_TABLE.ONCHAIN, ACCOUNT_ADDRESS1, logger) - ).toBe(performedQueryCount) - expect( - await getRequestExists( - db, - REQUESTS_TABLE.ONCHAIN, - req.account, - req.blindedQueryPhoneNumber, - logger - ) - ).toBe(false) - }) - - it('Should return 200 on failure to fetch DEK when shouldFailOpen is true', async () => { - mockGetDataEncryptionKey.mockImplementation(() => { - throw new Error() - }) - - const req = getPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.ENCRYPTION_KEY - ) - - const configWithFailOpenEnabled: typeof _config = JSON.parse(JSON.stringify(_config)) - configWithFailOpenEnabled.api.phoneNumberPrivacy.shouldFailOpen = true - const appWithFailOpenEnabled = startSigner( - configWithFailOpenEnabled, - db, - keyProvider, - newKit('dummyKit') - ) - - // NOT the dek private key, so authentication would fail if getDataEncryptionKey succeeded - const differentPk = '0x00000000000000000000000000000000000000000000000000000000ddddbbbb' - const authorization = getPnpRequestAuthorization(req, differentPk) - const res = await sendRequest( - req, - authorization, - SignerEndpoint.PNP_SIGN, - '1', - appWithFailOpenEnabled - ) - expect(res.status).toBe(200) - expect(res.body).toStrictEqual({ - success: true, - version: expectedVersion, - signature: expectedSignature, - performedQueryCount: performedQueryCount + 1, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - warnings: [ErrorMessage.FAILURE_TO_GET_DEK, ErrorMessage.FAILING_OPEN], - }) - }) - - it('Should return 500 on bls signing error', async () => { - const spy = jest - .spyOn(jest.requireActual('blind-threshold-bls'), 'partialSignBlindedMessage') - .mockImplementationOnce(() => { - throw new Error() - }) - - const req = getPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.PNP_SIGN) - - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - performedQueryCount: performedQueryCount, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - error: ErrorMessage.SIGNATURE_COMPUTATION_FAILURE, - }) - - spy.mockRestore() - - // check DB state: performedQueryCount was not incremented and request was not stored - expect( - await getPerformedQueryCount( - db, - ACCOUNTS_TABLE.ONCHAIN, - ACCOUNT_ADDRESS1, - rootLogger(_config.serviceName) - ) - ).toBe(performedQueryCount) - expect( - await getRequestExists( - db, - REQUESTS_TABLE.ONCHAIN, - req.account, - req.blindedQueryPhoneNumber, - rootLogger(_config.serviceName) - ) - ).toBe(false) - }) - - it('Should return 500 on generic error in sign', async () => { - const spy = jest - .spyOn( - jest.requireActual('../../src/common/bls/bls-cryptography-client'), - 'computeBlindedSignature' - ) - .mockImplementationOnce(() => { - // Trigger a generic error in .sign to trigger the default error returned. - throw new Error() - }) - - const req = getPnpSignRequest( - ACCOUNT_ADDRESS1, - BLINDED_PHONE_NUMBER, - AuthenticationMethod.WALLET_KEY - ) - const authorization = getPnpRequestAuthorization(req, PRIVATE_KEY1) - const res = await sendRequest(req, authorization, SignerEndpoint.PNP_SIGN) - - expect(res.status).toBe(500) - expect(res.body).toStrictEqual({ - success: false, - version: expectedVersion, - performedQueryCount: performedQueryCount, - totalQuota: expectedQuota, - blockNumber: testBlockNumber, - error: ErrorMessage.SIGNATURE_COMPUTATION_FAILURE, - }) - - spy.mockRestore() - - // check DB state: performedQueryCount was not incremented and request was not stored - expect( - await getPerformedQueryCount( - db, - ACCOUNTS_TABLE.ONCHAIN, - ACCOUNT_ADDRESS1, - rootLogger(config.serviceName) - ) - ).toBe(performedQueryCount) - expect( - await getRequestExists( - db, - REQUESTS_TABLE.ONCHAIN, - req.account, - req.blindedQueryPhoneNumber, - rootLogger(config.serviceName) - ) - ).toBe(false) - }) - }) - }) - }) -}) diff --git a/packages/phone-number-privacy/signer/test/key-management/aws-key-provider.test.ts b/packages/phone-number-privacy/signer/test/key-management/aws-key-provider.test.ts deleted file mode 100644 index ca870fb161b..00000000000 --- a/packages/phone-number-privacy/signer/test/key-management/aws-key-provider.test.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { AWSKeyProvider } from '../../src/common/key-management/aws-key-provider' -import { DefaultKeyName, Key } from '../../src/common/key-management/key-provider-base' - -const mockKey = '010101010101010101010101010101010101010101010101010101010101010101010101' -const mockResponse = { SecretString: `{"mockSecretKey":"${mockKey}"}` } -const mockEmptyMockSecretKeyResponse = { SecretString: `{"mockSecretKey":""}` } -const mockBinaryResponse = { SecretBinary: Buffer.from(mockKey).toString('base64') } -const mockInvalidResponse1 = { foo: 'bar' } -const mockInvalidResponse2 = { SecretString: 'totally not a json string' } - -const key: Key = { - name: DefaultKeyName.PHONE_NUMBER_PRIVACY, - version: 1, -} - -jest.mock('../../src/config', () => ({ - config: { - serviceName: 'odis-signer', - keystore: { - keys: { - phoneNumberPrivacy: { - name: 'phoneNumberPrivacy', - latest: 1, - }, - domains: { - name: 'domains', - latest: 1, - }, - }, - aws: { - region: 'mockRegion', - secretKey: 'mockSecretKey', - }, - }, - }, -})) - -const getSecretValue = jest.fn() - -jest.mock('aws-sdk', () => ({ - SecretsManager: jest.fn(() => ({ - config: { - update: jest.fn(), - }, - getSecretValue, - })), -})) - -describe('AWSKeyProvider', () => { - describe('with valid input', () => { - it('parses string keys correctly', async () => { - getSecretValue.mockReturnValue({ promise: jest.fn().mockResolvedValue(mockResponse) }) - - const provider = new AWSKeyProvider() - await provider.fetchPrivateKeyFromStore(key) - expect(provider.getPrivateKey(key)).toBe(mockKey) - }) - - it('parses binary keys correctly', async () => { - getSecretValue.mockReturnValue({ promise: jest.fn().mockResolvedValue(mockBinaryResponse) }) - - const provider = new AWSKeyProvider() - await provider.fetchPrivateKeyFromStore(key) - expect(provider.getPrivateKey(key)).toBe(mockKey) - }) - }) - - describe('with invalid input', () => { - it('invalid keys are properly handled', async () => { - getSecretValue.mockReturnValue({ - promise: jest - .fn() - .mockResolvedValueOnce(mockInvalidResponse1) - .mockResolvedValueOnce(mockInvalidResponse2), - }) - - const provider = new AWSKeyProvider() - expect.assertions(2) - await expect(provider.fetchPrivateKeyFromStore(key)).rejects.toThrow() - await expect(provider.fetchPrivateKeyFromStore(key)).rejects.toThrow() - }) - - it('empty key is handled correctly', async () => { - getSecretValue.mockReturnValue({ - promise: jest.fn().mockResolvedValue(mockEmptyMockSecretKeyResponse), - }) - - const provider = new AWSKeyProvider() - expect.assertions(1) - await expect(provider.fetchPrivateKeyFromStore(key)).rejects.toThrow() - }) - }) -}) diff --git a/packages/phone-number-privacy/signer/test/key-management/azure-key-provider.test.ts b/packages/phone-number-privacy/signer/test/key-management/azure-key-provider.test.ts deleted file mode 100644 index 393abcdc385..00000000000 --- a/packages/phone-number-privacy/signer/test/key-management/azure-key-provider.test.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { AzureKeyProvider } from '../../src/common/key-management/azure-key-provider' -import { DefaultKeyName, Key } from '../../src/common/key-management/key-provider-base' - -const mockKey = '030303030303030303030303030303030303030303030303030303030303030303030303' - -const key: Key = { - name: DefaultKeyName.PHONE_NUMBER_PRIVACY, - version: 1, -} - -jest.mock('../../src/config', () => ({ - config: { - serviceName: 'odis-signer', - keystore: { - keys: { - phoneNumberPrivacy: { - name: 'phoneNumberPrivacy', - latest: 1, - }, - domains: { - name: 'domains', - latest: 1, - }, - }, - azure: { - clientID: 'mockClientID', - clientSecret: 'mockClientSecret', - tenant: 'mockTenant', - vaultName: 'mockVaultName', - }, - }, - }, -})) - -const getSecret = jest.fn() - -jest.mock('@celo/wallet-hsm-azure', () => ({ - AzureKeyVaultClient: jest.fn(() => ({ getSecret })), -})) - -describe('AzureKeyProvider', () => { - it('parses keys correctly', async () => { - getSecret.mockResolvedValue(mockKey) - - const provider = new AzureKeyProvider() - await provider.fetchPrivateKeyFromStore(key) - expect(provider.getPrivateKey(key)).toBe(mockKey) - }) - - it('handles exceptions correctly', async () => { - getSecret.mockRejectedValue(new Error('Secret retrieval exception')) - - const provider = new AzureKeyProvider() - expect.assertions(1) - await expect(provider.fetchPrivateKeyFromStore(key)).rejects.toThrow() - }) -}) diff --git a/packages/phone-number-privacy/signer/test/key-management/google-key-provider.test.ts b/packages/phone-number-privacy/signer/test/key-management/google-key-provider.test.ts deleted file mode 100644 index 284d6ffb431..00000000000 --- a/packages/phone-number-privacy/signer/test/key-management/google-key-provider.test.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { GoogleKeyProvider } from '../../src/common/key-management/google-key-provider' -import { DefaultKeyName, Key } from '../../src/common/key-management/key-provider-base' - -const mockKey = '020202020202020202020202020202020202020202020202020202020202020202020202' -const mockResponse = [{ payload: { data: `${mockKey}` } }] -const emptyMockResponse = [{ payload: {} }] -const invalidMockResponse = [{ payload: { data: '123' } }] -const key: Key = { - name: DefaultKeyName.PHONE_NUMBER_PRIVACY, - version: 1, -} - -jest.mock('../../src/config', () => ({ - config: { - serviceName: 'odis-signer', - keystore: { - keys: { - phoneNumberPrivacy: { - name: 'phoneNumberPrivacy', - latest: 1, - }, - domains: { - name: 'domains', - latest: 1, - }, - }, - google: { - projectId: 'mockProject', - }, - }, - }, -})) - -const accessSecretVersion = jest.fn() - -jest.mock('@google-cloud/secret-manager/build/src/v1', () => ({ - SecretManagerServiceClient: jest.fn(() => ({ accessSecretVersion })), -})) - -describe('GoogleKeyProvider', () => { - it('parses keys correctly', async () => { - accessSecretVersion.mockResolvedValue(mockResponse) - - const provider = new GoogleKeyProvider() - await provider.fetchPrivateKeyFromStore(key) - expect(provider.getPrivateKey(key)).toBe(mockKey) - }) - - it('handles errors correctly', async () => { - accessSecretVersion.mockResolvedValue(emptyMockResponse) - - const provider = new GoogleKeyProvider() - expect.assertions(1) - await expect(provider.fetchPrivateKeyFromStore(key)).rejects.toThrow() - }) - - it('unitialized provider throws', () => { - accessSecretVersion.mockResolvedValue(mockResponse) - - const provider = new GoogleKeyProvider() - expect.assertions(1) - expect(() => provider.getPrivateKey(key)).toThrow() - }) - - it('set invalid private key throws', async () => { - accessSecretVersion.mockResolvedValue(invalidMockResponse) - - const provider = new GoogleKeyProvider() - expect.assertions(1) - await expect(provider.fetchPrivateKeyFromStore(key)).rejects.toThrow() - }) -}) diff --git a/packages/phone-number-privacy/signer/test/signing/bls-signature.test.ts b/packages/phone-number-privacy/signer/test/signing/bls-signature.test.ts deleted file mode 100644 index 75a26a3da85..00000000000 --- a/packages/phone-number-privacy/signer/test/signing/bls-signature.test.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { rootLogger, TestUtils } from '@celo/phone-number-privacy-common' -import threshold_bls from 'blind-threshold-bls' -import { computeBlindedSignature } from '../../src/common/bls/bls-cryptography-client' -import { config } from '../../src/config' - -describe(`BLS service computes signature`, () => { - it('provides blinded signature', async () => { - const message = Buffer.from('hello world') - const userSeed = new Uint8Array(32) - for (let i = 0; i < userSeed.length - 1; i++) { - userSeed[i] = i - } - - const blindedMsgResult = threshold_bls.blind(message, userSeed) - const blindedMsg = Buffer.from(blindedMsgResult.message).toString('base64') - - const actual = computeBlindedSignature( - blindedMsg, - TestUtils.Values.PNP_DEV_SIGNER_PRIVATE_KEY, - rootLogger(config.serviceName) - ) - expect(actual).toEqual( - 'MAAAAAAAAADDilSaA/xvbtE4NV3agMzHIf8PGPQ83Cu8gQy5E2mRWyUIges8bjE4EBe1L7pcY4AAAAAA' - ) - - expect( - threshold_bls.partialVerifyBlindSignature( - Buffer.from(TestUtils.Values.PNP_DEV_ODIS_POLYNOMIAL, 'hex'), - blindedMsgResult.message, - Buffer.from(actual, 'base64') - ) - ) - - const combinedSignature = threshold_bls.combine(1, Buffer.from(actual, 'base64')) - const unblindedSignedMessage = threshold_bls.unblind( - combinedSignature, - blindedMsgResult.blindingFactor - ) - const publicKey = Buffer.from(TestUtils.Values.PNP_DEV_ODIS_PUBLIC_KEY, 'base64') - expect(threshold_bls.verify(publicKey, message, unblindedSignedMessage)) - }) - - it('invalid blind message throws an error', async () => { - const blindedMsg = Buffer.from('invalid blinded message').toString('base64') - - expect.assertions(1) - expect(() => - computeBlindedSignature( - blindedMsg, - TestUtils.Values.PNP_DEV_SIGNER_PRIVATE_KEY, - rootLogger(config.serviceName) - ) - ).toThrow() - }) -}) diff --git a/packages/phone-number-privacy/signer/tsconfig.json b/packages/phone-number-privacy/signer/tsconfig.json deleted file mode 100644 index 73b06e83a5b..00000000000 --- a/packages/phone-number-privacy/signer/tsconfig.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "compilerOptions": { - "plugins": [ - { - "name": "typescript-tslint-plugin" - } - ], - "lib": ["es2017", "ES2020.Promise"], - "module": "commonjs", - "strict": true, - "allowJs": false, - "allowSyntheticDefaultImports": true, - "esModuleInterop": true, - "sourceMap": true, - "declaration": true, - "target": "es2017", - "rootDir": "src", - "outDir": "./dist", - "skipLibCheck": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "preserveConstEnums": true, - "composite": true - }, - "include": ["src", "index.d.ts"], - "compileOnSave": true -} diff --git a/packages/phone-number-privacy/signer/tslint.json b/packages/phone-number-privacy/signer/tslint.json deleted file mode 100644 index 5fc86ecb716..00000000000 --- a/packages/phone-number-privacy/signer/tslint.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": ["@celo/typescript/tslint.json"], - "rules": { - "no-global-arrow-functions": false, - "no-console": true - } -} diff --git a/packages/protocol/CHANGELOG.md b/packages/protocol/CHANGELOG.md new file mode 100644 index 00000000000..ce9b97f046c --- /dev/null +++ b/packages/protocol/CHANGELOG.md @@ -0,0 +1,27 @@ +# @celo/protocol + +## 1.0.1 + +### Patch Changes + +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [53bbd4958] + - @celo/connect@5.1.0 + - @celo/wallet-local@5.1.0 + - @celo/cryptographic-utils@5.0.5 + - @celo/utils@5.0.5 + - @celo/base@5.0.5 + +## 1.0.1-beta.0 + +### Patch Changes + +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [53bbd4958] + - @celo/connect@5.1.0-beta.0 + - @celo/wallet-local@5.1.0-beta.0 + - @celo/cryptographic-utils@5.0.5-beta.0 + - @celo/utils@5.0.5-beta.0 + - @celo/base@5.0.5-beta.0 diff --git a/packages/protocol/contracts/governance/LockedGold.sol b/packages/protocol/contracts/governance/LockedGold.sol index b148d33eac2..e85f0cd3d82 100755 --- a/packages/protocol/contracts/governance/LockedGold.sol +++ b/packages/protocol/contracts/governance/LockedGold.sol @@ -122,7 +122,7 @@ contract LockedGold is * @return Patch version of the contract. */ function getVersionNumber() external pure returns (uint256, uint256, uint256, uint256) { - return (1, 1, 3, 0); + return (1, 1, 4, 0); } /** @@ -769,6 +769,15 @@ contract LockedGold is return (pendingWithdrawal.value, pendingWithdrawal.timestamp); } + /** + * @notice Returns the number of pending withdrawals for the specified account. + * @param account The address of the account. + * @return The count of pending withdrawals. + */ + function getTotalPendingWithdrawalsCount(address account) external view returns (uint256) { + return balances[account].pendingWithdrawals.length; + } + /** * @notice Returns the total amount to withdraw from unlocked gold for an account. * @param account The address of the account. diff --git a/packages/protocol/lib/signing-utils.ts b/packages/protocol/lib/signing-utils.ts index 6f470c1fa37..64e5b143fa9 100644 --- a/packages/protocol/lib/signing-utils.ts +++ b/packages/protocol/lib/signing-utils.ts @@ -1,28 +1,12 @@ // Originally taken from https://github.com/ethereum/web3.js/blob/1.x/packages/web3-eth-accounts/src/index.js -import { inputCeloTxFormatter } from '@celo/connect/lib/utils/formatter' import { parseSignature } from '@celo/utils/lib/signatureUtils' -import { account as Account, bytes, hash, nat, RLP } from 'eth-lib' -import _ from 'underscore' +import { privateKeyToAddress } from '@celo/utils/lib/address' +import { LocalWallet } from '@celo/wallet-local' import Web3 from 'web3' -import { numberToHex } from 'web3-utils' function isNot(value: any) { - return _.isUndefined(value) || _.isNull(value) -} - -function trimLeadingZero(hex: string) { - while (hex && hex.startsWith('0x0')) { - hex = '0x' + hex.slice(3) - } - return hex -} - -function makeEven(hex: string) { - if (hex.length % 2 === 1) { - hex = hex.replace('0x', '0x0') - } - return hex + return value === null || value === undefined } export const getParsedSignatureOfAddress = async (web3: Web3, address: string, signer: string) => { @@ -32,13 +16,12 @@ export const getParsedSignatureOfAddress = async (web3: Web3, address: string, s } export async function signTransaction(web3: Web3, txn: any, privateKey: string) { - let result: any if (!txn) { throw new Error('No transaction object given!') } - const signed = (tx: any) => { + const signed = async (tx: any) => { if (!tx.gas && !tx.gasLimit) { throw new Error('"gas" is missing') } @@ -46,62 +29,17 @@ export async function signTransaction(web3: Web3, txn: any, privateKey: string) if (tx.nonce < 0 || tx.gas < 0 || tx.gasPrice < 0 || tx.chainId < 0) { throw new Error('Gas, gasPrice, nonce or chainId is lower than 0') } - try { - tx = inputCeloTxFormatter(tx) + const wallet = new LocalWallet() - const transaction = tx - transaction.to = tx.to || '0x' - transaction.data = tx.data || '0x' - transaction.value = tx.value || '0x' - transaction.chainId = numberToHex(tx.chainId) - transaction.feeCurrency = tx.feeCurrency || '0x' - transaction.gatewayFeeRecipient = tx.gatewayFeeRecipient || '0x' - transaction.gatewayFee = tx.gatewayFee || '0x' + wallet.addAccount(privateKey) - const rlpEncoded = RLP.encode([ - bytes.fromNat(transaction.nonce), - bytes.fromNat(transaction.gasPrice), - bytes.fromNat(transaction.gas), - transaction.feeCurrency.toLowerCase(), - transaction.gatewayFeeRecipient.toLowerCase(), - bytes.fromNat(transaction.gatewayFee), - transaction.to.toLowerCase(), - bytes.fromNat(transaction.value), - transaction.data, - bytes.fromNat(transaction.chainId || '0x1'), - '0x', - '0x', - ]) + return wallet.signTransaction(tx) - const messageHash = hash.keccak256(rlpEncoded) - - const signature = Account.makeSigner(nat.toNumber(transaction.chainId || '0x1') * 2 + 35)( - hash.keccak256(rlpEncoded), - privateKey - ) - - const rawTx = RLP.decode(rlpEncoded).slice(0, 9).concat(Account.decodeSignature(signature)) - - rawTx[9] = makeEven(trimLeadingZero(rawTx[9])) - rawTx[10] = makeEven(trimLeadingZero(rawTx[10])) - rawTx[11] = makeEven(trimLeadingZero(rawTx[11])) - - const rawTransaction = RLP.encode(rawTx) - - const values = RLP.decode(rawTransaction) - result = { - messageHash, - v: trimLeadingZero(values[9]), - r: trimLeadingZero(values[10]), - s: trimLeadingZero(values[11]), - rawTransaction, - } } catch (e) { + console.info('Error signing transaction', e) throw e } - - return result } // Resolve immediately if nonce, chainId and price are provided @@ -110,10 +48,10 @@ export async function signTransaction(web3: Web3, txn: any, privateKey: string) } // Otherwise, get the missing info from the Ethereum Node - const chainId = isNot(txn.chainId) ? await web3.eth.net.getId() : txn.chainId + const chainId = isNot(txn.chainId) ? await web3.eth.getChainId() : txn.chainId const gasPrice = isNot(txn.gasPrice) ? await web3.eth.getGasPrice() : txn.gasPrice const nonce = isNot(txn.nonce) - ? await web3.eth.getTransactionCount(Account.fromPrivate(privateKey).address) + ? await web3.eth.getTransactionCount(privateKeyToAddress(privateKey)) : txn.nonce if (isNot(chainId) || isNot(gasPrice) || isNot(nonce)) { @@ -122,5 +60,5 @@ export async function signTransaction(web3: Web3, txn: any, privateKey: string) JSON.stringify({ chainId, gasPrice, nonce }) ) } - return signed(_.extend(txn, { chainId, gasPrice, nonce })) + return signed({...txn, chainId, gasPrice, nonce }) } diff --git a/packages/protocol/lib/test-utils.ts b/packages/protocol/lib/test-utils.ts index c981d4d041d..51998bf1ebd 100644 --- a/packages/protocol/lib/test-utils.ts +++ b/packages/protocol/lib/test-utils.ts @@ -1,4 +1,3 @@ - import { ArtifactsSingleton } from '@celo/protocol/lib/artifactsSingleton'; import { hasEntryInRegistry, usesRegistry } from '@celo/protocol/lib/registry-utils'; import { getParsedSignatureOfAddress } from '@celo/protocol/lib/signing-utils'; diff --git a/packages/protocol/lib/web3-utils.ts b/packages/protocol/lib/web3-utils.ts index 947994641bd..b72b9d5cae0 100644 --- a/packages/protocol/lib/web3-utils.ts +++ b/packages/protocol/lib/web3-utils.ts @@ -1,21 +1,21 @@ /* tslint:disable:no-console */ // TODO(asa): Refactor and rename to 'deployment-utils.ts' -import { Address, CeloTxObject } from '@celo/connect'; -import { setAndInitializeImplementation } from '@celo/protocol/lib/proxy-utils'; -import { CeloContractName } from '@celo/protocol/lib/registry-utils'; -import { signTransaction } from '@celo/protocol/lib/signing-utils'; -import { privateKeyToAddress } from '@celo/utils/lib/address'; -import { BuildArtifacts } from '@openzeppelin/upgrades'; -import { BigNumber } from 'bignumber.js'; - -import { createInterfaceAdapter } from '@truffle/interface-adapter'; -import path from 'path'; -import prompts from 'prompts'; -import { GoldTokenInstance, MultiSigInstance, OwnableInstance, ProxyContract, ProxyInstance, RegistryInstance } from 'types'; -import { StableTokenInstance } from 'types/mento'; -import Web3 from 'web3'; -import { ContractPackage } from '../contractPackages'; -import { ArtifactsSingleton } from './artifactsSingleton'; +import { Address, CeloTxObject } from '@celo/connect' +import { setAndInitializeImplementation } from '@celo/protocol/lib/proxy-utils' +import { CeloContractName } from '@celo/protocol/lib/registry-utils' +import { signTransaction } from '@celo/protocol/lib/signing-utils' +import { privateKeyToAddress } from '@celo/utils/lib/address' +import { BuildArtifacts } from '@openzeppelin/upgrades' +import { createInterfaceAdapter } from '@truffle/interface-adapter' +import { BigNumber } from 'bignumber.js' +import path from 'path' +import prompts from 'prompts' +import { GoldTokenInstance, MultiSigInstance, OwnableInstance, ProxyContract, ProxyInstance, RegistryInstance } from 'types' +import { StableTokenInstance } from 'types/mento' +import Web3 from 'web3' +import { ContractPackage } from '../contractPackages' +import { ArtifactsSingleton } from './artifactsSingleton' + const truffleContract = require('@truffle/contract'); @@ -39,8 +39,7 @@ export async function sendTransactionWithPrivateKey( from: address, }) } - - const signedTx: any = await signTransaction( + const signedTx = await signTransaction( web3, { ...txArgs, @@ -53,7 +52,7 @@ export async function sendTransactionWithPrivateKey( privateKey ) - const rawTransaction = signedTx.rawTransaction.toString('hex') + const rawTransaction = signedTx.raw return web3.eth.sendSignedTransaction(rawTransaction) } @@ -267,7 +266,7 @@ export function deploymentForProxiedContract { const artifact = require(`${path.join(__dirname, "..")}/build/contracts-${contractPath}/${contractName}.json`) @@ -275,11 +274,11 @@ export const makeTruffleContractForMigrationWithoutSingleton = (contractName: st abi: artifact.abi, unlinked_binary: artifact.bytecode, }) - - + + Contract.setProvider(web3.currentProvider) - Contract.setNetwork(network.name) - + Contract.setNetwork(network.network_id) + Contract.interfaceAdapter = createInterfaceAdapter({ networkType: "ethereum", provider: web3.currentProvider @@ -287,6 +286,7 @@ export const makeTruffleContractForMigrationWithoutSingleton = (contractName: st Contract.configureNetwork({networkType: "ethereum", provider: web3.currentProvider}) Contract.defaults({from: network.from, gas: network.gas}) + return Contract } @@ -309,7 +309,7 @@ export function deploymentForContract Started deployment for", name) - let Contract + let Contract let ContractProxy if (artifactPath) { Contract = makeTruffleContractForMigration(name, artifactPath, web3) @@ -328,7 +328,7 @@ export function deploymentForContract { console.log("\n-> Deploying", name) @@ -447,12 +447,12 @@ export function getFunctionSelectorsForContract(contract: any, contractName: str export function checkImports(baseContractName: string, derivativeContractArtifact: any, artifacts: any) { const isImport = (astNode: any) => astNode.nodeType === 'ImportDirective' const imports: any[] = derivativeContractArtifact.ast.nodes.filter((astNode: any) => isImport(astNode)) - while (imports.length) { // BFS + while (imports.length) { // BFS const importedContractName = (imports.pop().file as string).split('/').pop().split('.')[0] if (importedContractName === baseContractName) { return true } - const importedContractArtifact = artifacts instanceof BuildArtifacts ? + const importedContractArtifact = artifacts instanceof BuildArtifacts ? artifacts.getArtifactByName(importedContractName) : artifacts.require(importedContractName) imports.unshift(...importedContractArtifact.ast.nodes.filter((astNode: any) => isImport(astNode))) diff --git a/packages/protocol/migrations_ts/00_initial_migration.ts b/packages/protocol/migrations_ts/00_initial_migration.ts index 0b8f5c1d515..735072f14cc 100644 --- a/packages/protocol/migrations_ts/00_initial_migration.ts +++ b/packages/protocol/migrations_ts/00_initial_migration.ts @@ -1,3 +1,4 @@ +/* tslint:disable no-console */ import { ArtifactsSingleton } from '../lib/artifactsSingleton' import { networks } from '../truffle-config.js' @@ -7,7 +8,6 @@ module.exports = async (deployer: any, network: any) => { const currentNetwork = { ...networks[network], name: network } - // tslint:disable-next-line console.log('Current network is', JSON.stringify(currentNetwork)) // Instad of setting this in a singleton, it could have been set in every migration // but it would have required quite a lot of refactoring diff --git a/packages/protocol/migrations_ts/28_governance.ts b/packages/protocol/migrations_ts/28_governance.ts index 18abd17f827..1410c95fa56 100644 --- a/packages/protocol/migrations_ts/28_governance.ts +++ b/packages/protocol/migrations_ts/28_governance.ts @@ -12,6 +12,7 @@ import { config } from '@celo/protocol/migrationsConfig' import { toFixed } from '@celo/utils/lib/fixidity' import { GovernanceApproverMultiSigInstance, GovernanceInstance } from 'types' import { MENTO_PACKAGE, SOLIDITY_08_PACKAGE } from '../contractPackages' + import { ArtifactsSingleton } from '../lib/artifactsSingleton' const initializeArgs = async (networkName: string): Promise => { diff --git a/packages/protocol/migrations_ts/29_elect_validators.ts b/packages/protocol/migrations_ts/29_elect_validators.ts index 844aa628ad3..742fff47910 100644 --- a/packages/protocol/migrations_ts/29_elect_validators.ts +++ b/packages/protocol/migrations_ts/29_elect_validators.ts @@ -105,7 +105,7 @@ async function registerValidatorGroup( // Add a premium to cover tx fees const v = lockedGoldValue.times(1.01).integerValue() - console.info(` - send funds ${v} to group address ${account.address}`) + console.info(` - send funds ${v} to group address ${account.address}}`) await sendTransaction(web3, null, privateKey, { to: account.address, value: v, diff --git a/packages/protocol/package.json b/packages/protocol/package.json index da036b43dcd..154d40becb2 100644 --- a/packages/protocol/package.json +++ b/packages/protocol/package.json @@ -1,6 +1,6 @@ { "name": "@celo/protocol", - "version": "1.0.0", + "version": "1.0.1", "private": true, "main": "index.ts", "author": "Celo", @@ -16,11 +16,11 @@ "test:devchain-release": "./scripts/bash/release-on-devchain.sh", "test:release-snapshots": "./scripts/bash/release-snapshots.sh", "test:generate-old-devchain-and-build": "./scripts/bash/generate-old-devchain-and-build.sh", - "build:ts": "rm -f migrations/*.js* && ts-node ./scripts/build.ts --truffleTypes ./types/typechain && tsc -b && mv migrations_ts/*.js* migrations", + "build:ts": "rm -f migrations/*.js* && ts-node --preferTsExts ./scripts/build.ts --truffleTypes ./types/typechain && tsc -b && mv migrations_ts/*.js* migrations", "gas": "yarn run test --gas", "pull-submodules": "git submodule update --init --recursive", - "build:sol": "git submodule update --init --recursive && mkdir -p migrations && ts-node ./scripts/build.ts --solidity ${BUILD_DIR:-./build}", - "build": "yarn pull-submodules && yarn build:sol && yarn build:ts", + "build:sol": "yarn pull-submodules && mkdir -p migrations && ts-node --preferTsExts ./scripts/build.ts --solidity ${BUILD_DIR:-./build}", + "build": "yarn build:sol && yarn build:ts", "sourcify-publish": "ts-node ./scripts/sourcify-publish.ts", "migrate": "./scripts/bash/migrate.sh", "set_block_gas_limit": "./scripts/bash/set_block_gas_limit.sh", @@ -50,29 +50,34 @@ "@0x/sol-profiler": "^4.1.37", "@0x/sol-trace": "^3.0.47", "@0x/subproviders": "^7.0.1", - "@celo/base": "4.1.1-dev", + "@celo/base": "^5.0.5", "@celo/bls12377js": "0.1.1", - "@celo/connect": "4.1.1-dev", - "@celo/cryptographic-utils": "4.1.1-dev", - "@celo/utils": "4.1.1-dev", + "@celo/connect": "^5.1.0", + "@celo/cryptographic-utils": "^5.0.5", + "@celo/utils": "^5.0.5", + "@celo/wallet-local": "^5.1.0", "@ethereumjs/util": "8.0.5", "@ethereumjs/vm": "npm:@soloseng/ethereumjs-vm@6.4.1", "@ganache/console.log": "0.3.0", "@openzeppelin/contracts8": "npm:@openzeppelin/contracts@^4.4.2", "@openzeppelin/upgrades": "^2.8.0", + "@summa-tx/memview.sol": "^1.1.0", "@truffle/artifactor": "4.0.180", "@truffle/contract": "4.6.10", "@truffle/resolver": "9.0.27", "bignumber.js": "9.1.0", "bip39": "https://github.com/bitcoinjs/bip39#d8ea080a18b40f301d4e2219a2991cd2417e83c2", + "bn.js": "^5.1.0", "chai": "^4.3.6", "chai-subset": "^1.6.0", + "chalk": "^2.4.2", "csv-parser": "^2.0.0", "csv-stringify": "^4.3.1", "elliptic": "^6.5.4", "ethereum-cryptography": "1.2.0", "ethereumjs-abi": "^0.6.8", "ethereumjs-wallet": "^0.6.3", + "form-data": "^3.0.0", "fs-extra": "^5.0.0", "ganache": "npm:@soloseng/ganache@7.8.0-beta.1", "glob-fs": "^0.1.7", @@ -80,7 +85,7 @@ "j6": "^1.0.2", "lodash": "^4.17.21", "mathjs": "^5.0.4", - "minimist": "~1.2.7", + "minimist": "^1.2.0", "node-fetch": "^2.6.9", "openzeppelin-solidity": "^2.5.0", "prompts": "^2.0.1", @@ -96,18 +101,17 @@ "web3-utils": "1.10.0" }, "devDependencies": { - "@celo/flake-tracker": "0.0.1-dev", - "@celo/phone-utils": "4.1.1-dev", + "@celo/phone-utils": "^5.0.5", "@celo/typechain-target-web3-v1-celo": "0.2.0", "@celo/typescript": "0.0.1", "@types/bn.js": "^5.1.0", "@types/chai": "^4.1.3", "@types/chai-subset": "1.3.3", + "@types/lodash": "^4.14.199", "@types/mathjs": "^4.4.1", "@types/mocha": "^7.0.2", "@types/targz": "^1.0.0", "@types/tmp": "^0.1.0", - "@types/underscore": "^1.8.8", "@types/yargs": "^13.0.2", "cross-env": "^5.1.6", "eth-gas-reporter": "^0.2.16", @@ -115,7 +119,8 @@ "targz": "^1.0.1", "tmp": "^0.1.0", "truffle-typings": "^1.0.6", - "ts-node": "8.3.0", + "ts-generator": "^0.0.8", + "ts-node": "^10.9.1", "typechain": "1.0.5", "typechain-target-truffle": "1.0.2", "yargs": "^14.0.0" diff --git a/packages/protocol/releaseData/initializationData/release11.json b/packages/protocol/releaseData/initializationData/release11.json new file mode 100644 index 00000000000..9e26dfeeb6e --- /dev/null +++ b/packages/protocol/releaseData/initializationData/release11.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/protocol/scripts/bash/check-versions.sh b/packages/protocol/scripts/bash/check-versions.sh index bcfa619044c..5c712d5c833 100755 --- a/packages/protocol/scripts/bash/check-versions.sh +++ b/packages/protocol/scripts/bash/check-versions.sh @@ -48,10 +48,16 @@ echo " - Checkout migrationsConfig.js at $NEW_BRANCH" CURRENT_HASH=`git log -n 1 --oneline | cut -c 1-9` git checkout $NEW_BRANCH -- migrationsConfig.js +echo 1 + yarn ts-node scripts/check-backward.ts sem_check \ --old_contracts $OLD_BRANCH_BUILD_DIR/contracts \ --new_contracts $NEW_BRANCH_BUILD_DIR/contracts \ --exclude $CONTRACT_EXCLUSION_REGEX \ $REPORT_FLAG +echo 2 + git checkout $CURRENT_HASH -- migrationsConfig.js + +echo 3 \ No newline at end of file diff --git a/packages/protocol/scripts/bash/contract-exclusion-regex.sh b/packages/protocol/scripts/bash/contract-exclusion-regex.sh index 774ae2fcad7..6dfa91c214e 100644 --- a/packages/protocol/scripts/bash/contract-exclusion-regex.sh +++ b/packages/protocol/scripts/bash/contract-exclusion-regex.sh @@ -1,9 +1,15 @@ +#!/usr/bin/env bash +set -euo pipefail + # Exclude test contracts, mock contracts, contract interfaces, Proxy contracts, inlined libraries, # MultiSig contracts, and the ReleaseGold contract. CONTRACT_EXCLUSION_REGEX=".*Test|Mock.*|I[A-Z].*|.*Proxy|MultiSig.*|ReleaseGold|SlasherUtil|UsingPrecompiles" +echo "old branch is $OLD_BRANCH" + # Before CR7, UsingRegistry and UsingRegistryV2 had been deployed, they need to keep getting deployed to keep the release reports without changes. VERSION_NUMBER=$(echo "$OLD_BRANCH" | tr -dc '0-9') +echo "version number is $VERSION_NUMBER" if [ $VERSION_NUMBER -gt 6 ] then @@ -24,3 +30,12 @@ if [ $VERSION_NUMBER -gt 9 ] then CONTRACT_EXCLUSION_REGEX="$CONTRACT_EXCLUSION_REGEX|SortedOracles" fi + +# TODO remove this after merge by fixing the report creation scipt to include GasPriceMinimum +if [ $VERSION_NUMBER -gt 9 ] + then + CONTRACT_EXCLUSION_REGEX="$CONTRACT_EXCLUSION_REGEX|GasPriceMinimum" +fi + +echo "CONTRACT_EXCLUSION_REGEX" +echo "$CONTRACT_EXCLUSION_REGEX" \ No newline at end of file diff --git a/packages/protocol/scripts/bash/ganache_devchain.sh b/packages/protocol/scripts/bash/ganache_devchain.sh index 0913a289ea0..174ad8adf0a 100755 --- a/packages/protocol/scripts/bash/ganache_devchain.sh +++ b/packages/protocol/scripts/bash/ganache_devchain.sh @@ -22,4 +22,4 @@ yarn run ganache \ --chain.allowUnlimitedContractSize=true \ --chain.chainId=1 \ --chain.hardfork='istanbul' \ - --database.dbPath=$DATA_DIR + --database.dbPath=$DATA_DIR \ diff --git a/packages/protocol/scripts/bash/generate-old-devchain-and-build.sh b/packages/protocol/scripts/bash/generate-old-devchain-and-build.sh index eabe49dcd25..739d3a7f20a 100755 --- a/packages/protocol/scripts/bash/generate-old-devchain-and-build.sh +++ b/packages/protocol/scripts/bash/generate-old-devchain-and-build.sh @@ -30,18 +30,21 @@ done [ -z "$BUILD_DIR" ] && BUILD_DIR=$(echo build/$(echo $BRANCH | sed -e 's/\//_/g')); echo "- Checkout source code at $BRANCH" -git fetch origin +"$BRANCH" 2>>$LOG_FILE >> $LOG_FILE -git checkout $BRANCH 2>>$LOG_FILE >> $LOG_FILE +# Fetching also tags so we can checkout if $BRACH references a tag +git fetch origin +"$BRANCH" --tags --force >> $LOG_FILE 2>&1 +git checkout $BRANCH >> $LOG_FILE 2>&1 echo "- Build monorepo (contract artifacts, migrations, + all dependencies)" cd ../.. # Using `yarn reset` to remove node_modules before re-installing using the node version of # the previous release branch. This is useful when node version between branches are incompatible -yarn run reset >> $LOG_FILE + +# yarn run reset >> $LOG_FILE # commented as may not be neede after everything is on node 18 + # build entire monorepo to account for any required dependencies. yarn install >> $LOG_FILE -yarn run clean >> $LOG_FILE +# yarn run clean >> $LOG_FILE # in release v8 and earlier, @celo/contractkit automatically uses set RELEASE_TAG # when building, which fails if this differs from `package/protocol`'s build directory. RELEASE_TAG="" yarn build >> $LOG_FILE @@ -54,7 +57,7 @@ else yarn devchain generate-tar "$PWD/devchain.tar.gz" --release_gold_contracts $GRANTS_FILE >> $LOG_FILE fi rm -rf $BUILD_DIR && mkdir -p $BUILD_DIR -mv build/contracts $BUILD_DIR +mv build/contracts* $BUILD_DIR mv "$PWD/devchain.tar.gz" $BUILD_DIR/. -git checkout - +git checkout - \ No newline at end of file diff --git a/packages/protocol/scripts/bash/release-on-devchain.sh b/packages/protocol/scripts/bash/release-on-devchain.sh index 4a862354b6f..f49349acc38 100755 --- a/packages/protocol/scripts/bash/release-on-devchain.sh +++ b/packages/protocol/scripts/bash/release-on-devchain.sh @@ -32,9 +32,19 @@ then BUILD_DIR=$(echo build/$(echo $BRANCH | sed -e 's/\//_/g')) fi + +rm -rf build/contracts* +cd ../.. +yarn run reset +yarn install >> $LOG_FILE +yarn build >> $LOG_FILE +cd packages/protocol + echo "- Run local network" yarn devchain run-tar-in-bg packages/protocol/$BUILD_DIR/devchain.tar.gz >> $LOG_FILE +sleep 60 + GANACHE_PID= if command -v lsof; then GANACHE_PID=`lsof -i tcp:8545 | tail -n 1 | awk '{print $2}'` @@ -43,9 +53,7 @@ fi echo "- Verify bytecode of the network" -rm -r build/contracts* - -yarn build >> $LOG_FILE +# yarn build >> $LOG_FILE yarn run truffle exec ./scripts/truffle/verify-bytecode.js --network development --build_artifacts $BUILD_DIR/contracts --branch $BRANCH --librariesFile libraries.json echo "- Check versions of current branch" @@ -56,13 +64,22 @@ echo " - Base commit $BASE_COMMIT" echo " - Checkout migrationsConfig.js at $BRANCH" git checkout $BRANCH -- migrationsConfig.js -OLD_BRANCH=$BUILD_DIR +OLD_BRANCH=$BRANCH source scripts/bash/contract-exclusion-regex.sh yarn ts-node scripts/check-backward.ts sem_check --old_contracts $BUILD_DIR/contracts --new_contracts build/contracts --exclude $CONTRACT_EXCLUSION_REGEX --output_file report.json echo "Undo checkout for migrationsConfig.js from $(git rev-parse HEAD) to $BASE_COMMIT" git checkout - -- migrationsConfig.js +# restart Ganache + +# kill $GANACHE_PID +# yarn devchain run-tar-in-bg packages/protocol/$BUILD_DIR/devchain.tar.gz >> $LOG_FILE +# if command -v lsof; then +# GANACHE_PID=`lsof -i tcp:8545 | tail -n 1 | awk '{print $2}'` +# echo "Network started with PID $GANACHE_PID, if exit 1, you will need to manually stop the process" +# fi + # From make-release.sh echo "- Deploy release of current branch" INITIALIZATION_FILE=`ls releaseData/initializationData/release*.json | sort -V | tail -n 1 | xargs realpath` diff --git a/packages/protocol/scripts/check-backward.ts b/packages/protocol/scripts/check-backward.ts index 239c9e3da10..3a0d7925a16 100644 --- a/packages/protocol/scripts/check-backward.ts +++ b/packages/protocol/scripts/check-backward.ts @@ -50,6 +50,7 @@ const argv = yargs .strict().argv const oldArtifactsFolder = path.relative(process.cwd(), argv.old_contracts) +// const oldArtifactsFolder08 = path.relative(process.cwd(), argv.old_contracts + '-0.8') const newArtifactsFolder = path.relative(process.cwd(), argv.new_contracts) const newArtifactsFolder08 = path.relative(process.cwd(), argv.new_contracts + '-0.8') const newArtifactsFolders = [newArtifactsFolder, newArtifactsFolder08] @@ -62,7 +63,9 @@ const out = (msg: string, force?: boolean): void => { const outFile = argv.output_file ? argv.output_file : tmp.tmpNameSync({}) const exclude: RegExp = argv.exclude ? new RegExp(argv.exclude) : null +// TODO generalize this with all the package const oldArtifacts = instantiateArtifacts(oldArtifactsFolder) +// const oldArtifacts08 = instantiateArtifacts(oldArtifactsFolder08) const newArtifacts = instantiateArtifacts(newArtifactsFolder) const newArtifacts08 = instantiateArtifacts(newArtifactsFolder08) diff --git a/packages/protocol/scripts/truffle/make-release.ts b/packages/protocol/scripts/truffle/make-release.ts index fe53bffd6bd..3047a9d6810 100644 --- a/packages/protocol/scripts/truffle/make-release.ts +++ b/packages/protocol/scripts/truffle/make-release.ts @@ -5,10 +5,16 @@ import { LibraryAddresses } from '@celo/protocol/lib/bytecode' import { ASTDetailedVersionedReport } from '@celo/protocol/lib/compatibility/report' import { getCeloContractDependencies } from '@celo/protocol/lib/contract-dependencies' import { CeloContractName, celoRegistryAddress } from '@celo/protocol/lib/registry-utils' + +import { SOLIDITY_08_PACKAGE } from '@celo/protocol/contractPackages' import { makeTruffleContractForMigrationWithoutSingleton } from '@celo/protocol/lib/web3-utils' import { Address, NULL_ADDRESS, eqAddress } from '@celo/utils/lib/address' import { TruffleContract } from '@truffle/contract' -import { SOLIDITY_08_PACKAGE } from 'contractPackages' + +// tslint:disable-next-line: ordered-imports + +// tslint:disable-next-line: ordered-imports + import { readJsonSync, readdirSync, writeJsonSync } from 'fs-extra' import { basename, join } from 'path' import { RegistryInstance } from 'types' @@ -113,9 +119,14 @@ const deployImplementation = async ( } console.log(`Deploying ${contractName}`) // Hack to trick truffle, which checks that the provided address has code + + // without this delay it sometimes fails with ProviderError + await delay(getRandomNumber(1, 1000)) + const contract = await (dryRun ? Contract.at(celoRegistryAddress) : Contract.new(testingDeployment)) + // Sanity check that any contracts that are being changed set a version number. const getVersionNumberAbi = contract.abi.find( (abi: any) => abi.type === 'function' && abi.name === 'getVersionNumber' @@ -306,7 +317,7 @@ module.exports = async (callback: (error?: any) => number) => { try { contractArtifact = await artifacts.require(contractName) } catch { - // it wasn't found in the standard artifacts folder, check if it's + // it wasn't found in the standard artifacts folder, check if it's 0.8 contract // TODO this needs generalization to support more packages // https://github.com/celo-org/celo-monorepo/issues/10563 contractArtifact = makeTruffleContractForMigrationWithoutSingleton( diff --git a/packages/protocol/test/common/gascurrencywhitelist.ts b/packages/protocol/test/common/gascurrencywhitelist.ts new file mode 100644 index 00000000000..dc40ba498d0 --- /dev/null +++ b/packages/protocol/test/common/gascurrencywhitelist.ts @@ -0,0 +1,46 @@ +import { assertTransactionRevertWithReason } from '@celo/protocol/lib/test-utils' +import { FeeCurrencyWhitelistContract, FeeCurrencyWhitelistInstance } from 'types' + +const FeeCurrencyWhitelist: FeeCurrencyWhitelistContract = artifacts.require('FeeCurrencyWhitelist') + +contract('FeeCurrencyWhitelist', (accounts: string[]) => { + let feeCurrencyWhitelist: FeeCurrencyWhitelistInstance + + const aTokenAddress = '0x000000000000000000000000000000000000ce10' + + const nonOwner = accounts[1] + + beforeEach(async () => { + feeCurrencyWhitelist = await FeeCurrencyWhitelist.new(true) + await feeCurrencyWhitelist.initialize() + }) + + describe('#initialize()', () => { + it('should have set the owner', async () => { + const owner: string = await feeCurrencyWhitelist.owner() + assert.equal(owner, accounts[0]) + }) + + it('should not be callable again', async () => { + await assertTransactionRevertWithReason( + feeCurrencyWhitelist.initialize(), + 'contract already initialized' + ) + }) + }) + + describe('#addToken()', () => { + it('should allow the owner to add a token', async () => { + await feeCurrencyWhitelist.addToken(aTokenAddress) + const tokens = await feeCurrencyWhitelist.getWhitelist() + assert.sameMembers(tokens, [aTokenAddress]) + }) + + it('should not allow a non-owner to add a token', async () => { + await assertTransactionRevertWithReason( + feeCurrencyWhitelist.addToken(aTokenAddress, { from: nonOwner }), + 'Ownable: caller is not the owner' + ) + }) + }) +}) diff --git a/packages/protocol/test/governance/network/governance.ts b/packages/protocol/test/governance/network/governance.ts index 5f66c36b51c..d970e5b4fe1 100644 --- a/packages/protocol/test/governance/network/governance.ts +++ b/packages/protocol/test/governance/network/governance.ts @@ -2230,7 +2230,7 @@ contract('Governance', (accounts: string[]) => { }) it('should revert when the account weight is 0', async () => { - await mockLockedGold.setAccountTotalGovernancePower(account, 0) + await mockLockedGold.setAccountTotalLockedGold(account, 0) await assertRevert(governance.vote(proposalId, index, value)) }) diff --git a/packages/protocol/test/governance/voting/lockedgold.ts b/packages/protocol/test/governance/voting/lockedgold.ts index 097ab277b96..8d8b94564b1 100644 --- a/packages/protocol/test/governance/voting/lockedgold.ts +++ b/packages/protocol/test/governance/voting/lockedgold.ts @@ -10,9 +10,9 @@ import { createAndAssertDelegatorDelegateeSigners, timeTravel, } from '@celo/protocol/lib/test-utils' +import { ZERO_ADDRESS } from '@celo/protocol/test/constants' import { fromFixed, toFixed } from '@celo/utils/lib/fixidity' import BigNumber from 'bignumber.js' -import { zeroAddress } from 'ethereumjs-util' import { AccountsContract, AccountsInstance, @@ -1154,14 +1154,14 @@ contract('LockedGold', (accounts: string[]) => { describe('#delegateGovernanceVotes', () => { it('should revert when delegatee is not account', async () => { await assertTransactionRevertWithReason( - lockedGold.delegateGovernanceVotes(zeroAddress(), toFixed(10 / 100)), + lockedGold.delegateGovernanceVotes(ZERO_ADDRESS, toFixed(10 / 100)), 'Must first register address with Account.createAccount' ) }) it('should revert when delegator is not an account', async () => { await assertTransactionRevertWithReason( - lockedGold.delegateGovernanceVotes(zeroAddress(), toFixed(10 / 100), { from: accounts[1] }), + lockedGold.delegateGovernanceVotes(ZERO_ADDRESS, toFixed(10 / 100), { from: accounts[1] }), 'Must first register address with Account.createAccount' ) }) @@ -1261,7 +1261,7 @@ contract('LockedGold', (accounts: string[]) => { it('should revert when incorrect percent amount is inserted', async () => { await assertTransactionRevertWithReason( - lockedGold.delegateGovernanceVotes(zeroAddress(), toFixed(101 / 100)), + lockedGold.delegateGovernanceVotes(ZERO_ADDRESS, toFixed(101 / 100)), 'Delegate fraction must be less than or equal to 1' ) }) @@ -1513,7 +1513,7 @@ contract('LockedGold', (accounts: string[]) => { it('should revert when incorrect percent amount is inserted', async () => { await assertTransactionRevertWithReason( - lockedGold.delegateGovernanceVotes(zeroAddress(), toFixed(101 / 100)), + lockedGold.delegateGovernanceVotes(ZERO_ADDRESS, toFixed(101 / 100)), 'Delegate fraction must be less than or equal to 1' ) }) @@ -1886,14 +1886,14 @@ contract('LockedGold', (accounts: string[]) => { describe('#revokeDelegatedGovernanceVotes()', () => { it('should revert when incorrect percent amount is inserted', async () => { await assertTransactionRevertWithReason( - lockedGold.revokeDelegatedGovernanceVotes(zeroAddress(), toFixed(101 / 100)), + lockedGold.revokeDelegatedGovernanceVotes(ZERO_ADDRESS, toFixed(101 / 100)), 'Revoke fraction must be less than or equal to 1' ) }) it('should revert when nothing delegated', async () => { await assertTransactionRevertWithReason( - lockedGold.revokeDelegatedGovernanceVotes(zeroAddress(), toFixed(10 / 100)), + lockedGold.revokeDelegatedGovernanceVotes(ZERO_ADDRESS, toFixed(10 / 100)), 'Not enough total delegated percents' ) }) @@ -1927,7 +1927,7 @@ contract('LockedGold', (accounts: string[]) => { it('should revert when trying to revert more percent than delegated', async () => { await assertTransactionRevertWithReason( - lockedGold.revokeDelegatedGovernanceVotes(zeroAddress(), toFixed(100 / 100)), + lockedGold.revokeDelegatedGovernanceVotes(ZERO_ADDRESS, toFixed(100 / 100)), 'Not enough total delegated percents' ) }) @@ -2245,7 +2245,7 @@ contract('LockedGold', (accounts: string[]) => { it('should revert when trying to revert more percent than delegated', async () => { await assertTransactionRevertWithReason( - lockedGold.revokeDelegatedGovernanceVotes(zeroAddress(), toFixed(100 / 100)), + lockedGold.revokeDelegatedGovernanceVotes(ZERO_ADDRESS, toFixed(100 / 100)), 'Not enough total delegated percents' ) }) @@ -2899,4 +2899,28 @@ contract('LockedGold', (accounts: string[]) => { }) }) }) + + describe('#getTotalPendingWithdrawalsCount()', () => { + it('should return 0 if account has no pending withdrawals', async () => { + const count = await lockedGold.getTotalPendingWithdrawalsCount(account) + assert.equal(count.toNumber(), 0) + }) + + it('should return the count of pending withdrawals', async () => { + const value = 10000 + // @ts-ignore + await lockedGold.lock({ value }) + await lockedGold.unlock(value / 2) + await lockedGold.unlock(value / 2) + + const count = await lockedGold.getTotalPendingWithdrawalsCount(account) + assert.equal(count.toNumber(), 2) + }) + + it('should return 0 for a non-existent account', async () => { + const nonExistentAccount = '0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef' + const count = await lockedGold.getTotalPendingWithdrawalsCount(nonExistentAccount) + assert.equal(count.toNumber(), 0) + }) + }) }) diff --git a/packages/protocol/truffle-config-parent.js b/packages/protocol/truffle-config-parent.js index 88166959b54..b5595f17760 100644 --- a/packages/protocol/truffle-config-parent.js +++ b/packages/protocol/truffle-config-parent.js @@ -4,7 +4,6 @@ const ProviderEngine = require('web3-provider-engine') const WebsocketSubprovider = require('web3-provider-engine/subproviders/websocket.js') const { TruffleArtifactAdapter } = require('@0x/sol-trace') const { CoverageSubprovider } = require('@0x/sol-coverage') -const flakeTrackingConfig = require('@celo/flake-tracker/src/mocha/config.js') var Web3 = require('web3') var net = require('net') diff --git a/packages/protocol/truffle-config.js b/packages/protocol/truffle-config.js index 1ea746e776c..6b46bc294b0 100644 --- a/packages/protocol/truffle-config.js +++ b/packages/protocol/truffle-config.js @@ -1,7 +1,6 @@ const SOLC_VERSION = '0.5.13' const parent = require('./truffle-config-parent.js') -const flakeTrackingConfig = require('@celo/flake-tracker/src/mocha/config.js') const networks = { ...parent.networks } console.log(`Using truffle version for Solidity ${SOLC_VERSION}`) @@ -18,7 +17,6 @@ module.exports = { }, }, networks, - mocha: flakeTrackingConfig, } if (process.argv.includes('--gas')) { diff --git a/packages/protocol/truffle-config0.8.js b/packages/protocol/truffle-config0.8.js index 467767c362d..ce1bc956b18 100644 --- a/packages/protocol/truffle-config0.8.js +++ b/packages/protocol/truffle-config0.8.js @@ -1,7 +1,6 @@ const SOLC_VERSION = '0.8.19' const parent = require('./truffle-config-parent.js') -const flakeTrackingConfig = require('@celo/flake-tracker/src/mocha/config.js') const networks = { ...parent.networks } console.log(`Using truffle version for Solidity ${SOLC_VERSION}`) @@ -17,7 +16,6 @@ module.exports = { }, }, networks, - mocha: flakeTrackingConfig, } if (process.argv.includes('--gas')) { diff --git a/packages/protocol/tslint.json b/packages/protocol/tslint.json index eab49bb0281..59a3611ad0f 100644 --- a/packages/protocol/tslint.json +++ b/packages/protocol/tslint.json @@ -5,6 +5,15 @@ "exclude": ["types/contracts/*", "lib/**"] }, "rules": { + "no-implicit-dependencies": [ + true, + "dev", + [ + "contractPackages", + "@celo/protocol", + "types" + ] + ], "no-global-arrow-functions": false, "no-floating-promises": true, "no-string-literal": false, diff --git a/packages/sdk/base/CHANGELOG.md b/packages/sdk/base/CHANGELOG.md new file mode 100644 index 00000000000..9fa1aa3b76b --- /dev/null +++ b/packages/sdk/base/CHANGELOG.md @@ -0,0 +1,13 @@ +# @celo/base + +## 5.0.5 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other + +## 5.0.5-beta.0 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other diff --git a/packages/sdk/base/jest.config.js b/packages/sdk/base/jest.config.js index 86abcf4a671..53358d65426 100644 --- a/packages/sdk/base/jest.config.js +++ b/packages/sdk/base/jest.config.js @@ -1,7 +1,4 @@ -const { nodeFlakeTracking } = require('@celo/flake-tracker/src/jest/config.js') - module.exports = { preset: 'ts-jest', testMatch: ['/src/**/?(*.)+(spec|test).ts?(x)'], - ...nodeFlakeTracking, } diff --git a/packages/sdk/base/package.json b/packages/sdk/base/package.json index fd14ba4f859..05f347361fd 100644 --- a/packages/sdk/base/package.json +++ b/packages/sdk/base/package.json @@ -1,6 +1,6 @@ { "name": "@celo/base", - "version": "4.1.1-dev", + "version": "5.0.5", "description": "Celo base common utils, no dependencies", "author": "Celo", "license": "Apache-2.0", @@ -23,7 +23,6 @@ ], "dependencies": {}, "devDependencies": { - "@celo/flake-tracker": "0.0.1-dev", "@celo/typescript": "0.0.1", "bignumber.js": "^9.0.0", "elliptic": "^6.5.4", diff --git a/packages/sdk/base/src/address.ts b/packages/sdk/base/src/address.ts index d49e641a2af..9eea852b30d 100644 --- a/packages/sdk/base/src/address.ts +++ b/packages/sdk/base/src/address.ts @@ -2,6 +2,8 @@ const HEX_REGEX = /^0x[0-9A-F]*$/i export type Address = string +export type StrongAddress = `0x${string}` + export const eqAddress = (a: Address, b: Address) => normalizeAddress(a) === normalizeAddress(b) export const normalizeAddress = (a: Address) => trimLeading0x(a).toLowerCase() @@ -12,7 +14,8 @@ export const normalizeAddressWith0x = (a: Address) => ensureLeading0x(a).toLower export const trimLeading0x = (input: string) => (input.startsWith('0x') ? input.slice(2) : input) -export const ensureLeading0x = (input: string) => (input.startsWith('0x') ? input : `0x${input}`) +export const ensureLeading0x = (input: string): StrongAddress => + input.startsWith('0x') ? (input as StrongAddress) : (`0x${input}` as const) // Turns '0xce10ce10ce10ce10ce10ce10ce10ce10ce10ce10' // into ['ce10','ce10','ce10','ce10','ce10','ce10','ce10','ce10','ce10','ce10'] diff --git a/packages/sdk/connect/CHANGELOG.md b/packages/sdk/connect/CHANGELOG.md new file mode 100644 index 00000000000..c188ae85a0e --- /dev/null +++ b/packages/sdk/connect/CHANGELOG.md @@ -0,0 +1,29 @@ +# @celo/connect + +## 5.1.0 + +### Minor Changes + +- 53bbd4958: Add cip64 support for feeCurrency Transactions. Note this is the replacement for the deprecated cip42 and legacy tx types https://github.com/celo-org/celo-proposals/blob/master/CIPs/cip/0064.md + +### Patch Changes + +- d48c68afc: Add memoization to Connection.chainId() funciton. this is reset when setProvider is called. +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- Updated dependencies [53bbd4958] + - @celo/utils@5.0.5 + - @celo/base@5.0.5 + +## 5.1.0-beta.0 + +### Minor Changes + +- 53bbd4958: Add cip64 support for feeCurrency Transactions. Note this is the replacement for the deprecated cip42 and legacy tx types https://github.com/celo-org/celo-proposals/blob/master/CIPs/cip/0064.md + +### Patch Changes + +- d48c68afc: Add memoization to Connection.chainId() funciton. this is reset when setProvider is called. +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- Updated dependencies [53bbd4958] + - @celo/utils@5.0.5-beta.0 + - @celo/base@5.0.5-beta.0 diff --git a/packages/sdk/connect/LICENSE b/packages/sdk/connect/LICENSE new file mode 100644 index 00000000000..f49a4e16e68 --- /dev/null +++ b/packages/sdk/connect/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/packages/sdk/connect/jest.config.js b/packages/sdk/connect/jest.config.js index 0b093cd179c..c0198cdd329 100644 --- a/packages/sdk/connect/jest.config.js +++ b/packages/sdk/connect/jest.config.js @@ -1,9 +1,5 @@ -const { nodeFlakeTracking } = require('@celo/flake-tracker/src/jest/config.js') - module.exports = { preset: 'ts-jest', - ...nodeFlakeTracking, testMatch: ['/src/**/?(*.)+(spec|test).ts?(x)'], - setupFilesAfterEnv: [...nodeFlakeTracking.setupFilesAfterEnv], verbose: true, } diff --git a/packages/sdk/connect/package.json b/packages/sdk/connect/package.json index 8daf50d161a..f1dd5057cc0 100644 --- a/packages/sdk/connect/package.json +++ b/packages/sdk/connect/package.json @@ -1,6 +1,6 @@ { "name": "@celo/connect", - "version": "4.1.1-dev", + "version": "5.1.0", "description": "Light Toolkit for connecting with the Celo network", "main": "./lib/index.js", "types": "./lib/index.d.ts", @@ -22,16 +22,19 @@ "prepublishOnly": "yarn build" }, "dependencies": { + "@ethereumjs/util": "8.0.5", "@types/debug": "^4.1.5", "@types/utf8": "^2.1.6", - "@celo/base": "4.1.1-dev", - "@celo/utils": "4.1.1-dev", + "@celo/base": "^5.0.5", + "@celo/utils": "^5.0.5", "bignumber.js": "^9.0.0", "debug": "^4.1.1", - "utf8": "3.0.0" + "utf8": "3.0.0", + "web3-core": "1.10.0", + "web3-eth": "1.10.0", + "web3-eth-contract": "1.10.0" }, "devDependencies": { - "@celo/flake-tracker": "0.0.1-dev", "web3": "1.10.0", "web3-core": "1.10.0", "web3-eth": "1.10.0", diff --git a/packages/sdk/connect/src/celo-provider.test.ts b/packages/sdk/connect/src/celo-provider.test.ts index 252c5d1c347..daa2b2668d5 100644 --- a/packages/sdk/connect/src/celo-provider.test.ts +++ b/packages/sdk/connect/src/celo-provider.test.ts @@ -29,8 +29,9 @@ class MockWallet implements ReadOnlyWallet { } signTransaction(_txParams: CeloTx): Promise { return Promise.resolve({ - raw: 'mock', + raw: '0xmock', tx: { + type: 'celo-legacy', nonce: 'nonce', gasPrice: 'gasPrice', gas: 'gas', @@ -193,7 +194,7 @@ describe('CeloProvider', () => { describe('but tries to use it with a different account', () => { interceptedByCeloProvider.forEach((method: string) => { - test(`fowards the call to '${method}' to the original provider`, (done) => { + test(`forwards the call to '${method}' to the original provider`, (done) => { const payload: JsonRpcPayload = { id: 0, jsonrpc: '2.0', diff --git a/packages/sdk/connect/src/connection.ts b/packages/sdk/connect/src/connection.ts index bb5fc7f4553..be7cf97c047 100644 --- a/packages/sdk/connect/src/connection.ts +++ b/packages/sdk/connect/src/connection.ts @@ -1,11 +1,12 @@ +// tslint:disable: ordered-imports import { ensureLeading0x, toChecksumAddress } from '@celo/utils/lib/address' import { EIP712TypedData, generateTypedDataHash } from '@celo/utils/lib/sign-typed-data-utils' -import { parseSignatureWithoutPrefix, Signature } from '@celo/utils/lib/signatureUtils' +import { Signature, parseSignatureWithoutPrefix } from '@celo/utils/lib/signatureUtils' import { bufferToHex } from '@ethereumjs/util' import debugFactory from 'debug' import Web3 from 'web3' import { AbiCoder } from './abi-types' -import { assertIsCeloProvider, CeloProvider } from './celo-provider' +import { CeloProvider, assertIsCeloProvider } from './celo-provider' import { Address, Block, @@ -32,9 +33,9 @@ import { outputCeloTxReceiptFormatter, } from './utils/formatter' import { hasProperty } from './utils/provider-utils' -import { getRandomId, HttpRpcCaller, RpcCaller } from './utils/rpc-caller' +import { HttpRpcCaller, RpcCaller, getRandomId } from './utils/rpc-caller' import { TxParamsNormalizer } from './utils/tx-params-normalizer' -import { toTxResult, TransactionResult } from './utils/tx-result' +import { TransactionResult, toTxResult } from './utils/tx-result' import { ReadOnlyWallet } from './wallet' const debugGasEstimation = debugFactory('connection:gas-estimation') @@ -42,7 +43,6 @@ const debugGasEstimation = debugFactory('connection:gas-estimation') type BN = ReturnType export interface ConnectionOptions { gasInflationFactor: number - gasPrice: string feeCurrency?: Address from?: Address } @@ -55,10 +55,11 @@ export interface ConnectionOptions { */ export class Connection { private config: ConnectionOptions + private _chainID: number | undefined readonly paramsPopulator: TxParamsNormalizer rpcCaller!: RpcCaller - /** @deprecated no longer needed since gasPrice is available on node rpc */ + /** @deprecated no longer needed since gasPrice is available on minimumClientVersion node rpc */ private currencyGasPrice: Map = new Map() constructor(readonly web3: Web3, public wallet?: ReadOnlyWallet, handleRevert = true) { @@ -66,8 +67,6 @@ export class Connection { this.config = { gasInflationFactor: 1.3, - // gasPrice:0 means the node will compute gasPrice on its own - gasPrice: '0', } const existingProvider: Provider = web3.currentProvider as Provider @@ -82,6 +81,7 @@ export class Connection { if (!provider) { throw new Error('Must have a valid Provider') } + this._chainID = undefined try { if (!(provider instanceof CeloProvider)) { this.rpcCaller = new HttpRpcCaller(provider) @@ -89,7 +89,8 @@ export class Connection { } this.web3.setProvider(provider as any) return true - } catch { + } catch (error) { + console.error(`could not attach provider`, error) return false } } @@ -125,14 +126,6 @@ export class Connection { return this.config.gasInflationFactor } - set defaultGasPrice(price: number) { - this.config.gasPrice = price.toString(10) - } - - get defaultGasPrice() { - return parseInt(this.config.gasPrice, 10) - } - /** * Set the ERC20 address for the token to use to pay for transaction fees. * The ERC20 must be whitelisted for gas. @@ -224,7 +217,6 @@ export class Connection { */ sendTransaction = async (tx: CeloTx): Promise => { tx = this.fillTxDefaults(tx) - tx = this.fillGasPrice(tx) let gas = tx.gas if (gas == null) { @@ -244,7 +236,6 @@ export class Connection { tx?: Omit ): Promise => { tx = this.fillTxDefaults(tx) - tx = this.fillGasPrice(tx) let gas = tx.gas if (gas == null) { @@ -341,8 +332,9 @@ export class Connection { sendSignedTransaction = async (signedTransactionData: string): Promise => { return toTxResult(this.web3.eth.sendSignedTransaction(signedTransactionData)) } + // if neither gas price nor feeMarket fields are present set them. - /** @deprecated no longer needed since gasPrice is available on node rpc */ + /** @deprecated no longer needed since gasPrice is available on minimumClientVersion node rpc */ fillGasPrice(tx: CeloTx): CeloTx { if (tx.feeCurrency && tx.gasPrice === '0' && this.currencyGasPrice.has(tx.feeCurrency)) { return { @@ -352,7 +344,8 @@ export class Connection { } return tx } - /** @deprecated no longer needed since gasPrice is available on node rpc */ + + /** @deprecated no longer needed since gasPrice is available on minimumClientVersion node rpc */ async setGasPriceForCurrency(address: Address, gasPrice: string) { this.currencyGasPrice.set(address, gasPrice) } @@ -404,10 +397,16 @@ export class Connection { } } + // An instance of Connection will only change chain id if provider is changed. chainId = async (): Promise => { + if (this._chainID) { + return this._chainID + } // Reference: https://eth.wiki/json-rpc/API#net_version const response = await this.rpcCaller.call('net_version', []) - return parseInt(response.result.toString(), 10) + const chainID = parseInt(response.result.toString(), 10) + this._chainID = chainID + return chainID } getTransactionCount = async (address: Address): Promise => { @@ -430,7 +429,6 @@ export class Connection { gasPrice = async (feeCurrency?: Address): Promise => { // Required otherwise is not backward compatible const parameter = feeCurrency ? [feeCurrency] : [] - // Reference: https://eth.wiki/json-rpc/API#eth_gasprice const response = await this.rpcCaller.call('eth_gasPrice', parameter) const gasPriceInHex = response.result.toString() @@ -446,10 +444,7 @@ export class Connection { private isBlockNumberHash = (blockNumber: BlockNumber) => blockNumber instanceof String && blockNumber.indexOf('0x') === 0 - getBlock = async ( - blockHashOrBlockNumber: BlockNumber, - fullTxObjects: boolean = true - ): Promise => { + getBlock = async (blockHashOrBlockNumber: BlockNumber, fullTxObjects = true): Promise => { const endpoint = this.isBlockNumberHash(blockHashOrBlockNumber) ? 'eth_getBlockByHash' // Reference: https://eth.wiki/json-rpc/API#eth_getBlockByHash : 'eth_getBlockByNumber' // Reference: https://eth.wiki/json-rpc/API#eth_getBlockByNumber @@ -508,7 +503,6 @@ export class Connection { const defaultTx: CeloTx = { from: this.config.from, feeCurrency: this.config.feeCurrency, - gasPrice: this.config.gasPrice, } return { @@ -522,3 +516,18 @@ export class Connection { this.web3.currentProvider.stop() } } + +function isEmpty(value: string | undefined | number | BN) { + return ( + value === 0 || + value === undefined || + value === null || + value === '0' || + (typeof value === 'string' && + (value.toLowerCase() === '0x' || value.toLowerCase() === '0x0')) || + Web3.utils.toBN(value.toString()).eq(Web3.utils.toBN(0)) + ) +} +export function isPresent(value: string | undefined | number | BN) { + return !isEmpty(value) +} diff --git a/packages/sdk/connect/src/types.ts b/packages/sdk/connect/src/types.ts index 4f3d4493d6a..8312c142f05 100644 --- a/packages/sdk/connect/src/types.ts +++ b/packages/sdk/connect/src/types.ts @@ -1,16 +1,57 @@ -import { PromiEvent, Transaction, TransactionConfig, TransactionReceipt } from 'web3-core' +import { + AccessList, + PromiEvent, + Transaction, + TransactionConfig, + TransactionReceipt, +} from 'web3-core' import { Contract } from 'web3-eth-contract' - export type Address = string +export type Hex = `0x${string}` export interface CeloParams { feeCurrency: string + /* + @deprecated + */ gatewayFeeRecipient: string + /* + @deprecated + */ gatewayFee: string } -export type CeloTx = TransactionConfig & Partial +export type AccessListRaw = Array<[string, string[]]> + +export type HexOrMissing = Hex | undefined +export interface FormattedCeloTx { + chainId: number + from: HexOrMissing + to: HexOrMissing + data: string | undefined + value: HexOrMissing + feeCurrency?: HexOrMissing + /* + @deprecated + */ + gatewayFeeRecipient?: HexOrMissing + /* + @deprecated + */ + gatewayFee?: HexOrMissing + gas: HexOrMissing + gasPrice?: Hex + maxFeePerGas?: Hex + maxPriorityFeePerGas?: Hex + nonce: HexOrMissing | number + accessList?: AccessListRaw + type: TransactionTypes +} + +export type CeloTx = TransactionConfig & + Partial & { accessList?: AccessList; type?: TransactionTypes } +export type CeloTxWithSig = CeloTx & { v: number; s: string; r: string; yParity: 0 | 1 } export interface CeloTxObject { arguments: any[] call(tx?: CeloTx): Promise @@ -24,23 +65,57 @@ export { BlockNumber, EventLog, Log, PromiEvent, Sign } from 'web3-core' export { Block, BlockHeader, Syncing } from 'web3-eth' export { Contract, ContractSendMethod, PastEventOptions } from 'web3-eth-contract' +export type TransactionTypes = 'eip1559' | 'celo-legacy' | 'cip42' | 'cip64' + +interface CommonTXProperties { + nonce: string + gas: string + to: string + value: string + input: string + r: string + s: string + v: string + hash: string + type: TransactionTypes +} + +interface FeeMarketAndAccessListTXProperties extends CommonTXProperties { + maxFeePerGas: string + maxPriorityFeePerGas: string + accessList?: AccessList +} + +export interface EIP1559TXProperties extends FeeMarketAndAccessListTXProperties { + type: 'eip1559' +} + +export interface CIP64TXProperties extends FeeMarketAndAccessListTXProperties { + feeCurrency: string + type: 'cip64' +} + +export interface CIP42TXProperties extends FeeMarketAndAccessListTXProperties { + feeCurrency: string + gatewayFeeRecipient?: string + gatewayFee?: string + type: 'cip42' +} + +/* + @deprecated + */ +export interface LegacyTXProperties extends CommonTXProperties { + gasPrice: string + feeCurrency: string + gatewayFeeRecipient: string + gatewayFee: string + type: 'celo-legacy' +} + export interface EncodedTransaction { - raw: string - tx: { - nonce: string - gasPrice: string - gas: string - feeCurrency: string - gatewayFeeRecipient: string - gatewayFee: string - to: string - value: string - input: string - r: string - s: string - v: string - hash: string - } + raw: Hex + tx: LegacyTXProperties | CIP42TXProperties | EIP1559TXProperties | CIP64TXProperties } export type CeloTxPending = Transaction & Partial @@ -87,6 +162,7 @@ export interface HttpProvider { } export interface RLPEncodedTx { - transaction: CeloTx - rlpEncode: string + transaction: FormattedCeloTx + rlpEncode: Hex + type: TransactionTypes } diff --git a/packages/sdk/connect/src/utils/formatter.test.ts b/packages/sdk/connect/src/utils/formatter.test.ts new file mode 100644 index 00000000000..0eaeaa92be6 --- /dev/null +++ b/packages/sdk/connect/src/utils/formatter.test.ts @@ -0,0 +1,297 @@ +import { CeloTx } from '../types' +import { inputAccessListFormatter, inputCeloTxFormatter, outputCeloTxFormatter } from './formatter' + +describe('inputAccessListFormatter', () => { + test('with valid accessList', () => { + const accessList = [ + { + address: '0x0000000000000000000000000000000000000000', + storageKeys: [ + '0x0000000000000000000000000000000000000000000000000000000000000001', + '0x60fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe', + ], + }, + ] + + expect(inputAccessListFormatter(accessList)).toEqual([ + [ + '0x0000000000000000000000000000000000000000', + [ + '0x0000000000000000000000000000000000000000000000000000000000000001', + '0x60fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe', + ], + ], + ]) + }) +}) + +describe('inputCeloTxFormatter', () => { + const base: CeloTx = { + chainId: 42220, + nonce: 1, + gas: 1000000, + value: '0x0241', + from: '0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe', + to: '0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe', + data: '0x', + } + describe('when address does not pass checksum', () => { + ;['from', 'to', 'feeCurrency'].forEach((property) => { + test(`${property}`, () => { + const faulty = { ...base, [property]: '0x3e8' } + expect(() => inputCeloTxFormatter(faulty)).toThrowError( + `Provided address 0x3e8 is invalid, the capitalization checksum test failed` + ) + }) + }) + }) + + describe('valid celo-legacy tx', () => { + const legacy = { + ...base, + gasPrice: '0x3e8', + feeCurrency: '0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe', + } + it('formats', () => { + expect(inputCeloTxFormatter(legacy)).toMatchInlineSnapshot(` + { + "data": "0x", + "feeCurrency": "0x11f4d0a3c12e86b4b5f39b213f7e19d048276dae", + "from": "0x11f4d0a3c12e86b4b5f39b213f7e19d048276dae", + "gas": "0xf4240", + "gasPrice": "0x3e8", + "nonce": "0x1", + "to": "0x11f4d0a3c12e86b4b5f39b213f7e19d048276dae", + "value": "0x241", + } + `) + }) + }) + describe('valid cip64 tx', () => { + const cip64 = { + ...base, + maxFeePerGas: '0x3e8', + maxPriorityFeePerGas: '0x3e8', + feeCurrency: '0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe', + } + it('formats', () => { + expect(inputCeloTxFormatter(cip64)).toMatchInlineSnapshot(` + { + "data": "0x", + "feeCurrency": "0x11f4d0a3c12e86b4b5f39b213f7e19d048276dae", + "from": "0x11f4d0a3c12e86b4b5f39b213f7e19d048276dae", + "gas": "0xf4240", + "maxFeePerGas": "0x3e8", + "maxPriorityFeePerGas": "0x3e8", + "nonce": "0x1", + "to": "0x11f4d0a3c12e86b4b5f39b213f7e19d048276dae", + "value": "0x241", + } + `) + }) + }) + describe('valid cip42 tx', () => { + const cip42 = { + ...base, + maxFeePerGas: '0x3e8', + maxPriorityFeePerGas: '0x3e8', + feeCurrency: '0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe', + } + it('formats', () => { + expect(inputCeloTxFormatter(cip42)).toMatchInlineSnapshot(` + { + "data": "0x", + "feeCurrency": "0x11f4d0a3c12e86b4b5f39b213f7e19d048276dae", + "from": "0x11f4d0a3c12e86b4b5f39b213f7e19d048276dae", + "gas": "0xf4240", + "maxFeePerGas": "0x3e8", + "maxPriorityFeePerGas": "0x3e8", + "nonce": "0x1", + "to": "0x11f4d0a3c12e86b4b5f39b213f7e19d048276dae", + "value": "0x241", + } + `) + }) + }) + describe('valid eip1559 tx', () => { + const eip1559 = { + ...base, + maxFeePerGas: '0x3e8', + maxPriorityFeePerGas: '0x3e8', + } + it('formats', () => { + expect(inputCeloTxFormatter(eip1559)).toMatchInlineSnapshot(` + { + "data": "0x", + "from": "0x11f4d0a3c12e86b4b5f39b213f7e19d048276dae", + "gas": "0xf4240", + "maxFeePerGas": "0x3e8", + "maxPriorityFeePerGas": "0x3e8", + "nonce": "0x1", + "to": "0x11f4d0a3c12e86b4b5f39b213f7e19d048276dae", + "value": "0x241", + } + `) + }) + }) +}) + +describe('outputCeloTxFormatter', () => { + const base = { + nonce: '0x4', + data: '0x', + input: '0x3454645634534', + from: '0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe', + to: '0x11f4d0a3c12e86b4b5f39b213f7e19d048276dae', + value: '0x3e8', + gas: '0x3e8', + transactionIndex: '0x1', + blockNumber: '0x3e8', + blockHash: '0xc9b9cdc2092a9d6589d96662b1fd6949611163fb3910cf8a173cd060f17702f9', + } + describe('with blockNumber', () => { + test('when valid', () => { + expect(outputCeloTxFormatter({ ...base, blockNumber: '0x1' })).toMatchInlineSnapshot(` + { + "blockHash": "0xc9b9cdc2092a9d6589d96662b1fd6949611163fb3910cf8a173cd060f17702f9", + "blockNumber": 1, + "data": "0x", + "from": "0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe", + "gas": 1000, + "input": "0x3454645634534", + "nonce": 4, + "to": "0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe", + "transactionIndex": 1, + "value": "1000", + } + `) + }) + test('when invalid', () => { + expect(outputCeloTxFormatter({ ...base, blockNumber: null })).toMatchInlineSnapshot(` + { + "blockHash": "0xc9b9cdc2092a9d6589d96662b1fd6949611163fb3910cf8a173cd060f17702f9", + "blockNumber": null, + "data": "0x", + "from": "0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe", + "gas": 1000, + "input": "0x3454645634534", + "nonce": 4, + "to": "0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe", + "transactionIndex": 1, + "value": "1000", + } + `) + }) + }) + describe('with valid celo-legacy tx', () => { + const legacy = { + ...base, + gasPrice: '0x3e8', + } + test('when valid', () => { + expect(outputCeloTxFormatter(legacy)).toMatchInlineSnapshot(` + { + "blockHash": "0xc9b9cdc2092a9d6589d96662b1fd6949611163fb3910cf8a173cd060f17702f9", + "blockNumber": 1000, + "data": "0x", + "from": "0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe", + "gas": 1000, + "gasPrice": "1000", + "input": "0x3454645634534", + "nonce": 4, + "to": "0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe", + "transactionIndex": 1, + "value": "1000", + } + `) + }) + }) + describe('with valid cip42 tx', () => { + const cip42 = { + ...base, + gateWayFee: '0x3e8', + feeCurrency: '0x11f4d0a3c12e86b4b5f39b213f7e19d048276dae', + maxFeePerGas: '0x3e8', + maxPriorityFeePerGas: '0x3e8', + } + test('when valid', () => { + expect(outputCeloTxFormatter(cip42)).toMatchInlineSnapshot(` + { + "blockHash": "0xc9b9cdc2092a9d6589d96662b1fd6949611163fb3910cf8a173cd060f17702f9", + "blockNumber": 1000, + "data": "0x", + "feeCurrency": "0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe", + "from": "0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe", + "gas": 1000, + "gateWayFee": "0x3e8", + "input": "0x3454645634534", + "maxFeePerGas": "1000", + "maxPriorityFeePerGas": "1000", + "nonce": 4, + "to": "0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe", + "transactionIndex": 1, + "value": "1000", + } + `) + }) + }) + describe('with valid eip1559 tx', () => { + const eip1559 = { + ...base, + maxFeePerGas: '0x3e8', + maxPriorityFeePerGas: '0x3e8', + } + test('when valid', () => { + expect(outputCeloTxFormatter(eip1559)).toMatchInlineSnapshot(` + { + "blockHash": "0xc9b9cdc2092a9d6589d96662b1fd6949611163fb3910cf8a173cd060f17702f9", + "blockNumber": 1000, + "data": "0x", + "from": "0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe", + "gas": 1000, + "input": "0x3454645634534", + "maxFeePerGas": "1000", + "maxPriorityFeePerGas": "1000", + "nonce": 4, + "to": "0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe", + "transactionIndex": 1, + "value": "1000", + } + `) + }) + }) + describe('when properties are missing', () => { + test('without from', () => { + expect(outputCeloTxFormatter({ ...base, from: null })).toMatchInlineSnapshot(` + { + "blockHash": "0xc9b9cdc2092a9d6589d96662b1fd6949611163fb3910cf8a173cd060f17702f9", + "blockNumber": 1000, + "data": "0x", + "from": null, + "gas": 1000, + "input": "0x3454645634534", + "nonce": 4, + "to": "0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe", + "transactionIndex": 1, + "value": "1000", + } + `) + }) + test('without to', () => { + expect(outputCeloTxFormatter({ ...base, to: null })).toMatchInlineSnapshot(` + { + "blockHash": "0xc9b9cdc2092a9d6589d96662b1fd6949611163fb3910cf8a173cd060f17702f9", + "blockNumber": 1000, + "data": "0x", + "from": "0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe", + "gas": 1000, + "input": "0x3454645634534", + "nonce": 4, + "to": null, + "transactionIndex": 1, + "value": "1000", + } + `) + }) + }) +}) diff --git a/packages/sdk/connect/src/utils/formatter.ts b/packages/sdk/connect/src/utils/formatter.ts index dfe3c07f650..ebba6760dce 100644 --- a/packages/sdk/connect/src/utils/formatter.ts +++ b/packages/sdk/connect/src/utils/formatter.ts @@ -1,46 +1,85 @@ -import { ensureLeading0x, trimLeading0x } from '@celo/base/lib/address' +import { ensureLeading0x, StrongAddress, trimLeading0x } from '@celo/base/lib/address' import { isValidAddress, toChecksumAddress } from '@celo/utils/lib/address' import { sha3 } from '@celo/utils/lib/solidity' import BigNumber from 'bignumber.js' import { encode } from 'utf8' +import { AccessList } from 'web3-core' import { + AccessListRaw, Block, BlockHeader, BlockNumber, CeloTx, CeloTxPending, CeloTxReceipt, + FormattedCeloTx, + Hex, Log, } from '../types' /** * Formats the input of a transaction and converts all values to HEX */ -export function inputCeloTxFormatter(tx: CeloTx) { - tx.from = inputAddressFormatter(tx.from?.toString()) - tx.to = inputAddressFormatter(tx.to) - tx.feeCurrency = inputAddressFormatter(tx.feeCurrency) - tx.gatewayFeeRecipient = inputAddressFormatter(tx.gatewayFeeRecipient) - - if (tx.data) { - tx.data = ensureLeading0x(tx.data) - } - - if (tx.data && !isHex(tx.data)) { +export function inputCeloTxFormatter(tx: CeloTx): FormattedCeloTx { + const { + from, + chainId, + nonce, + to, + gas, + gasPrice, + maxFeePerGas, + maxPriorityFeePerGas, + feeCurrency, + gatewayFee, + gatewayFeeRecipient, + data, + value, + accessList, + common, + chain, + hardfork, + ...rest + } = tx + const formattedTX: Partial = rest + formattedTX.from = inputAddressFormatter(from?.toString()) + formattedTX.to = inputAddressFormatter(to) + + formattedTX.gas = numberToHex(gas) + + formattedTX.value = numberToHex(value?.toString()) + formattedTX.nonce = numberToHex(nonce?.toString()) + + if (feeCurrency) { + formattedTX.feeCurrency = inputAddressFormatter(feeCurrency) + } + if (gatewayFeeRecipient) { + formattedTX.gatewayFeeRecipient = inputAddressFormatter(gatewayFeeRecipient) + } + if (gatewayFee) { + formattedTX.gatewayFee = numberToHex(gatewayFee) + } + + if (data && !isHex(data)) { throw new Error('The data field must be HEX encoded data.') + } else if (data) { + formattedTX.data = ensureLeading0x(data) } - tx.gas = numberToHex(tx.gas) - tx.gasPrice = numberToHex(tx.gasPrice?.toString()) - tx.value = numberToHex(tx.value?.toString()) - // @ts-ignore - nonce is defined as number, but uses as string (web3) - tx.nonce = numberToHex(tx.nonce?.toString()) - tx.gatewayFee = numberToHex(tx.gatewayFee) - - // @ts-ignore - prune undefines - Object.keys(tx).forEach((key) => tx[key] === undefined && delete tx[key]) + if (gasPrice) { + formattedTX.gasPrice = numberToHex(gasPrice.toString()) + } + if (maxFeePerGas) { + formattedTX.maxFeePerGas = numberToHex(maxFeePerGas.toString()) + } + if (maxPriorityFeePerGas) { + formattedTX.maxPriorityFeePerGas = numberToHex(maxPriorityFeePerGas.toString()) + } + if (accessList) { + formattedTX.accessList = inputAccessListFormatter(accessList) + } - return tx + return formattedTX as FormattedCeloTx } export function outputCeloTxFormatter(tx: any): CeloTxPending { @@ -52,9 +91,21 @@ export function outputCeloTxFormatter(tx: any): CeloTxPending { } tx.nonce = hexToNumber(tx.nonce) tx.gas = hexToNumber(tx.gas) - tx.gasPrice = outputBigNumberFormatter(tx.gasPrice) tx.value = outputBigNumberFormatter(tx.value) - tx.gatewayFee = outputBigNumberFormatter(tx.gatewayFee) + + if (tx.gatewayFee) { + tx.gatewayFee = outputBigNumberFormatter(tx.gatewayFee) + } + + if (tx.gasPrice) { + tx.gasPrice = outputBigNumberFormatter(tx.gasPrice) + } + if (tx.maxFeePerGas) { + tx.maxFeePerGas = outputBigNumberFormatter(tx.maxFeePerGas) + } + if (tx.maxPriorityFeePerGas) { + tx.maxPriorityFeePerGas = outputBigNumberFormatter(tx.maxPriorityFeePerGas) + } tx.to = tx.to && isValidAddress(tx.to) @@ -132,6 +183,7 @@ export function inputBlockNumberFormatter(blockNumber: BlockNumber) { : numberToHex(blockNumber.toString())! } +// TODO prune after gingerbread hardfork export function outputBlockHeaderFormatter(blockHeader: any): BlockHeader { // transform to number blockHeader.gasLimit = hexToNumber(blockHeader.gasLimit) @@ -213,12 +265,64 @@ export function outputBigNumberFormatter(hex: string): string { return new BigNumber(hex).toString(10) } -export function inputAddressFormatter(address?: string): string | undefined { +function isHash(value: string) { + return isHex(value) && value.length === 32 +} + +export function parseAccessList(accessListRaw: AccessListRaw | undefined): AccessList { + const accessList: AccessList = [] + if (!accessListRaw) { + return accessList + } + for (const entry of accessListRaw) { + const [address, storageKeys] = entry + + throwIfInvalidAddress(address) + + accessList.push({ + address, + storageKeys: storageKeys.map((key) => { + if (isHash(key)) { + return key + } else { + // same behavior as web3 + throw new Error(`Invalid storage key: ${key}`) + } + }), + }) + } + return accessList +} + +function throwIfInvalidAddress(address: string) { + if (!isValidAddress(address)) { + throw new Error(`Invalid address: ${address}`) + } +} + +export function inputAccessListFormatter(accessList?: AccessList): AccessListRaw { + if (!accessList || accessList.length === 0) { + return [] + } + return accessList.reduce((acc, { address, storageKeys }) => { + throwIfInvalidAddress(address) + + storageKeys.forEach((storageKey) => { + if (storageKey.length - 2 !== 64) { + throw new Error(`Invalid storage key: ${storageKey}`) + } + }) + acc.push([address, storageKeys]) + return acc + }, [] as AccessListRaw) +} + +export function inputAddressFormatter(address?: string): StrongAddress | undefined { if (!address || address === '0x') { return undefined } if (isValidAddress(address)) { - return ensureLeading0x(address).toLocaleLowerCase() + return ensureLeading0x(address).toLocaleLowerCase() as StrongAddress } throw new Error(`Provided address ${address} is invalid, the capitalization checksum test failed`) } @@ -256,12 +360,12 @@ function isHexStrict(hex: string): boolean { return /^(-)?0x[0-9a-f]*$/i.test(hex) } -function numberToHex(value?: BigNumber.Value) { +function numberToHex(value?: BigNumber.Value): Hex | undefined { if (value) { const numberValue = new BigNumber(value) const result = ensureLeading0x(new BigNumber(value).toString(16)) // Seen in web3, copied just in case - return numberValue.lt(new BigNumber(0)) ? `-${result}` : result + return (numberValue.lt(new BigNumber(0)) ? `-${result}` : result) as Hex } return undefined } diff --git a/packages/sdk/connect/src/utils/tx-params-normalizer.test.ts b/packages/sdk/connect/src/utils/tx-params-normalizer.test.ts index 2bfa4b0710e..b43b01954bd 100644 --- a/packages/sdk/connect/src/utils/tx-params-normalizer.test.ts +++ b/packages/sdk/connect/src/utils/tx-params-normalizer.test.ts @@ -17,6 +17,8 @@ describe('TxParamsNormalizer class', () => { value: 1, gas: 1, gasPrice: 1, + maxFeePerGas: undefined, + maxPriorityFeePerGas: undefined, feeCurrency: undefined, gatewayFeeRecipient: '1', gatewayFee: '1', @@ -92,56 +94,50 @@ describe('TxParamsNormalizer class', () => { expect(mockGasEstimation.mock.calls.length).toBe(1) }) - /* Disabled till the coinbase issue is fixed - - test('will populate the gatewayFeeRecipient', async () => { + test('will not pop maxFeePerGas and maxPriorityFeePerGas when gasPrice is set', async () => { const celoTx: CeloTx = { ...completeCeloTx } - celoTx.gatewayFeeRecipient = undefined + celoTx.gasPrice = 1 const newCeloTx = await populator.populate(celoTx) - expect(newCeloTx.gatewayFeeRecipient).toBe('27') - expect(mockRpcCall.mock.calls.length).toBe(1) - expect(mockRpcCall.mock.calls[0][0]).toBe('eth_coinbase') + expect(newCeloTx.maxFeePerGas).toBe(undefined) + expect(newCeloTx.maxPriorityFeePerGas).toBe(undefined) }) - - test('will retrieve only once the gatewayFeeRecipient', async () => { + test('will not pop maxFeePerGas if it is set', async () => { const celoTx: CeloTx = { ...completeCeloTx } - celoTx.gatewayFeeRecipient = undefined + celoTx.maxFeePerGas = 100 const newCeloTx = await populator.populate(celoTx) - expect(newCeloTx.gatewayFeeRecipient).toBe('27') - - const newCeloTx2 = await populator.populate(celoTx) - expect(newCeloTx2.gatewayFeeRecipient).toBe('27') - - expect(mockRpcCall.mock.calls.length).toBe(1) - expect(mockRpcCall.mock.calls[0][0]).toBe('eth_coinbase') + expect(newCeloTx.maxFeePerGas).toBe(100) }) - */ - - test('will populate the gas price without fee currency', async () => { + test('will not pop maxPriorityFeePerGas if it is set', async () => { const celoTx: CeloTx = { ...completeCeloTx } - celoTx.gasPrice = undefined + celoTx.maxPriorityFeePerGas = 2000 const newCeloTx = await populator.populate(celoTx) - expect(newCeloTx.gasPrice).toBe('0x27') - expect(mockRpcCall.mock.calls.length).toBe(1) - expect(mockRpcCall.mock.calls[0][0]).toBe('eth_gasPrice') + expect(newCeloTx.maxPriorityFeePerGas).toBe(2000) }) - test('will populate the gas price with fee currency', async () => { + test('will populate the maxFeePerGas and maxPriorityFeePerGas without fee currency', async () => { const celoTx: CeloTx = { ...completeCeloTx } celoTx.gasPrice = undefined - celoTx.feeCurrency = 'celoMagic' + celoTx.maxFeePerGas = undefined + celoTx.maxPriorityFeePerGas = undefined + celoTx.feeCurrency = undefined const newCeloTx = await populator.populate(celoTx) - expect(newCeloTx.gasPrice).toBe('0x27') - expect(mockRpcCall.mock.calls[0]).toEqual(['eth_gasPrice', ['celoMagic']]) + expect(newCeloTx.maxFeePerGas).toBe('0x2f') + expect(newCeloTx.maxPriorityFeePerGas).toBe('0x27') + expect(mockRpcCall.mock.calls[0]).toEqual(['eth_gasPrice', []]) + expect(mockRpcCall.mock.calls[1]).toEqual(['eth_maxPriorityFeePerGas', []]) }) - test('will not populate the gas price when fee currency is undefined', async () => { + test('will populate the maxFeePerGas and maxPriorityFeePerGas with fee currency', async () => { const celoTx: CeloTx = { ...completeCeloTx } celoTx.gasPrice = undefined - celoTx.feeCurrency = undefined + celoTx.maxFeePerGas = undefined + celoTx.maxPriorityFeePerGas = undefined + celoTx.feeCurrency = 'celoMagic' const newCeloTx = await populator.populate(celoTx) - expect(newCeloTx.gasPrice).toBe('0x27') - expect(mockRpcCall.mock.calls[0]).toEqual(['eth_gasPrice', []]) + expect(newCeloTx.maxFeePerGas).toBe('0x2f') + expect(newCeloTx.maxPriorityFeePerGas).toBe('0x27') + expect(mockRpcCall.mock.calls[0]).toEqual(['eth_gasPrice', ['celoMagic']]) + expect(mockRpcCall.mock.calls[1]).toEqual(['eth_maxPriorityFeePerGas', []]) }) }) }) diff --git a/packages/sdk/connect/src/utils/tx-params-normalizer.ts b/packages/sdk/connect/src/utils/tx-params-normalizer.ts index 7ef8168f67b..986bd3d4b6e 100644 --- a/packages/sdk/connect/src/utils/tx-params-normalizer.ts +++ b/packages/sdk/connect/src/utils/tx-params-normalizer.ts @@ -1,3 +1,4 @@ +import BigNumber from 'bignumber.js' import { Connection } from '../connection' import { CeloTx } from '../types' @@ -10,6 +11,9 @@ function isEmpty(value: string | undefined) { value.toLowerCase() === '0x0' ) } +function isPresent(value: string | undefined) { + return !isEmpty(value) +} export class TxParamsNormalizer { private chainId: number | null = null @@ -20,20 +24,73 @@ export class TxParamsNormalizer { public async populate(celoTxParams: CeloTx): Promise { const txParams = { ...celoTxParams } - if (txParams.chainId == null) { - txParams.chainId = await this.getChainId() + if (isPresent(txParams.gatewayFeeRecipient) || isPresent(txParams.gatewayFee)) { + console.warn( + 'Gateway fee has been deprecated and will be removed see: https://github.com/celo-org/celo-proposals/blob/master/CIPs/cip-0057.md' + ) } - if (txParams.nonce == null) { - txParams.nonce = await this.connection.nonce(txParams.from!.toString()) - } + const [chainId, nonce, gas, maxFeePerGas] = await Promise.all( + [ + async () => { + if (txParams.chainId == null) { + return this.getChainId() + } + return txParams.chainId + }, + async () => { + if (txParams.nonce == null) { + return this.connection.nonce(txParams.from!.toString()) + } + return txParams.nonce + }, + async () => { + if (!txParams.gas || isEmpty(txParams.gas.toString())) { + return this.connection.estimateGas(txParams) + } + return txParams.gas + }, + async () => { + // if gasPrice is not set and maxFeePerGas is not set, set maxFeePerGas + if ( + isEmpty(txParams.gasPrice?.toString()) && + isEmpty(txParams.maxFeePerGas?.toString()) + ) { + const suggestedPrice = await this.connection.gasPrice(txParams.feeCurrency) + // add small buffer to suggested price like other libraries do + const priceWithRoom = new BigNumber(suggestedPrice) + .times(120) + .dividedBy(100) + .integerValue() + .toString(16) + return `0x${priceWithRoom}` + } + return txParams.maxFeePerGas + }, + ].map(async (fn) => fn()) + ) + txParams.chainId = chainId as number + txParams.nonce = nonce as number + txParams.gas = gas as string + txParams.maxFeePerGas = maxFeePerGas - if (!txParams.gas || isEmpty(txParams.gas.toString())) { - txParams.gas = await this.connection.estimateGas(txParams) + // need to wait until after gas price has been handled + // if maxFeePerGas is set make sure maxPriorityFeePerGas is also set + if ( + isPresent(txParams.maxFeePerGas?.toString()) && + isEmpty(txParams.maxPriorityFeePerGas?.toString()) + ) { + const clientMaxPriorityFeePerGas = await this.connection.rpcCaller.call( + 'eth_maxPriorityFeePerGas', + [] + ) + txParams.maxPriorityFeePerGas = clientMaxPriorityFeePerGas.result } - if (!txParams.gasPrice || isEmpty(txParams.gasPrice.toString())) { - txParams.gasPrice = await this.connection.gasPrice(txParams.feeCurrency) + // remove gasPrice if maxFeePerGas is set + if (isPresent(txParams.gasPrice?.toString()) && isPresent(txParams.maxFeePerGas?.toString())) { + txParams.gasPrice = undefined + delete txParams.gasPrice } return txParams diff --git a/packages/sdk/contractkit/CHANGELOG.md b/packages/sdk/contractkit/CHANGELOG.md new file mode 100644 index 00000000000..511289cae7a --- /dev/null +++ b/packages/sdk/contractkit/CHANGELOG.md @@ -0,0 +1,41 @@ +# @celo/contractkit + +## 5.1.0 + +### Minor Changes + +- d48c68afc: Add MultiSig.getTransaction() now optionally takes a second boolean param to avoid fetching confirmations information +- d48c68afc: Add method getConfirmations() to Multisig Wrapper +- 53bbd4958: Add cip64 support for feeCurrency Transactions. Note this is the replacement for the deprecated cip42 and legacy tx types https://github.com/celo-org/celo-proposals/blob/master/CIPs/cip/0064.md + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- d48c68afc: parallelize async calls in Governance Wrapper +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [53bbd4958] + - @celo/connect@5.1.0 + - @celo/wallet-local@5.1.0 + - @celo/utils@5.0.5 + - @celo/base@5.0.5 + +## 5.1.0-beta.0 + +### Minor Changes + +- d48c68afc: Add MultiSig.getTransaction() now optionally takes a second boolean param to avoid fetching confirmations information +- d48c68afc: Add method getConfirmations() to Multisig Wrapper +- 53bbd4958: Add cip64 support for feeCurrency Transactions. Note this is the replacement for the deprecated cip42 and legacy tx types https://github.com/celo-org/celo-proposals/blob/master/CIPs/cip/0064.md + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- d48c68afc: parallelize async calls in Governance Wrapper +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [53bbd4958] + - @celo/connect@5.1.0-beta.0 + - @celo/wallet-local@5.1.0-beta.0 + - @celo/utils@5.0.5-beta.0 + - @celo/base@5.0.5-beta.0 diff --git a/packages/sdk/contractkit/MIGRATION-TO-ETHERS.md b/packages/sdk/contractkit/MIGRATION-TO-ETHERS.md new file mode 100644 index 00000000000..55d68442a1c --- /dev/null +++ b/packages/sdk/contractkit/MIGRATION-TO-ETHERS.md @@ -0,0 +1,108 @@ +# Migration document from Contractkit + +Hello devs 🌱 this is a migration path away from contractkit. This aims to give examples to help you move to [ethers](https://docs.ethers.org/). + +## Initialization + +```diff +- import Web3 from "web3"; +- import { newKitFromWeb3 } from "@celo/contractkit"; + +- const web3 = new Web3("https://alfajores-forno.celo-testnet.org"); +- const kit = newKitFromWeb3(web3); ++ import { providers } from 'ethers' ++ ++ const provider = new providers.JsonRpcProvider('https://alfajores-forno.celo-testnet.org') +``` + +## Basic usage + +While we cannot here show all the use-cases of contrackit or ethers or viem, let's try to give an overview of how they can be used for different goals. + +### Get address + +```diff +- const accounts = await kit.web3.eth.getAccounts(); ++ const accounts = await provider.listAccounts(); +const defaultAccount = accounts[0]; +``` + +### Get wallet + +```diff ++ import { Wallet } from 'ethers' + +- const wallet = kit.getWallet(); ++ const wallet = new Wallet('0x...', provider); +``` + +### Provider methods + +```diff +- const provider = kit.connection.web3.currentProvider +- kit.connection.getBlock(...) +- kit.connection.getTransaction(...) +provider.getBlock(...) +provider.getTransaction(...) +``` + +### Signer methods + +```diff +- const provider = kit.connection.web3.currentProvider +- const signer = provider.getSigner(kit.connection.defaultAccount) ++ const signer = provider.getSigner(address) ++ signer.sendTransaction(...) ++ signer.signMessage(...) +``` + +### Contract interaction + +I'll show the most "basic" interaction, which is a transfer. On CELO, it comes with a twist, you can transfer 4 currencies, CELO, cUSD, cEUR, and cREAL. + +You can get the addresses on these tokens by heading to the explorer and getting their abi and addresses, or you can also use our [registry contract](https://docs.celo.org/developer/contractkit/contracts-wrappers-registry). You can also use the [`@celo/abis`](https://www.npmjs.com/package/@celo/abis) package to get the ABIs directly. + +```ts +// this address is constant +const REGISTRY_CONTRACT_ADDRESS = '0x000000000000000000000000000000000000ce10' +const registry = new Contract(REGISTRY_CONTRACT_ADDRESS, registryAbi, wallet) + +async function getToken(token: string) { + const goldTokenAddress = await registry.getAddressForString(token) + return goldTokenAddress +} +async function CeloTokens(): Promise<[string, string][]> { + return Promise.all( + ['GoldToken', 'StableToken', 'StableTokenEUR', 'StableTokenBRL'].map(async (token) => [ + token, + await getToken(token), + ]) + ) +} +``` + +#### Balance + +```diff ++ import { tokenAbi } from './abi.json' + +- const contract = await kit.contracts.getGoldToken(); ++ const tokenAddress = '0x...' // Grabbed from the registry or from the explorer ++ const contract = new ethers.Contract(tokenAddress, tokenAbi, signer); +const balance = await contract.balanceOf(wallet.address); +``` + +#### Transfer + +Then, use the address of the token that you need and call the transfer method of the contract. + +```diff ++ import { tokenAbi } from './abi.json' + +- const contract = await kit.contracts.getGoldToken(); ++ const tokenAddress = '0x...' // Grabbed from the registry or from the explorer ++ const contract = new ethers.Contract(tokenAddress, tokenAbi, signer); +const txReceipt = await contract.transfer('0x...', amount); +``` + +For more in depth examples, I highly recommend checking out the extensive documentations of both [ethers](https://docs.ethers.org/) and [viem](https://viem.sh/). diff --git a/packages/sdk/contractkit/MIGRATION-TO-VIEM.md b/packages/sdk/contractkit/MIGRATION-TO-VIEM.md new file mode 100644 index 00000000000..494f158ce93 --- /dev/null +++ b/packages/sdk/contractkit/MIGRATION-TO-VIEM.md @@ -0,0 +1,232 @@ +# Migration document from Contractkit + +Hello devs 🌱 this is a migration path away from contractkit. This aims to give examples to help you move to [viem](https://viem.sh/). + +## Initialization + +```diff +- import Web3 from "web3"; +- import { newKitFromWeb3 } from "@celo/contractkit"; +- +- const web3 = new Web3("https://alfajores-forno.celo-testnet.org"); +- const kit = newKitFromWeb3(web3); ++ import { createPublicClient, http } from 'viem' ++ import { celo, celoAlfajores } from 'viem/chains' ++ ++ const publicClient = createPublicClient({ ++ chain: celoAlfajores, // or celo for celo's mainnet ++ transport: http() ++ }) +``` + +## Basic usage + +While we cannot here show all the use-cases of contrackit or ethers or viem, let's try to give an overview of how they can be used for different goals. + +### Get address + +With viem: + +```diff +- const accounts = await kit.web3.eth.getAccounts(); ++ const accounts = await publicClient.getAddresses() +const defaultAccount = accounts[0]; +``` + +### Get wallet + +With viem: + +> [viem does not full support](<[source](https://viem.sh/docs/ethers-migration.html#viem-11)>) client-side signing (it's coming shortly!) – until then, you can use an Ethers Wallet, however it does support `signMessage` and `signTypedData` already. + +```diff ++ import { privateKeyToAccount } from 'viem/accounts' ++ ++ const privateKey = "0x..."; ++ const walletClient = createWalletClient({ ++ transport: http(celoAlfajores.rpcUrls.default.http[0] as string), ++ chain: celoAlfajores, ++ }); ++ const account = privateKeyToAccount(privateKey); ++ await walletClient.signMessage({ ++ account, ++ message: 'hello world', ++ }) +``` + +### Provider methods + +```diff +- const provider = kit.connection.web3.currentProvider +- kit.connection.getBlock(...) +- kit.connection.getTransaction(...) ++ const block = await publicClient.getBlock() ++ /** ++ * { ++ * baseFeePerGas: 10000n, ++ * number: 1234n, ++ * parentHash: "0x....", ++ * ... ++ * } ++ */ ++ const tx = await publicClient.getTransaction({ ++ hash: "0x...", ++ }) ++ /** ++ * { ++ * blockHash: '0x...', ++ * blockNumber: 1234n, ++ * from: '0x...', ++ * ... ++ * } ++ */ +``` + +### Signer methods + +```diff +- const provider = kit.connection.web3.currentProvider +- const signer = provider.getSigner(kit.connection.defaultAccount) ++ const [account] = await walletClient.getAddresses() ++ const hash = await walletClient.sendTransaction({ account, to: "0x...", value: 1000n }) +``` + +### Contract interaction + +I'll show the most "basic" interaction, which is a transfer. On CELO, it comes with a twist, you can transfer 4 currencies, CELO, cUSD, cEUR, and cREAL. + +You can get the addresses on these tokens by heading to the explorer and getting their abi and addresses, or you can also use our [registry contract](https://docs.celo.org/developer/contractkit/contracts-wrappers-registry). You can also use the [`@celo/abis`](https://www.npmjs.com/package/@celo/abis) package to get the ABIs directly. + +```ts +import { getContract } from 'viem' +import { registryABI } from '@celo/abis/types/viem' + +// this address is constant +const REGISTRY_CONTRACT_ADDRESS = '0x000000000000000000000000000000000000ce10' +const registryContract = getContract({ + address: REGISTRY_CONTRACT_ADDRESS, + abi: registryABI, + publicClient, +}) + +async function CeloTokens(): Promise<[string, string][]> { + return Promise.all( + ['GoldToken', 'StableToken', 'StableTokenEUR', 'StableTokenBRL'].map(async (token) => [ + token, + await registryContract.read.getAddressForString(token), + ]) + ) +} +``` + +#### Balance + +```diff ++ import { stableTokenABI } from '@celo/abis/types/viem' + +- const contract = await kit.contracts.getGoldToken(); +- const balance = await contract.balanceOf(wallet.address); ++ const tokenAddresses = await CeloTokens(); ++ const cUSD = tokenAddresses["StableToken] ++ const balance = await client.readContract({ ++ abi: tokenAbi, ++ address: cUSD, ++ functionName: "balanceOf", ++ args: [account.address], ++ }); +``` + +#### Transfer + +Then, use the address of the token that you need and call the transfer method of the contract. + +```diff ++ import { stableTokenABI } from '@celo/abis/types/viem' +- const CELO = await kit.contracts.getGoldToken(); +- const txReceipt = await CELO.transfer('0x...', amount) ++ const tokenAddresses = await CeloTokens(); ++ const cUSD = tokenAddresses["StableToken] ++ const { request } = await walletClient.simulateContract({ ++ abi, ++ address: cUSD, ++ functionName: 'transfer', ++ args: [ ++ '0x...', // to address ++ amount: 1000n, ++ ], ++ account: '0x...', // from address ++ }) ++ const hash = await walletClient.sendTransaction(request); +``` + +#### Multicall + +While contractkit didn't directly support multicall, you could use libraries such as `@dopex-io/web3-multicall` as such: + +```ts +import Multicall from '@dopex-io/web3-multicall' +const MULTICALL_ADDRESS = '0xcA11bde05977b3631167028862bE2a173976CA11' // same on mainnet and alfajores +const multicall = new Multicall({ + provider, + chainId, + multicallAddress: MULTICALL_ADDRESS, +}) +const governance = await kit.contracts._web3Contracts.getGovernance() +const stageTxs = _dequeue.map((proposalId) => governance.methods.getProposalStage(proposalId)) +const stages: string[] = await multicall.aggregate(stageTxs) +``` + +You now can use `viem` directly to multicall since they have the address configured in the `viem/chains` files. + +```ts +const governanceAddress = await registryContract.read.getAddressForString(['Governance']) +const governanceContract = getContract({ + address: governanceAddress, + abi: governanceABI, + publicClient, +}) +const _dequeue = await governanceContract.read.getDequeue() +const stageCalls = _dequeue.map((proposalId) => ({ + address: governanceAddress, + abi: governanceABI, + functionName: 'getProposalStage', + args: [proposalId], +})) +const stages = (await publicClient.multicall({ contracts: stageCalls })).map((x) => x.result) +``` + +#### Fee Currency + +With Viem's built in Celo transaction serializer and Celo block/transaction formatters it is easy to build a wallet that supports Celo's ability to pay gas fees with various erc20 tokens. Simply, import a Celo chain from `viem/chain` and pass it to Viem's `createWalletClient`. Once the client is created you can add the feeCurrency field to your transaction with the address of the token you want to use for gas. + +```ts + import { celo } from 'viem/chains' + import { createWalletClient, privateKeyToAccount, type SendTransactionParameters, http } from 'viem' + + const account = privateKeyToAccount(PRIVATE_KEY) + + // ALFAJORES ADDRESS: Celo Mainnet can be fetched from the registry + const cUSDAddress = '0x874069Fa1Eb16D44d622F2e0Ca25eeA172369bC1' + + const localAccountClient = createWalletClient({ + account, + chain: celo, + }) + + const sendTransaction = (tx: SendTransactionParameters) => { + return localAccountClient.sendTransaction(tx) + } + + const hash = await sendTransaction({ + feeCurrency: cUSDAddress, + value: BigInt(100000000), + to: '0x22579CA45eE22E2E16dDF72D955D6cf4c767B0eF', + }) +``` + +### Further reading + +For more in depth examples and documentation about viem specifically, I highly recommend checking out the extensive documentations of [viem](https://viem.sh/). + +Another interesting application to help you migrate could be StCelo-v2. +You can checkout the changes going from `react-celo` + `contractkit` to `rainbowkit` + `wagmi` + `viem` in [this pull-request](https://github.com/celo-org/staked-celo-web-app/pull/129). diff --git a/packages/sdk/contractkit/jest.config.js b/packages/sdk/contractkit/jest.config.js index 597915ba69b..5ce2d268625 100644 --- a/packages/sdk/contractkit/jest.config.js +++ b/packages/sdk/contractkit/jest.config.js @@ -1,14 +1,7 @@ -const { nodeFlakeTracking } = require('@celo/flake-tracker/src/jest/config.js') - module.exports = { preset: 'ts-jest', - ...nodeFlakeTracking, testMatch: ['/src/**/?(*.)+(spec|test).ts?(x)'], - setupFilesAfterEnv: [ - '@celo/dev-utils/lib/matchers', - '/jest_setup.ts', - ...nodeFlakeTracking.setupFilesAfterEnv, - ], + setupFilesAfterEnv: ['@celo/dev-utils/lib/matchers', '/jest_setup.ts'], globalSetup: '/src/test-utils/setup.global.ts', globalTeardown: '/src/test-utils/teardown.global.ts', testSequencer: '/src/test-utils/AlphabeticSequencer.js', diff --git a/packages/sdk/contractkit/package.json b/packages/sdk/contractkit/package.json index e839ca8410f..abc3c199893 100644 --- a/packages/sdk/contractkit/package.json +++ b/packages/sdk/contractkit/package.json @@ -1,6 +1,6 @@ { "name": "@celo/contractkit", - "version": "4.1.1-dev", + "version": "5.1.0", "description": "Celo's ContractKit to interact with Celo network", "main": "./lib/index.js", "types": "./lib/index.d.ts", @@ -29,10 +29,10 @@ "lint": "tslint -c tslint.json --project ." }, "dependencies": { - "@celo/base": "4.1.1-dev", - "@celo/connect": "4.1.1-dev", - "@celo/utils": "4.1.1-dev", - "@celo/wallet-local": "4.1.1-dev", + "@celo/base": "^5.0.5", + "@celo/connect": "^5.1.0", + "@celo/utils": "^5.0.5", + "@celo/wallet-local": "^5.1.0", "@types/bn.js": "^5.1.0", "@types/debug": "^4.1.5", "bignumber.js": "^9.0.0", @@ -41,18 +41,19 @@ "fp-ts": "2.1.1", "io-ts": "2.0.1", "semver": "^7.3.5", - "web3": "1.10.0" + "web3": "1.10.0", + "web3-core-helpers": "1.10.0" }, "devDependencies": { - "@celo/phone-utils": "4.1.1-dev", - "@celo/dev-utils": "0.0.1-dev", - "@celo/flake-tracker": "0.0.1-dev", - "@celo/protocol": "1.0.0", + "@celo/phone-utils": "^5.0.5", + "@celo/dev-utils": "0.0.1", + "@celo/protocol": "1.0.1", "@types/debug": "^4.1.5", "fetch-mock": "9.10.4", - "ganache": "npm:@soloseng/ganache@7.8.0-beta.1", + "ganache": "npm:@celo/ganache@7.8.0-unofficial.0", "jest": "^29.0.2", - "ts-node": "8.3.0", + "bn.js": "^5.1.0", + "ts-node": "^10.9.1", "typedoc": "^0.19.2", "typedoc-plugin-markdown": "^2.2.16", "xhr2-cookies": "1.1.0" diff --git a/packages/sdk/contractkit/src/kit.test.ts b/packages/sdk/contractkit/src/kit.test.ts index 951e7fd056a..f2d76946676 100644 --- a/packages/sdk/contractkit/src/kit.test.ts +++ b/packages/sdk/contractkit/src/kit.test.ts @@ -1,5 +1,4 @@ import { CeloTx, CeloTxObject, CeloTxReceipt, JsonRpcPayload, PromiEvent } from '@celo/connect' -import { BigNumber } from 'bignumber.js' import Web3 from 'web3' import { HttpProvider } from 'web3-core' import { newKitFromWeb3 as newFullKitFromWeb3, newKitWithApiKey } from './kit' @@ -79,31 +78,46 @@ export function txoStub(): TransactionObjectStub { const txo = txoStub() await kit.connection.sendTransactionObject(txo, { gas: 555, from: '0xAAFFF' }) expect(txo.send).toBeCalledWith({ - gasPrice: '0', + feeCurrency: undefined, gas: 555, from: '0xAAFFF', }) }) - }) -}) -test('should retrieve currency gasPrice with feeCurrency', async () => { - const kit = newFullKitFromWeb3(new Web3('http://')) + test('works with maxFeePerGas and maxPriorityFeePerGas', async () => { + const txo = txoStub() + await kit.connection.sendTransactionObject(txo, { + gas: 1000, + maxFeePerGas: 555, + maxPriorityFeePerGas: 555, + from: '0xAAFFF', + }) + expect(txo.send).toBeCalledWith({ + feeCurrency: undefined, + maxFeePerGas: 555, + maxPriorityFeePerGas: 555, + gas: 1000, + from: '0xAAFFF', + }) + }) - const txo = txoStub() - const gasPrice = 100 - const getGasPriceMin = jest.fn().mockImplementation(() => ({ - getGasPriceMinimum() { - return new BigNumber(gasPrice) - }, - })) - kit.contracts.getGasPriceMinimum = getGasPriceMin.bind(kit.contracts) - await kit.updateGasPriceInConnectionLayer('XXX') - const options: CeloTx = { gas: 555, feeCurrency: 'XXX', from: '0xAAFFF' } - await kit.connection.sendTransactionObject(txo, options) - expect(txo.send).toBeCalledWith({ - gasPrice: `${gasPrice * 5}`, - ...options, + test('when maxFeePerGas and maxPriorityFeePerGas and feeCurrency', async () => { + const txo = txoStub() + await kit.connection.sendTransactionObject(txo, { + gas: 1000, + maxFeePerGas: 555, + maxPriorityFeePerGas: 555, + feeCurrency: '0xe8537a3d056da446677b9e9d6c5db704eaab4787', + from: '0xAAFFF', + }) + expect(txo.send).toBeCalledWith({ + gas: 1000, + maxFeePerGas: 555, + maxPriorityFeePerGas: 555, + feeCurrency: '0xe8537a3d056da446677b9e9d6c5db704eaab4787', + from: '0xAAFFF', + }) + }) }) }) diff --git a/packages/sdk/contractkit/src/kit.ts b/packages/sdk/contractkit/src/kit.ts index 49ec754fc21..5c47fdb22c0 100644 --- a/packages/sdk/contractkit/src/kit.ts +++ b/packages/sdk/contractkit/src/kit.ts @@ -1,3 +1,4 @@ +// tslint:disable: ordered-imports import { Address, CeloTx, @@ -16,9 +17,9 @@ import { CeloContract, CeloTokenContract } from './base' import { CeloTokens, EachCeloToken } from './celo-tokens' import { ValidWrappers, WrapperCache } from './contract-cache' import { + HttpProviderOptions, ensureCurrentProvider, getWeb3ForKit, - HttpProviderOptions, setupAPIKey, } from './setupForKits' import { Web3ContractCache } from './web3-contract-cache' @@ -200,20 +201,9 @@ export class ContractKit { tokenContract === CeloContract.GoldToken ? undefined : await this.registry.addressFor(tokenContract) - if (address) { - await this.updateGasPriceInConnectionLayer(address) - } this.connection.defaultFeeCurrency = address } - /** @deprecated no longer needed since gasPrice is available on node rpc */ - async updateGasPriceInConnectionLayer(currency: Address) { - const gasPriceMinimum = await this.contracts.getGasPriceMinimum() - const rawGasPrice = await gasPriceMinimum.getGasPriceMinimum(currency) - const gasPrice = rawGasPrice.multipliedBy(this.gasPriceSuggestionMultiplier).toFixed() - await this.connection.setGasPriceForCurrency(currency, gasPrice) - } - async getEpochSize(): Promise { const blockchainParamsWrapper = await this.contracts.getBlockchainParameters() return blockchainParamsWrapper.getEpochSizeNumber() @@ -258,14 +248,6 @@ export class ContractKit { return this.connection.defaultGasInflationFactor } - set gasPrice(price: number) { - this.connection.defaultGasPrice = price - } - - get gasPrice() { - return this.connection.defaultGasPrice - } - set defaultFeeCurrency(address: Address | undefined) { this.connection.defaultFeeCurrency = address } @@ -281,13 +263,6 @@ export class ContractKit { isSyncing(): Promise { return this.connection.isSyncing() } - /** @deprecated no longer needed since gasPrice is available on node rpc */ - async fillGasPrice(tx: CeloTx): Promise { - if (tx.feeCurrency && tx.gasPrice === '0') { - await this.updateGasPriceInConnectionLayer(tx.feeCurrency) - } - return this.connection.fillGasPrice(tx) - } async sendTransaction(tx: CeloTx): Promise { return this.connection.sendTransaction(tx) diff --git a/packages/sdk/contractkit/src/setupForKits.ts b/packages/sdk/contractkit/src/setupForKits.ts index 61d1756dbfc..4a27513da6e 100644 --- a/packages/sdk/contractkit/src/setupForKits.ts +++ b/packages/sdk/contractkit/src/setupForKits.ts @@ -1,5 +1,5 @@ import Web3 from 'web3' -import { HttpProviderOptions as Web3HttpProviderOptions } from 'web3-providers-http' +import { HttpProviderOptions as Web3HttpProviderOptions } from 'web3-core-helpers' export type HttpProviderOptions = Web3HttpProviderOptions export const API_KEY_HEADER_KEY = 'apiKey' diff --git a/packages/sdk/contractkit/src/test-utils/setup.global.ts b/packages/sdk/contractkit/src/test-utils/setup.global.ts index 96fc3d25bdf..5d50245d7ff 100644 --- a/packages/sdk/contractkit/src/test-utils/setup.global.ts +++ b/packages/sdk/contractkit/src/test-utils/setup.global.ts @@ -2,15 +2,12 @@ import baseSetup from '@celo/dev-utils/lib/ganache-setup' // Has to import the matchers somewhere so that typescript knows the matchers have been made available import _unused from '@celo/dev-utils/lib/matchers' import { waitForPortOpen } from '@celo/dev-utils/lib/network' -// @ts-ignore -import flakeTrackerSetup from '@celo/flake-tracker/src/jest/setup.global' import * as path from 'path' // Warning: There should be an unused import of '@celo/dev-utils/lib/matchers' above. // If there is not, then your editor probably deleted it automatically. export default async function globalSetup() { - await flakeTrackerSetup() console.log('\nstarting ganache...') await baseSetup(path.resolve(path.join(__dirname, '../..')), '.tmp/devchain.tar.gz', { from_targz: true, diff --git a/packages/sdk/contractkit/src/test-utils/teardown.global.ts b/packages/sdk/contractkit/src/test-utils/teardown.global.ts index c85f17c2e77..aea64f79dbb 100644 --- a/packages/sdk/contractkit/src/test-utils/teardown.global.ts +++ b/packages/sdk/contractkit/src/test-utils/teardown.global.ts @@ -1,8 +1,5 @@ import teardown from '@celo/dev-utils/lib/ganache-teardown' -// @ts-ignore -import flakeTrackerTeardown from '@celo/flake-tracker/src/jest/teardown.global.js' export default async function globalTeardown() { - await flakeTrackerTeardown() await teardown() } diff --git a/packages/sdk/contractkit/src/wrappers/Accounts.ts b/packages/sdk/contractkit/src/wrappers/Accounts.ts index ef3c447701f..1a5671982f1 100644 --- a/packages/sdk/contractkit/src/wrappers/Accounts.ts +++ b/packages/sdk/contractkit/src/wrappers/Accounts.ts @@ -8,7 +8,7 @@ import { } from '@celo/utils/lib/signatureUtils' import { soliditySha3 } from '@celo/utils/lib/solidity' import { authorizeSigner as buildAuthorizeSignerTypedData } from '@celo/utils/lib/typed-data-constructors' -import BN from 'bn.js' // just the types +import type BN from 'bn.js' // just the types import { Accounts } from '../generated/Accounts' import { newContractVersion } from '../versions' import { diff --git a/packages/sdk/contractkit/src/wrappers/Governance.ts b/packages/sdk/contractkit/src/wrappers/Governance.ts index 895c926f53a..6cc2c23e58c 100644 --- a/packages/sdk/contractkit/src/wrappers/Governance.ts +++ b/packages/sdk/contractkit/src/wrappers/Governance.ts @@ -211,7 +211,10 @@ export class GovernanceWrapper extends BaseWrapperForGoverning { * @param proposal Proposal to determine the constitution for running. */ async getConstitution(proposal: Proposal): Promise { - let constitution = new BigNumber(0) + // Default value that is harcoded on Governance contract + // it's 0.5 in Fixidity + // https://github.com/celo-org/celo-monorepo/blob/3fffa158d67ffd6366e81ba7243eadede1974b1b/packages/protocol/contracts/governance/Governance.sol#L39 + let constitution = fromFixed(new BigNumber('500000000000000000000000')) for (const tx of proposal) { constitution = BigNumber.max(await this.getTransactionConstitution(tx), constitution) } @@ -236,17 +239,19 @@ export class GovernanceWrapper extends BaseWrapperForGoverning { // in the total of yes votes required async getSupportWithConstitutionThreshold(proposalID: BigNumber.Value, constitution: BigNumber) { const support = await this.getSupport(proposalID) - support.required = support.required.times(constitution) + support.required = support.required.times(constitution).integerValue() return support } // simulates proposal.getSupportWithQuorumPadding async getSupport(proposalID: BigNumber.Value) { - const participation = await this.getParticipationParameters() + const [participation, votes, lockedGold] = await Promise.all([ + this.getParticipationParameters(), + this.getVotes(proposalID), + this.contracts.getLockedGold(), + ]) const quorum = participation.baseline.times(participation.baselineQuorumFactor) - const votes = await this.getVotes(proposalID) const total = votes.Yes.plus(votes.No).plus(votes.Abstain) - const lockedGold = await this.contracts.getLockedGold() // NOTE: this networkWeight is not as governance calculates it, // but we don't have access to proposal.networkWeight const networkWeight = await lockedGold.getTotalLockedGold() @@ -452,11 +457,18 @@ export class GovernanceWrapper extends BaseWrapperForGoverning { } async getApprovalStatus(proposalID: BigNumber.Value): Promise { - const multisig = await this.getApproverMultisig() - const approveTx = await this.approve(proposalID) - const multisigTxs = await multisig.getTransactionDataByContent(this.address, approveTx.txo) + const [multisig, approveTx] = await Promise.all([ + this.getApproverMultisig(), + this.approve(proposalID), + ]) + + const [multisigTxs, approvers] = await Promise.all([ + multisig.getTransactionDataByContent(this.address, approveTx.txo), + multisig.getOwners() as Promise, + ]) + const confirmations = multisigTxs ? multisigTxs.confirmations : [] - const approvers = await multisig.getOwners() + return { completion: `${confirmations.length} / ${approvers.length}`, confirmations, @@ -469,9 +481,11 @@ export class GovernanceWrapper extends BaseWrapperForGoverning { * @param proposalID Governance proposal UUID */ async getProposalRecord(proposalID: BigNumber.Value): Promise { - const metadata = await this.getProposalMetadata(proposalID) - const proposal = await this.getProposal(proposalID) - const stage = await this.getProposalStage(proposalID) + const [proposal, metadata, stage] = await Promise.all([ + this.getProposal(proposalID), + this.getProposalMetadata(proposalID), + this.getProposalStage(proposalID), + ]) const record: ProposalRecord = { proposal, @@ -484,13 +498,17 @@ export class GovernanceWrapper extends BaseWrapperForGoverning { if (stage === ProposalStage.Queued) { record.upvotes = await this.getUpvotes(proposalID) } else if (stage === ProposalStage.Referendum || stage === ProposalStage.Execution) { - record.approved = true - record.passed = await this.isProposalPassing(proposalID) - record.votes = await this.getVotes(proposalID) - record.approved = await this.isApproved(proposalID) - record.approvals = await this.getApprovalStatus(proposalID) + const [passed, votes, approved, approvals] = await Promise.all([ + this.isProposalPassing(proposalID) as Promise, + this.getVotes(proposalID), + this.isApproved(proposalID), + this.getApprovalStatus(proposalID), + ]) + record.passed = passed as boolean + record.votes = votes + record.approved = approved + record.approvals = approvals } - return record } diff --git a/packages/sdk/contractkit/src/wrappers/LockedGold.test.ts b/packages/sdk/contractkit/src/wrappers/LockedGold.test.ts index eb16e538c97..07950be8bce 100644 --- a/packages/sdk/contractkit/src/wrappers/LockedGold.test.ts +++ b/packages/sdk/contractkit/src/wrappers/LockedGold.test.ts @@ -43,4 +43,32 @@ testWithGanache('LockedGold Wrapper', (web3) => { await Promise.all(txos.map((txo) => txo.sendAndWaitForReceipt())) // }) + + test('should return the count of pending withdrawals', async () => { + await lockedGold.lock().sendAndWaitForReceipt({ value: value * 2 }) + await lockedGold.unlock(value).sendAndWaitForReceipt() + await lockedGold.unlock(value).sendAndWaitForReceipt() + + const count = await lockedGold.getTotalPendingWithdrawalsCount(account) + expect(count).toEqBigNumber(2) + }) + + test('should return zero when there are no pending withdrawals', async () => { + const count = await lockedGold.getTotalPendingWithdrawalsCount(account) + expect(count).toEqBigNumber(0) + }) + + test('should return the pending withdrawal at a given index', async () => { + await lockedGold.lock().sendAndWaitForReceipt({ value: value * 2 }) + await lockedGold.unlock(value).sendAndWaitForReceipt() + const pendingWithdrawal = await lockedGold.getPendingWithdrawal(account, 0) + + expect(pendingWithdrawal.value).toEqBigNumber(value) + }) + + test('should throw an error for an invalid index', async () => { + await expect(lockedGold.getPendingWithdrawal(account, 999)).rejects.toThrow( + 'Bad pending withdrawal index' + ) + }) }) diff --git a/packages/sdk/contractkit/src/wrappers/LockedGold.ts b/packages/sdk/contractkit/src/wrappers/LockedGold.ts index e0497344b32..806d73f5078 100644 --- a/packages/sdk/contractkit/src/wrappers/LockedGold.ts +++ b/packages/sdk/contractkit/src/wrappers/LockedGold.ts @@ -236,6 +236,21 @@ export class LockedGoldWrapper extends BaseWrapperForGoverning { ) } + /** + * Returns the pending withdrawal at a given index for a given account. + * @param account The address of the account. + * @param index The index of the pending withdrawal. + * @return The value of the pending withdrawal. + * @return The timestamp of the pending withdrawal. + */ + async getPendingWithdrawal(account: string, index: number) { + const response = await this.contract.methods.getPendingWithdrawal(account, index).call() + return { + value: valueToBigNumber(response[0]), + time: valueToBigNumber(response[1]), + } + } + /** * Retrieves AccountSlashed for epochNumber. * @param epochNumber The epoch to retrieve AccountSlashed at. @@ -314,6 +329,17 @@ export class LockedGoldWrapper extends BaseWrapperForGoverning { } return res } + + /** + * Returns the number of pending withdrawals for the specified account. + * @param account The account. + * @returns The count of pending withdrawals. + */ + getTotalPendingWithdrawalsCount = proxyCall( + this.contract.methods.getTotalPendingWithdrawalsCount, + undefined, + valueToBigNumber + ) } export type LockedGoldWrapperType = LockedGoldWrapper diff --git a/packages/sdk/contractkit/src/wrappers/MultiSig.ts b/packages/sdk/contractkit/src/wrappers/MultiSig.ts index 9cecd439041..5944ea4bb43 100644 --- a/packages/sdk/contractkit/src/wrappers/MultiSig.ts +++ b/packages/sdk/contractkit/src/wrappers/MultiSig.ts @@ -19,6 +19,12 @@ export interface TransactionData { executed: boolean confirmations: string[] } +export interface TransactionDataWithOutConfirmations { + destination: string + value: BigNumber + data: string + executed: boolean +} /** * Contract for handling multisig actions @@ -30,11 +36,7 @@ export class MultiSigWrapper extends BaseWrapper { * Otherwise, submits the `txObject` to the multisig and add confirmation. * @param index The index of the pending withdrawal to withdraw. */ - async submitOrConfirmTransaction( - destination: string, - txObject: CeloTxObject, - value: string = '0' - ) { + async submitOrConfirmTransaction(destination: string, txObject: CeloTxObject, value = '0') { const data = stringToSolidityBytes(txObject.encodeABI()) const transactionCount = await this.contract.methods.getTransactionCount(true, true).call() let transactionId @@ -81,35 +83,75 @@ export class MultiSigWrapper extends BaseWrapper { ) { const data = stringToSolidityBytes(txo.encodeABI()) const transactionCount = await this.getTransactionCount(true, true) - // reverse order for recency - for (let transactionId = transactionCount - 1; transactionId >= 0; transactionId--) { - const tx = await this.getTransaction(transactionId) - if (tx.data === data && tx.destination === destination && tx.value.isEqualTo(value)) { - return tx - } + const transactionsOrEmpties = await Promise.all( + Array(transactionCount) + .fill(0) + .map(async (_, index) => { + const tx = await this.getTransaction(index, false) + if (tx.data === data && tx.destination === destination && tx.value.isEqualTo(value)) { + return { index, ...tx } + } + return null + }) + ) + const wantedTransaction = transactionsOrEmpties.find((tx) => tx !== null) + if (!wantedTransaction) { + return + } + const confirmations = await this.getConfirmations(wantedTransaction.index) + return { + ...wantedTransaction, + confirmations, } - return undefined } - - async getTransaction(i: number): Promise { + async getTransaction(i: number): Promise + async getTransaction( + i: number, + includeConfirmations: false + ): Promise + async getTransaction(i: number, includeConfirmations = true) { const { destination, value, data, executed } = await this.contract.methods .transactions(i) .call() - const confirmations = [] - for (const e of await this.getOwners()) { - if (await this.contract.methods.confirmations(i, e).call()) { - confirmations.push(e) + if (!includeConfirmations) { + return { + destination, + data, + executed, + value: new BigNumber(value), } } + + const confirmations = await this.getConfirmations(i) return { + confirmations, destination, data, executed, - confirmations, value: new BigNumber(value), } } + /* + * Returns array of signer addresses which have confirmed a transaction + * when given the index of that transaction. + */ + async getConfirmations(txId: number) { + const owners = await this.getOwners() + const confirmationsOrEmpties = await Promise.all( + owners.map(async (owner) => { + const confirmation = await this.contract.methods.confirmations(txId, owner).call() + if (confirmation) { + return owner + } else { + return null + } + }) + ) + const confirmations = confirmationsOrEmpties.filter((c) => c !== null) as string[] + return confirmations + } + async getTransactions(): Promise { const txcount = await this.totalTransactionCount() const res: TransactionData[] = [] diff --git a/packages/sdk/cryptographic-utils/CHANGELOG.md b/packages/sdk/cryptographic-utils/CHANGELOG.md new file mode 100644 index 00000000000..f2b40e95d0d --- /dev/null +++ b/packages/sdk/cryptographic-utils/CHANGELOG.md @@ -0,0 +1,19 @@ +# @celo/cryptographic-utils + +## 5.0.5 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- Updated dependencies [53bbd4958] + - @celo/utils@5.0.5 + - @celo/base@5.0.5 + +## 5.0.5-beta.0 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- Updated dependencies [53bbd4958] + - @celo/utils@5.0.5-beta.0 + - @celo/base@5.0.5-beta.0 diff --git a/packages/sdk/cryptographic-utils/jest.config.js b/packages/sdk/cryptographic-utils/jest.config.js index 5ac89f2d41c..8d5ec6e8ff7 100644 --- a/packages/sdk/cryptographic-utils/jest.config.js +++ b/packages/sdk/cryptographic-utils/jest.config.js @@ -1,7 +1,4 @@ -const { nodeFlakeTracking } = require('@celo/flake-tracker/src/jest/config.js') - module.exports = { preset: 'ts-jest', testMatch: ['/src/**/?(*.)+(spec|test).ts'], - ...nodeFlakeTracking, } diff --git a/packages/sdk/cryptographic-utils/package.json b/packages/sdk/cryptographic-utils/package.json index 5a58f85c10d..44846aa092b 100644 --- a/packages/sdk/cryptographic-utils/package.json +++ b/packages/sdk/cryptographic-utils/package.json @@ -1,6 +1,6 @@ { "name": "@celo/cryptographic-utils", - "version": "4.1.1-dev", + "version": "5.0.5", "description": "Some Celo utils for comment/data encryption, bls, and mnemonics", "author": "Celo", "license": "Apache-2.0", @@ -22,9 +22,9 @@ "lib/**/*" ], "dependencies": { - "@celo/utils": "4.1.1-dev", + "@celo/utils": "^5.0.5", "@celo/bls12377js": "0.1.1", - "@celo/base": "4.1.1-dev", + "@celo/base": "^5.0.5", "@ethereumjs/util": "8.0.5", "@types/bn.js": "^5.1.0", "@types/elliptic": "^6.4.9", @@ -36,10 +36,10 @@ "buffer-reverse": "^1.0.1", "elliptic": "^6.5.4", "ethereum-cryptography": "1.2.0", + "randombytes": "^2.0.1", "tiny-secp256k1": "2.2.1" }, "devDependencies": { - "@celo/flake-tracker": "0.0.1-dev", "@celo/typescript": "0.0.1" } } diff --git a/packages/sdk/cryptographic-utils/src/types.d.ts b/packages/sdk/cryptographic-utils/src/types.d.ts new file mode 100644 index 00000000000..e54094850fc --- /dev/null +++ b/packages/sdk/cryptographic-utils/src/types.d.ts @@ -0,0 +1 @@ +declare module 'bip39' diff --git a/packages/sdk/encrypted-backup/.gitignore b/packages/sdk/encrypted-backup/.gitignore deleted file mode 100644 index 7fabe89f619..00000000000 --- a/packages/sdk/encrypted-backup/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -lib/ -tmp/ -.tmp/ -.env \ No newline at end of file diff --git a/packages/sdk/encrypted-backup/.npmignore b/packages/sdk/encrypted-backup/.npmignore deleted file mode 100644 index 45e506bacd1..00000000000 --- a/packages/sdk/encrypted-backup/.npmignore +++ /dev/null @@ -1,17 +0,0 @@ -/.devchain/ -/.devchain.tar.gz -/coverage/ -/node_modules/ -/src/ -/tmp/ -/.tmp/ - -/tslint.json -/tsconfig.* -/jest.config.* -*.tgz - -/src - -/lib/**/*.test.* -/lib/test-utils \ No newline at end of file diff --git a/packages/sdk/encrypted-backup/README.md b/packages/sdk/encrypted-backup/README.md deleted file mode 100644 index 1396599940c..00000000000 --- a/packages/sdk/encrypted-backup/README.md +++ /dev/null @@ -1,125 +0,0 @@ -# Pin/Password-Encrypted Backup SDK - -## Overview - -This package provides an SDK for creating PIN/password encrypted user data backups, as is used in -the [PIN/Password Encrypted Account Recovery (PEAR)]. - -[PIN/Password Encrypted Account Recovery (PEAR)]: https://docs.celo.org/celo-codebase/protocol/identity/encrypted-cloud-backup - -It includes functionality to create, serialize, deserialize, and open encrypted backups. - -**Note that this SDK relies on the [Domains extension] to ODIS, which is not currently deployed to -the Mainnet operators** - -[Domains extension]: https://docs.celo.org/celo-codebase/protocol/odis/domains - -Developers can integrate with two preconfigured encryption profiles with the functions, -`createPinEncryptedBackup` and `createPasswordEncryptedBackup` which apply recommended levels of -hardening to generate the backup encryption key from the given PIN or password respectively. - -### Configuration - -Developers who want more control over the security parameters and user experience trade-offs (e.g. -more restrictive rate limiting for higher security at the cost of higher friction), may define their -own hardening configurations and use the `createBackup` function to build those backups. - -If the available ODIS hardening profiles do not work for your use case, the ODIS [Domains extension] -which underlies the key hardening is designed to be readily extensible. Feel free to propose a new -rate limiting function by filing an issue or opening a PR. You can read [CIP-40] for more -information about what is possible. - -[Domains extension]: https://docs.celo.org/celo-codebase/protocol/odis/domains -[CIP-40]: https://github.com/celo-org/celo-proposals/blob/master/CIPs/cip-0040.md - -### Key hardening - -PIN or password derived encryption keys are not secure by default because these secrets do not have -enough entropy (i.e. they are too easy to guess). As a result key hardening is required to ensure -the derived key is strong enough to withstand a motivated attackers. - -### ODIS - -The primary method of key hardening used in this SDK is the ODIS [POPRF] functionality, exposed -through the [Domains extension] for [key hardening]. This uses ODIS as a "hashing service" with an -applied rate limit that is configurable by the client. The enforcement of the rate limit makes -guessing a password or PIN infeasible by greatly restricting the amount of attempts an attacker gets -to guess the key. This necessarily also limits the number of attempts the user gets. - -[POPRF]: https://github.com/celo-org/celo-poprf-rs -[Domains extension]: https://docs.celo.org/celo-codebase/protocol/odis/domains -[key hardening]: https://docs.celo.org/celo-codebase/protocol/odis/use-cases/key-hardening - -### Additional hardening - -In addition to ODIS, this library supports two more sources of key hardening. - -First, it supports computational key hardening through `scrypt` or `PBKDF2`. These functions impose -a computational cost on checking a password guess. For secrets such as passwords with a moderate -amount of entropy, this can be a useful line of defense in preventing key cracking. The default -password hardening configuration includes `scrypt` hardening. In the case of low entropy secrets -such as PINs, it does not provide a meaningful increase in security. - -Second, it supports the use of a "circuit breaker" service. When enabled, the key generation process -will include a random "fuse key". Once mixed into the encryption key, the fuse key is encrypted to -the public key of the circuit breaker service and then discarded. The fuse key ciphertext is -included in the backup. Under normal circumstances, the user can send the fuse key ciphertext to the -circuit breaker service to have it decrypted. If ODIS is ever believed to be compromised, the -circuit breaker operator will disable this service, disabling recovery of the fuse key and -decryption of the backup data. Using this service represents a trade-off of electing a third-party -with the right to disable backup access for the benefit of security against attackers that might -successfully compromise ODIS. The PIN default hardening configuration includes this service, -electing [Valora] as the circuit breaker operator. - -[Valora]: https://valoraapp.com/ - -## Documentation - -The best resource for understanding the library is the inline documentation in [backup.ts] or the -SDK documentation at [celo-sdk-docs.readthedocs.io]. - -[backup.ts]: src/backup.ts -[celo-sdk-docs.readthedocs.io]: https://celo-sdk-docs.readthedocs.io/en/latest/encrypted-backup/ - -## Examples - -### Creating a backup - -In order to create a backup, the application should serialize the account data (e.g. seed phrase, user -name, wallet metadata, etc) into a `Buffer` and obtain a PIN or password from the user. In this -example, we use a PIN. - -Calling `createPinEncryptedBackup` will use ODIS for key hardening, add a circuit breaker key, and -bundle the result into a `Backup` object. The application should store this data somewhere the user -can access it for recovery. Although it is encrypted, it should not be exposed publicly and -instead should be stored in consumer cloud storage like Google Drive or iCloud, or on the -application servers with some application specific authentication. - -```typescript -import { createPinEncryptedBackup } from '@celo/encrypted-backup' - -const backup = await createPinEncryptedBackup(accountData, userPin) -if (!backup.ok) { - // handle backup.error -} -storeUserBackup(backup.result) -``` - -### Opening a backup - -In order to open the backup, the application should fetch the user backup and ask the user to enter -their PIN or password. Calling `openBackup` will read the backup to determine what hardening was -applied, the make the required calls ODIS, a circuit breaker service, and computational hardening to -recover the encryption key and decrypt the backup. The backup data can then be used to restore the -user account. - -```typescript -import { openBackup } from '@celo/encrypted-backup' - -const backup = await fetchUserBackup() -const accountData = await openBackup(backup, userPin) -if (!accountKey.ok) { - // handle backup.error -} -restoreAccount(accountData.result) -``` diff --git a/packages/sdk/encrypted-backup/jest.config.js b/packages/sdk/encrypted-backup/jest.config.js deleted file mode 100644 index ea4971b7402..00000000000 --- a/packages/sdk/encrypted-backup/jest.config.js +++ /dev/null @@ -1,13 +0,0 @@ -const { nodeFlakeTracking } = require('@celo/flake-tracker/src/jest/config.js') - -module.exports = { - preset: 'ts-jest', - ...nodeFlakeTracking, - testMatch: ['/src/**/?(*.)+(spec|test).ts?(x)'], - setupFilesAfterEnv: [ - '@celo/dev-utils/lib/matchers', - '/jestSetup.ts', - ...nodeFlakeTracking.setupFilesAfterEnv, - ], - verbose: true, -} diff --git a/packages/sdk/encrypted-backup/jestSetup.ts b/packages/sdk/encrypted-backup/jestSetup.ts deleted file mode 100644 index 0e7775597a6..00000000000 --- a/packages/sdk/encrypted-backup/jestSetup.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Setup mock for the fetch API to intercept requests to ODIS and the circuit breaker service. -// cross-fetch is used by the @celo/identity library. -const fetchMockSandbox = require('fetch-mock').sandbox() -jest.mock('cross-fetch', () => fetchMockSandbox) - -// @ts-ignore -global.fetchMock = fetchMockSandbox diff --git a/packages/sdk/encrypted-backup/package.json b/packages/sdk/encrypted-backup/package.json deleted file mode 100644 index ec467d043fe..00000000000 --- a/packages/sdk/encrypted-backup/package.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": "@celo/encrypted-backup", - "version": "4.1.1-dev", - "description": "Libraries for implemented password encrypted account backups", - "main": "./lib/index.js", - "types": "./lib/index.d.ts", - "author": "Celo", - "license": "Apache-2.0", - "homepage": "https://celo-sdk-docs.readthedocs.io/en/latest/encrypted-backup", - "repository": "https://github.com/celo-org/celo-monorepo/tree/master/packages/sdk/encrypted-backup", - "keywords": [ - "celo", - "blockchain", - "odis", - "backup", - "encrypted", - "encrypted-backup" - ], - "scripts": { - "build": "tsc -b .", - "clean": "tsc -b . --clean", - "docs": "typedoc", - "test": "jest --runInBand", - "lint": "tslint -c tslint.json --project .", - "prepublishOnly": "yarn build" - }, - "dependencies": { - "@celo/base": "4.1.1-dev", - "@celo/identity": "4.1.1-dev", - "@celo/phone-number-privacy-common": "^3.0.0-dev", - "@celo/poprf": "^0.1.9", - "@celo/utils": "4.1.1-dev", - "@types/debug": "^4.1.5", - "debug": "^4.1.1", - "fp-ts": "2.1.1", - "io-ts": "2.0.1" - }, - "devDependencies": { - "@celo/dev-utils": "0.0.1-dev", - "@celo/flake-tracker": "0.0.1-dev", - "fetch-mock": "9.10.4" - }, - "engines": { - "node": ">=8.13.0" - } -} \ No newline at end of file diff --git a/packages/sdk/encrypted-backup/src/backup.test.ts b/packages/sdk/encrypted-backup/src/backup.test.ts deleted file mode 100644 index 9e6cb75e794..00000000000 --- a/packages/sdk/encrypted-backup/src/backup.test.ts +++ /dev/null @@ -1,421 +0,0 @@ -import { - CircuitBreakerErrorTypes, - CircuitBreakerKeyStatus, -} from '@celo/identity/lib/odis/circuit-breaker' -import { MockCircuitBreaker } from '@celo/identity/lib/odis/circuit-breaker.mock' -import { defined, noBool } from '@celo/utils/lib/sign-typed-data-utils' -import debugFactory from 'debug' -import { Backup, createBackup, openBackup } from './backup' -import { ComputationalHardeningFunction, HardeningConfig } from './config' -import { BackupErrorTypes } from './errors' -import { MockOdis } from './odis.mock' -import { deserializeBackup, serializeBackup } from './schema' - -const debug = debugFactory('kit:encrypted-backup:backup:test') - -const TEST_HARDENING_CONFIG: HardeningConfig = { - odis: { - rateLimit: [{ delay: 0, resetTimer: noBool, batchSize: defined(3), repetitions: defined(1) }], - environment: MockOdis.environment, - }, - circuitBreaker: { - environment: MockCircuitBreaker.environment, - }, - computational: { - function: ComputationalHardeningFunction.SCRYPT, - cost: 1024, - }, -} - -let mockOdis: MockOdis | undefined -let mockCircuitBreaker: MockCircuitBreaker | undefined - -beforeEach(() => { - fetchMock.reset() - fetchMock.config.overwriteRoutes = true - - // Mock ODIS using the mock implementation defined above. - mockOdis = new MockOdis() - mockOdis.install(fetchMock) - - // Mock the circuit breaker service using the implementation from the identity library. - mockCircuitBreaker = new MockCircuitBreaker() - mockCircuitBreaker.install(fetchMock) -}) - -afterEach(() => { - fetchMock.reset() -}) - -describe('end-to-end', () => { - it('should be able to create, serialize, deserialize, and open a backup', async () => { - const testData = Buffer.from('backup test data', 'utf8') - const testPassword = Buffer.from('backup test password', 'utf8') - const testMetadata = { - name: 'test backup', - timestamp: Date.now(), - } - - const backup = await createBackup({ - data: testData, - userSecret: testPassword, - hardening: TEST_HARDENING_CONFIG, - metadata: testMetadata, - }) - debug('Created backup result', backup) - expect(backup.ok).toBe(true) - if (!backup.ok) { - return - } - expect(backup.result.metadata).toEqual(testMetadata) - - // Attempt to open the backup before passing it through the serialize function. - const opened = await openBackup({ backup: backup.result, userSecret: testPassword }) - debug('Open backup result', opened) - expect(opened.ok).toBe(true) - if (!opened.ok) { - return - } - expect(opened.result).toEqual(testData) - - // Serialize the backup. - const serialized = serializeBackup(backup.result) - debug('Serialized backup', serialized) - - // Deserialize the backup, check that it is correctly deserialized and can be opened. - const deserialized = deserializeBackup(serialized) - debug('Deserialize backup result', deserialized) - expect(deserialized.ok).toBe(true) - if (!deserialized.ok) { - return - } - expect(deserialized.result).toEqual(backup.result) - expect(deserialized.result.metadata).toEqual(testMetadata) - - // Open the backup and check that that the expect data is recovered. - const reopened = await openBackup({ backup: deserialized.result, userSecret: testPassword }) - debug('Reopen backup result', reopened) - expect(reopened.ok).toBe(true) - if (!reopened.ok) { - return - } - expect(reopened.result).toEqual(testData) - }) -}) - -describe('createBackup', () => { - const testData = Buffer.from('backup test data', 'utf8') - const testPassword = Buffer.from('backup test password', 'utf8') - - it('should return a fetch error when request to ODIS fails', async () => { - mockOdis!.installSignEndpoint(fetchMock, { throws: new Error('fetch failed') }) - const result = await createBackup({ - data: testData, - userSecret: testPassword, - hardening: TEST_HARDENING_CONFIG, - }) - expect(result.ok).toBe(false) - if (result.ok) { - return - } - expect(result.error.errorType).toEqual(BackupErrorTypes.FETCH_ERROR) - }) - - it('should return a service error when ODIS returns an error status', async () => { - mockOdis!.installSignEndpoint(fetchMock, { status: 501 }) - const result = await createBackup({ - data: testData, - userSecret: testPassword, - hardening: TEST_HARDENING_CONFIG, - }) - expect(result.ok).toBe(false) - if (result.ok) { - return - } - expect(result.error.errorType).toEqual(BackupErrorTypes.ODIS_SERVICE_ERROR) - }) - - it('should return a rate limit error when ODIS returns a rate limiting status', async () => { - mockOdis!.installSignEndpoint(fetchMock, { status: 429, headers: { 'Retry-After': '60' } }) - const result = await createBackup({ - data: testData, - userSecret: testPassword, - hardening: TEST_HARDENING_CONFIG, - }) - expect(result.ok).toBe(false) - if (result.ok) { - return - } - expect(result.error.errorType).toEqual(BackupErrorTypes.ODIS_RATE_LIMITING_ERROR) - }) - - it('should return a rate limit error when ODIS indicates no quota available', async () => { - mockOdis!.installQuotaEndpoint(fetchMock, { - status: 200, - body: { - success: true, - version: 'mock', - status: { - timer: Date.now() / 1000 + 3600, - counter: 0, - disabled: false, - now: Date.now() / 1000, - }, - }, - }) - const result = await createBackup({ - data: testData, - userSecret: testPassword, - hardening: TEST_HARDENING_CONFIG, - }) - expect(result.ok).toBe(false) - if (result.ok) { - return - } - expect(result.error.errorType).toEqual(BackupErrorTypes.ODIS_RATE_LIMITING_ERROR) - }) - - it('should not rely on ODIS when not included in the configuration', async () => { - mockOdis!.installSignEndpoint(fetchMock, { status: 501 }) - const result = await createBackup({ - data: testData, - userSecret: testPassword, - hardening: { ...TEST_HARDENING_CONFIG, odis: undefined }, - }) - expect(result.ok).toBe(true) - }) - - it('should return a fetch error when the circuit breaker status check fails', async () => { - mockCircuitBreaker!.installStatusEndpoint(fetchMock, { throws: new Error('fetch failed') }) - const result = await createBackup({ - data: testData, - userSecret: testPassword, - hardening: TEST_HARDENING_CONFIG, - }) - expect(result.ok).toBe(false) - if (result.ok) { - return - } - expect(result.error.errorType).toEqual(CircuitBreakerErrorTypes.FETCH_ERROR) - }) - - it('should return a service error when the circuit breaker service returns 501', async () => { - mockCircuitBreaker!.installStatusEndpoint(fetchMock, { status: 501 }) - const result = await createBackup({ - data: testData, - userSecret: testPassword, - hardening: TEST_HARDENING_CONFIG, - }) - expect(result.ok).toBe(false) - if (result.ok) { - return - } - expect(result.error.errorType).toEqual(CircuitBreakerErrorTypes.SERVICE_ERROR) - }) - - it('should return an unavailable error when the circuit breaker key is destroyed', async () => { - mockCircuitBreaker!.keyStatus = CircuitBreakerKeyStatus.DESTROYED - const result = await createBackup({ - data: testData, - userSecret: testPassword, - hardening: TEST_HARDENING_CONFIG, - }) - expect(result.ok).toBe(false) - if (result.ok) { - return - } - expect(result.error.errorType).toEqual(CircuitBreakerErrorTypes.UNAVAILABLE_ERROR) - }) - - it('should not rely on the circuit breaker when not included in the configuration', async () => { - mockCircuitBreaker!.installStatusEndpoint(fetchMock, { status: 501 }) - const result = await createBackup({ - data: testData, - userSecret: testPassword, - hardening: { ...TEST_HARDENING_CONFIG, circuitBreaker: undefined }, - }) - expect(result.ok).toBe(true) - }) -}) - -describe('openBackup', () => { - const testPassword = Buffer.from('backup test password', 'utf8') - const testData = Buffer.from('backup test data', 'utf8') - let testBackup: Backup | undefined - - beforeEach(async () => { - // Create a backup to use for tests of opening below - const testBackupResult = await createBackup({ - data: testData, - userSecret: testPassword, - hardening: TEST_HARDENING_CONFIG, - }) - if (!testBackupResult.ok) { - throw new Error(`failed to create backup for test setup: ${testBackupResult.error}`) - } - testBackup = testBackupResult.result - }) - - it('should result in a decryption error if the encrypted data is modified', async () => { - // Flip a bit in the encrypted data. - // tslint:disable-next-line:no-bitwise - testBackup!.encryptedData[0] ^= 0x01 - const result = await openBackup({ - backup: testBackup!, - userSecret: testPassword, - }) - expect(result.ok).toBe(false) - if (result.ok) { - return - } - expect(result.error.errorType).toEqual(BackupErrorTypes.DECRYPTION_ERROR) - }) - - it('should result in a decryption error if odis domain is modified', async () => { - testBackup!.odisDomain!.salt = defined('some salt') - const result = await openBackup({ - backup: testBackup!, - userSecret: testPassword, - }) - expect(result.ok).toBe(false) - if (result.ok) { - return - } - expect(result.error.errorType).toEqual(BackupErrorTypes.DECRYPTION_ERROR) - }) - - it('should result in a decryption error if the circuit breaker response changes', async () => { - mockCircuitBreaker!.installUnwrapKeyEndpoint(fetchMock, { - status: 200, - body: { plaintext: Buffer.from('bad fuse key').toString('base64') }, - }) - const result = await openBackup({ - backup: testBackup!, - userSecret: testPassword, - }) - expect(result.ok).toBe(false) - if (result.ok) { - return - } - expect(result.error.errorType).toEqual(BackupErrorTypes.DECRYPTION_ERROR) - }) - - it('should result in a decryption error if the computational hardening is altered', async () => { - testBackup!.computationalHardening = { - function: ComputationalHardeningFunction.SCRYPT, - cost: 16, - } - const result = await openBackup({ - backup: testBackup!, - userSecret: testPassword, - }) - expect(result.ok).toBe(false) - if (result.ok) { - return - } - expect(result.error.errorType).toEqual(BackupErrorTypes.DECRYPTION_ERROR) - }) - - it('should return a fetch error when request to ODIS fails', async () => { - mockOdis!.installSignEndpoint(fetchMock, { throws: new Error('fetch failed') }) - const result = await openBackup({ - backup: testBackup!, - userSecret: testPassword, - }) - expect(result.ok).toBe(false) - if (result.ok) { - return - } - expect(result.error.errorType).toEqual(BackupErrorTypes.FETCH_ERROR) - }) - - it('should return a service error when ODIS returns an error status', async () => { - mockOdis!.installSignEndpoint(fetchMock, { status: 501 }) - const result = await openBackup({ - backup: testBackup!, - userSecret: testPassword, - }) - expect(result.ok).toBe(false) - if (result.ok) { - return - } - expect(result.error.errorType).toEqual(BackupErrorTypes.ODIS_SERVICE_ERROR) - }) - - it('should return a rate limit error when ODIS returns a rate limiting status', async () => { - mockOdis!.installSignEndpoint(fetchMock, { status: 429, headers: { 'Retry-After': '60' } }) - const result = await openBackup({ - backup: testBackup!, - userSecret: testPassword, - }) - expect(result.ok).toBe(false) - if (result.ok) { - return - } - expect(result.error.errorType).toEqual(BackupErrorTypes.ODIS_RATE_LIMITING_ERROR) - }) - - it('should return a rate limit error when ODIS indicates no quota available', async () => { - mockOdis!.installQuotaEndpoint(fetchMock, { - status: 200, - body: { - success: true, - version: 'mock', - status: { - timer: Date.now() / 1000 + 3600, - counter: 0, - disabled: false, - now: Date.now() / 1000, - }, - }, - }) - const result = await openBackup({ - backup: testBackup!, - userSecret: testPassword, - }) - expect(result.ok).toBe(false) - if (result.ok) { - return - } - expect(result.error.errorType).toEqual(BackupErrorTypes.ODIS_RATE_LIMITING_ERROR) - }) - - it('should return a fetch error when circuit breaker unwrap key fails', async () => { - mockCircuitBreaker!.installUnwrapKeyEndpoint(fetchMock, { throws: new Error('fetch failed') }) - const result = await openBackup({ - backup: testBackup!, - userSecret: testPassword, - }) - expect(result.ok).toBe(false) - if (result.ok) { - return - } - expect(result.error.errorType).toEqual(CircuitBreakerErrorTypes.FETCH_ERROR) - }) - - it('should return a service error when the circuit breaker service returns 501', async () => { - mockCircuitBreaker!.installUnwrapKeyEndpoint(fetchMock, { status: 501 }) - const result = await openBackup({ - backup: testBackup!, - userSecret: testPassword, - }) - expect(result.ok).toBe(false) - if (result.ok) { - return - } - expect(result.error.errorType).toEqual(CircuitBreakerErrorTypes.SERVICE_ERROR) - }) - - it('should return an unavailable error when the circuit breaker key is destroyed', async () => { - mockCircuitBreaker!.keyStatus = CircuitBreakerKeyStatus.DESTROYED - const result = await openBackup({ - backup: testBackup!, - userSecret: testPassword, - }) - expect(result.ok).toBe(false) - if (result.ok) { - return - } - expect(result.error.errorType).toEqual(CircuitBreakerErrorTypes.UNAVAILABLE_ERROR) - }) -}) diff --git a/packages/sdk/encrypted-backup/src/backup.ts b/packages/sdk/encrypted-backup/src/backup.ts deleted file mode 100644 index 7ee6f445e6e..00000000000 --- a/packages/sdk/encrypted-backup/src/backup.ts +++ /dev/null @@ -1,507 +0,0 @@ -import { eqAddress } from '@celo/base/lib/address' -import { Err, Ok, Result } from '@celo/base/lib/result' -import { - CircuitBreakerClient, - CircuitBreakerKeyStatus, - CircuitBreakerServiceContext, - CircuitBreakerUnavailableError, -} from '@celo/identity/lib/odis/circuit-breaker' -import { ServiceContext as OdisServiceContext } from '@celo/identity/lib/odis/query' -import { SequentialDelayDomain } from '@celo/phone-number-privacy-common/lib/domains' -import * as crypto from 'crypto' -import debugFactory from 'debug' -import { - ComputationalHardeningConfig, - EnvironmentIdentifier, - HardeningConfig, - PASSWORD_HARDENING_ALFAJORES_CONFIG, - PASSWORD_HARDENING_MAINNET_CONFIG, - PIN_HARDENING_ALFAJORES_CONFIG, - PIN_HARDENING_MAINNET_CONFIG, -} from './config' -import { BackupError, InvalidBackupError, UsageError } from './errors' -import { buildOdisDomain, odisHardenKey, odisQueryAuthorizer } from './odis' -import { computationalHardenKey, decrypt, deriveKey, encrypt, KDFInfo } from './utils' - -const debug = debugFactory('kit:encrypted-backup:backup') - -/** - * Backup structure encoding the information needed to implement the encrypted backup protocol. - * - * @remarks The structure below and its related functions implement the encrypted backup protocol - * designed for wallet account backups. More information about the protocol can be found in the - * official {@link https://docs.celo.org/celo-codebase/protocol/identity/encrypted-cloud-backup | - * Celo documentation} - */ -export interface Backup { - /** - * AES-128-GCM encryption of the user's secret backup data. - * - * @remarks The backup key is derived from the user's password or PIN hardened with input from the - * ODIS rate-limited hashing service and optionally a circuit breaker service. - */ - encryptedData: Buffer - - /** - * A randomly chosen 256-bit value. Ensures uniqueness of the password derived encryption key. - * - * @remarks The nonce value is appended to the password for local key derivation. It is also used - * to derive an authentication key to include in the ODIS Domain for domain separation and to - * ensure quota cannot be consumed by parties without access to the backup. - */ - nonce: Buffer - - /** - * ODIS Domain instance to be included in the query to ODIS for password hardening, - * - * @remarks Currently only SequentialDelayDomain is supported. Other ODIS domains intended for key - * hardening may be supported in the future. - */ - odisDomain?: SequentialDelayDomain - - /** - * RSA-OAEP-256 encryption of a randomly chosen 128-bit value, the fuse key. - * - * @remarks The fuse key, if provided, is combined with the password in local key derivation. - * Encryption is under the public key of the circuit breaker service. In order to get the fuseKey - * the client will send this ciphertext to the circuit breaker service for decryption. - */ - encryptedFuseKey?: Buffer - - /** - * Options for local computational hardening of the encryption key through PBKDF or scrypt. - * - * @remarks Adding computational hardening provides a measure of security from password guessing - * when the password has a moderate amount of entropy (e.g. a password generated under good - * guidelines). If the user secret has very low entropy, such as with a 6-digit PIN, - * computational hardening does not add significant security. - */ - computationalHardening?: ComputationalHardeningConfig - - /** Version number for the backup feature. Used to facilitate backwards compatibility. */ - version: string - - /** - * Data provided by the backup creator to identify the backup and its context - * - * @remarks Metadata is provided by, and only meaningful to, the SDK user. The intention is for - * this metadata to be used for identifying the backup and providing any context needed in the - * application - * - * @example - * ```typescript - * { - * // Address of the primary account stored a backup of an account key. Used to display the - * // balance and latest transaction information for a given backup. - * accountAddress: string - * // Unix timestamp used to indicate when the backup was created. - * timestamp: number - * } - * ``` - */ - metadata?: { [key: string]: unknown } - - /** Information including the URL and public keys of the ODIS and circuit breaker services. */ - environment?: { - odis?: OdisServiceContext - circuitBreaker?: CircuitBreakerServiceContext - } -} - -export interface CreatePinEncryptedBackupArgs { - data: Buffer - pin: string - environment?: EnvironmentIdentifier - metadata?: { [key: string]: unknown } -} - -/** - * Create a data backup, encrypting it with a hardened key derived from the given PIN. - * - * @remarks Using a 4 or 6 digit PIN for encryption requires an extremely restrictive rate limit for - * attempts to guess the PIN. This is enforced by ODIS through the SequentialDelayDomain with - * settings to allow the user (or an attacker) only a fixed number of attempts to guess their PIN. - * - * Because PINs have very little entropy, the total number of guesses is very restricted. - * * On the first day, the client has 10 attempts. 5 within 10s. 5 more over roughly 45 minutes. - * * On the second day, the client has 5 attempts over roughly 2 minutes. - * * On the third day, the client has 3 attempts over roughly 40 seconds. - * * On the fourth day, the client has 2 attempts over roughly 10 seconds. - * * Overall, the client has 25 attempts over 4 days. All further attempts will be denied. - * - * It is strongly recommended that the calling application implement a PIN blocklist to prevent the - * user from selecting a number of the most common PIN codes (e.g. blocking the top 25k PINs by - * frequency of appearance in the HIBP Passwords dataset). An example implementation can be seen in - * the Valora wallet. {@link - * https://github.com/valora-inc/wallet/blob/3940661c40d08e4c5db952bd0abeaabb0030fc7a/packages/mobile/src/pincode/authentication.ts#L56-L108 - * | PIN blocklist implementation} - * - * In order to handle the event of an ODIS service compromise, this configuration additionally - * includes a circuit breaker service run by Valora. In the event of an ODIS compromise, the Valora - * team will take their service offline, preventing backups using the circuit breaker from being - * opened. This ensures that an attacker who has compromised ODIS cannot leverage their attack to - * forcibly open backups created with this function. - * - * @param data The secret data (e.g. BIP-39 mnemonic phrase) to be included in the encrypted backup. - * @param pin PIN to use in deriving the encryption key. - * @param hardening Configuration for how the password should be hardened in deriving the key. - * @param metadata Arbitrary key-value data to include in the backup to identify it. - */ -export async function createPinEncryptedBackup({ - data, - pin, - environment, - metadata, -}: CreatePinEncryptedBackupArgs): Promise> { - // Select the hardening configuration based on the environment selector. - let hardening: HardeningConfig | undefined - if (environment === EnvironmentIdentifier.ALFAJORES) { - hardening = PIN_HARDENING_ALFAJORES_CONFIG - } else if (environment === EnvironmentIdentifier.MAINNET || environment === undefined) { - hardening = PIN_HARDENING_MAINNET_CONFIG - } - if (hardening === undefined) { - throw new Error('Implementation error: unhandled environment identifier') - } - - return createBackup({ data, userSecret: pin, hardening, metadata }) -} - -export interface CreatePasswordEncryptedBackupArgs { - data: Buffer - password: string - environment?: EnvironmentIdentifier - metadata?: { [key: string]: unknown } -} - -/** - * Create a data backup, encrypting it with a hardened key derived from the given password. - * - * @remarks Because passwords have moderate entropy, the total number of guesses is restricted. - * * The user initially gets 5 attempts without delay. - * * Then the user gets two attempts every 5 seconds for up to 20 attempts. - * * Then the user gets two attempts every 30 seconds for up to 20 attempts. - * * Then the user gets two attempts every 5 minutes for up to 20 attempts. - * * Then the user gets two attempts every hour for up to 20 attempts. - * * Then the user gets two attempts every day for up to 20 attempts. - * - * Following guidelines in NIST-800-63-3 it is strongly recommended that the caller apply a password - * blocklist to the users choice of password. - * - * In order to handle the event of an ODIS service compromise, this configuration additionally - * hardens the password input with a computational hardening function. In particular, scrypt is used - * with IETF recommended parameters {@link - * https://tools.ietf.org/id/draft-whited-kitten-password-storage-00.html#name-scrypt | IETF - * recommended scrypt parameters } - * - * @param data The secret data (e.g. BIP-39 mnemonic phrase) to be included in the encrypted backup. - * @param password Password to use in deriving the encryption key. - * @param hardening Configuration for how the password should be hardened in deriving the key. - * @param metadata Arbitrary key-value data to include in the backup to identify it. - */ -export async function createPasswordEncryptedBackup({ - data, - password, - environment, - metadata, -}: CreatePasswordEncryptedBackupArgs): Promise> { - // Select the hardening configuration based on the environment selector. - let hardening: HardeningConfig | undefined - if (environment === EnvironmentIdentifier.ALFAJORES) { - hardening = PASSWORD_HARDENING_ALFAJORES_CONFIG - } else if (environment === EnvironmentIdentifier.MAINNET || environment === undefined) { - hardening = PASSWORD_HARDENING_MAINNET_CONFIG - } - if (hardening === undefined) { - throw new Error('Implementation error: unhandled environment identifier') - } - - return createBackup({ data, userSecret: password, hardening, metadata }) -} - -export interface CreateBackupArgs { - data: Buffer - userSecret: Buffer | string - hardening: HardeningConfig - metadata?: { [key: string]: unknown } -} - -/** - * Create a data backup, encrypting it with a hardened key derived from the given password or PIN. - * - * @param data The secret data (e.g. BIP-39 mnemonic phrase) to be included in the encrypted backup. - * @param userSecret Password, PIN, or other user secret to use in deriving the encryption key. - * If a string is provided, it will be UTF-8 encoded into a Buffer before use. - * @param hardening Configuration for how the password should be hardened in deriving the key. - * @param metadata Arbitrary key-value data to include in the backup to identify it. - * - * @privateRemarks Most of this functions code is devoted to key generation starting with the input - * password or PIN and ending up with a hardened encryption key. It is important that the order and - * inputs to each step in the derivation be well considered and implemented correctly. One important - * requirement is that no output included in the backup acts as a "commitment" to the password or PIN - * value, except the final ciphertext. An example of an issue with this would be if a hash of the - * password and nonce were included in the backup. If a commitment to the password or PIN is - * included, an attacker can locally brute force that commitment to recover the password, then use - * that knowledge to complete the derivation. - */ -export async function createBackup({ - data, - userSecret, - hardening, - metadata, -}: CreateBackupArgs): Promise> { - // Password and backup data are not included in any logging as they are likely sensitive. - debug('creating a backup with the following information', hardening, metadata) - - // Safety measure to prevent users from accidentally using this API without any hardening. - if (hardening.odis === undefined && hardening.computational === undefined) { - return Err( - new UsageError(new Error('createBackup cannot be used with a empty hardening config')) - ) - } - - // Generate a 32-byte random nonce for the backup. Use the first half to salt the user secret - // input and the second half to derive an authentication key for making queries to ODIS. - const nonce = crypto.randomBytes(32) - const passwordSalt = nonce.slice(0, 16) - const odisAuthKeySeed = nonce.slice(16, 32) - const userSecretBuffer = - typeof userSecret === 'string' ? Buffer.from(userSecret, 'utf8') : userSecret - const initialKey = deriveKey(KDFInfo.PASSWORD, [userSecretBuffer, passwordSalt]) - - // Generate a fuse key and mix it into the entropy of the key - let encryptedFuseKey: Buffer | undefined - let updatedKey: Buffer - if (hardening.circuitBreaker !== undefined) { - debug('generating a fuse key to enabled use of the circuit breaker service') - const circuitBreakerClient = new CircuitBreakerClient(hardening.circuitBreaker.environment) - - // Check that the circuit breaker is online. Although we do not need to interact with the - // service to create the backup, we should ensure its keys are not disabled or destroyed, - // otherwise we may not be able to open the backup that we create. - // Note that this status check is not strictly necessary and can be removed to all users to - // proceed when the circuit breaker is temporarily unavailable at the risk of not being able to - // open their backup later. - const serviceStatus = await circuitBreakerClient.status() - if (!serviceStatus.ok) { - return Err(serviceStatus.error) - } - if (serviceStatus.result !== CircuitBreakerKeyStatus.ENABLED) { - return Err(new CircuitBreakerUnavailableError(serviceStatus.result)) - } - debug('confirmed that the circuit breaker is online') - - // Generate a fuse key and encrypt it against the circuit breaker public key. - debug('generating and wrapping the fuse key') - const fuseKey = crypto.randomBytes(16) - const wrap = circuitBreakerClient.wrapKey(fuseKey) - if (!wrap.ok) { - return Err(wrap.error) - } - encryptedFuseKey = wrap.result - - // Mix the fuse key into the ongoing key hardening. Note that mixing in the circuit breaker key - // occurs before the request to ODIS. This means an attacker would need to acquire the fuse key - // _before_ they can make any attempts to guess the user's secret. - debug('mixing the fuse key into the keying material') - updatedKey = deriveKey(KDFInfo.FUSE_KEY, [initialKey, fuseKey]) - } else { - debug('not using the circuit breaker service') - updatedKey = initialKey - } - - // Harden the key with the output of a rate limited ODIS POPRF function. - let domain: SequentialDelayDomain | undefined - let odisHardenedKey: Buffer | undefined - if (hardening.odis !== undefined) { - debug('hardening the user key with output from ODIS') - // Derive the query authorizer wallet and address from the nonce, then build the ODIS domain. - // This domain acts as a binding rate limit configuration for ODIS, enforcing that the client must - // know the backup nonce, and can only make the given number of queries. - const authorizer = odisQueryAuthorizer(odisAuthKeySeed) - domain = buildOdisDomain(hardening.odis, authorizer.address) - - debug('sending request to ODIS to harden the backup encryption key') - const odisHardening = await odisHardenKey( - updatedKey, - domain, - hardening.odis.environment, - authorizer.wallet - ) - if (!odisHardening.ok) { - return Err(odisHardening.error) - } - odisHardenedKey = odisHardening.result - } else { - debug('not using ODIS for key hardening') - } - - let computationalHardenedKey: Buffer | undefined - if (hardening.computational !== undefined) { - debug('hardening user key with computational function', hardening.computational) - const computationalHardening = await computationalHardenKey(updatedKey, hardening.computational) - if (!computationalHardening.ok) { - return Err(computationalHardening.error) - } - computationalHardenedKey = computationalHardening.result - } else { - debug('not using computational key hardening') - } - - debug('finalizing encryption key') - const finalKey = deriveKey(KDFInfo.FINALIZE, [ - updatedKey, - odisHardenedKey ?? Buffer.alloc(0), - computationalHardenedKey ?? Buffer.alloc(0), - ]) - - debug('encrypting backup data with final encryption key') - const encryption = encrypt(finalKey, data) - if (!encryption.ok) { - return Err(encryption.error) - } - - // Encrypted and wrap the data in a Backup structure. - debug('created encrypted backup') - return Ok({ - encryptedData: encryption.result, - nonce, - odisDomain: domain, - encryptedFuseKey, - computationalHardening: hardening.computational, - // TODO(victor): Bump this to 1.0 when the final crypto is added. - version: '0.0.1', - metadata, - environment: { - odis: hardening.odis?.environment, - circuitBreaker: hardening.circuitBreaker?.environment, - }, - }) -} - -export interface OpenBackupArgs { - backup: Backup - userSecret: Buffer | string -} - -/** - * Open an encrypted backup file, using the provided password or PIN to derive the decryption key. - * - * @param backup Backup structure including the ciphertext and key derivation information. - * @param userSecret Password, PIN, or other user secret to use in deriving the encryption key. - * If a string is provided, it will be UTF-8 encoded into a Buffer before use. - */ -export async function openBackup({ - backup, - userSecret, -}: OpenBackupArgs): Promise> { - debug('opening an encrypted backup') - - // Split the nonce into the two halves for password salting and auth key generation respectively. - const passwordSalt = backup.nonce.slice(0, 16) - const odisAuthKeySeed = backup.nonce.slice(16, 32) - const userSecretBuffer = - typeof userSecret === 'string' ? Buffer.from(userSecret, 'utf8') : userSecret - const initialKey = deriveKey(KDFInfo.PASSWORD, [userSecretBuffer, passwordSalt]) - - // If a circuit breaker is in use, request a decryption of the fuse key and mix it in. - let updatedKey: Buffer - if (backup.encryptedFuseKey !== undefined) { - if (backup.environment?.circuitBreaker === undefined) { - return Err( - new InvalidBackupError( - new Error('encrypted fuse key is provided but no circuit breaker environment is provided') - ) - ) - } - const circuitBreakerClient = new CircuitBreakerClient(backup.environment.circuitBreaker) - - debug( - 'requesting the circuit breaker service unwrap the encrypted circuit breaker key', - backup.environment.circuitBreaker - ) - const unwrap = await circuitBreakerClient.unwrapKey(backup.encryptedFuseKey) - if (!unwrap.ok) { - return Err(unwrap.error) - } - - // Mix the fuse key into the ongoing key hardening. Note that mixing in the circuit breaker key - // occurs before the request to ODIS. This means an attacker would need to aquire the fuse key - // _before_ they can make any attempts to guess the user's secret. - updatedKey = deriveKey(KDFInfo.FUSE_KEY, [initialKey, unwrap.result]) - } else { - debug('backup did not specify an encrypted fuse key') - updatedKey = initialKey - } - - // If ODIS is in use, harden the key with the output of a rate limited ODIS POPRF function. - let odisHardenedKey: Buffer | undefined - if (backup.odisDomain !== undefined) { - const domain = backup.odisDomain - - // Derive the query authorizer wallet and address from the nonce. - // If the ODIS domain is authenticated, the authorizer address should match the domain. - const authorizer = odisQueryAuthorizer(odisAuthKeySeed) - if (domain.address.defined && !eqAddress(authorizer.address, domain.address.value)) { - return Err( - new InvalidBackupError( - new Error( - 'domain query authorizer address is provided but is not derived from the backup nonce' - ) - ) - ) - } - if (backup.environment?.odis === undefined) { - return Err( - new InvalidBackupError( - new Error('ODIS domain is provided by no ODIS environment information') - ) - ) - } - - debug('requesting a key hardening response from ODIS') - const odisHardening = await odisHardenKey( - updatedKey, - domain, - backup.environment.odis, - authorizer.wallet - ) - if (!odisHardening.ok) { - return Err(odisHardening.error) - } - odisHardenedKey = odisHardening.result - } else { - debug('not using ODIS for key hardening') - } - - let computationalHardenedKey: Buffer | undefined - if (backup.computationalHardening !== undefined) { - debug('hardening user key with computational function', backup.computationalHardening) - const computationalHardening = await computationalHardenKey( - updatedKey, - backup.computationalHardening - ) - if (!computationalHardening.ok) { - return Err(computationalHardening.error) - } - computationalHardenedKey = computationalHardening.result - } else { - debug('not using computational key hardening') - } - - debug('finalizing decryption key') - const finalKey = deriveKey(KDFInfo.FINALIZE, [ - updatedKey, - odisHardenedKey ?? Buffer.alloc(0), - computationalHardenedKey ?? Buffer.alloc(0), - ]) - - debug('decrypting backup with finalized decryption key') - const decryption = decrypt(finalKey, backup.encryptedData) - if (!decryption.ok) { - return Err(decryption.error) - } - - debug('decrypted backup') - return Ok(decryption.result) -} diff --git a/packages/sdk/encrypted-backup/src/config.ts b/packages/sdk/encrypted-backup/src/config.ts deleted file mode 100644 index edc19449433..00000000000 --- a/packages/sdk/encrypted-backup/src/config.ts +++ /dev/null @@ -1,380 +0,0 @@ -import { - CircuitBreakerServiceContext, - VALORA_ALFAJORES_CIRCUIT_BREAKER_ENVIRONMENT, - VALORA_MAINNET_CIRCUIT_BREAKER_ENVIRONMENT, -} from '@celo/identity/lib/odis/circuit-breaker' -import { - ODIS_ALFAJORES_CONTEXT_DOMAINS, - ODIS_MAINNET_CONTEXT_DOMAINS, - ServiceContext as OdisServiceContext, -} from '@celo/identity/lib/odis/query' -import { SequentialDelayStage } from '@celo/phone-number-privacy-common' -import { defined, noNumber } from '@celo/utils/lib/sign-typed-data-utils' -import { ScryptOptions } from './utils' - -export interface HardeningConfig { - /** - * If provided, a computational hardening function (e.g. scrypt or PBKDF2) will be applied to - * locally harden the backup encryption key. - * - * @remarks Recommended for password-encrypted backups, especially if a circuit breaker is not in - * use, as this provides some degree of protection in the event of an ODIS compromise. When - * generating backups on low-power devices (e.g. budget smart phones) and encrypting with - * low-entropy secrets (e.g. 6-digit PINs) local hardening cannot offer significant protection. - */ - computational?: ComputationalHardeningConfig - - /** If provided, ODIS will be used with the given configuration to harden the backup key */ - odis?: OdisHardeningConfig - - /** - * If provided, a circuit breaker will be used with the given configuration to protect the backup key - */ - circuitBreaker?: CircuitBreakerConfig -} - -/** Configuration for usage of ODIS to harden the encryption keys */ -export interface OdisHardeningConfig { - /** - * Rate limiting information used to construct the ODIS domain which will be used to harden the - * encryption key through ODIS' domain password hardening service. - * - * @remarks Currently supports the SequentialDelayDomain. In the future, as additional domains are - * standardized for key hardening, they may be added here to allow a wider range of configuration. - */ - rateLimit: SequentialDelayStage[] - - /** Environment information including the URL and public key of the ODIS service */ - environment: OdisServiceContext -} - -/** Configuration for usage of a circuit breaker to protect the encryption keys */ -export interface CircuitBreakerConfig { - /** Environment information including the URL and public key of the circuit breaker service */ - environment: CircuitBreakerServiceContext -} - -export enum ComputationalHardeningFunction { - PBKDF = 'pbkdf2_sha256', - SCRYPT = 'scrypt', -} - -export interface PbkdfConfig { - function: ComputationalHardeningFunction.PBKDF - iterations: number -} - -export interface ScryptConfig extends ScryptOptions { - function: ComputationalHardeningFunction.SCRYPT -} - -export type ComputationalHardeningConfig = PbkdfConfig | ScryptConfig - -/** - * ODIS SequentialDelayDomain rate limit configured to be appropriate for hardening a 6-digit PIN. - * - * @remarks Because PINs have very little entropy, the total number of guesses is very restricted. - * * On the first day, the client has 10 attempts. 5 within 10s. 5 more over roughly 45 minutes. - * * On the second day, the client has 5 attempts over roughly 2 minutes. - * * On the third day, the client has 3 attempts over roughly 40 seconds. - * * On the fourth day, the client has 2 attempts over roughly 10 seconds. - * * Overall, the client has 20 attempts over 4 days. All further attempts will be denied. - */ -const PIN_HARDENING_RATE_LIMIT: SequentialDelayStage[] = [ - // First stage is setup, as the user will need to make a single query to create their backup. - { - delay: 0, - resetTimer: defined(true), - batchSize: defined(1), - repetitions: noNumber, - }, - // On the first day, the client has 10 attempts. 5 within 10s. 5 more over roughly 45 minutes. - { - delay: 0, - resetTimer: defined(true), - batchSize: defined(3), - repetitions: noNumber, - }, - { - delay: 10, - resetTimer: defined(true), - batchSize: defined(2), - repetitions: noNumber, - }, - { - delay: 30, - resetTimer: defined(false), - batchSize: defined(1), - repetitions: noNumber, - }, - { - delay: 60, - resetTimer: defined(false), - batchSize: defined(1), - repetitions: noNumber, - }, - { - delay: 300, - resetTimer: defined(false), - batchSize: defined(1), - repetitions: noNumber, - }, - { - delay: 900, - resetTimer: defined(false), - batchSize: defined(1), - repetitions: noNumber, - }, - { - delay: 1800, - resetTimer: defined(true), - batchSize: defined(1), - repetitions: noNumber, - }, - // On the second day, the client has 5 attempts over roughly 2 minutes. - { - delay: 86400, - resetTimer: defined(true), - batchSize: defined(2), - repetitions: noNumber, - }, - { - delay: 10, - resetTimer: defined(false), - batchSize: defined(1), - repetitions: noNumber, - }, - { - delay: 30, - resetTimer: defined(false), - batchSize: defined(1), - repetitions: noNumber, - }, - { - delay: 60, - resetTimer: defined(true), - batchSize: defined(1), - repetitions: noNumber, - }, - // On the third day, the client has 3 attempts over roughly 40 seconds. - { - delay: 86400, - resetTimer: defined(true), - batchSize: defined(1), - repetitions: noNumber, - }, - { - delay: 10, - resetTimer: defined(false), - batchSize: defined(1), - repetitions: noNumber, - }, - { - delay: 30, - resetTimer: defined(true), - batchSize: defined(1), - repetitions: noNumber, - }, - // On the fourth day, the client has 2 attempts over roughly 10 seconds. - { - delay: 86400, - resetTimer: defined(true), - batchSize: defined(1), - repetitions: noNumber, - }, - { - delay: 10, - resetTimer: defined(false), - batchSize: defined(1), - repetitions: noNumber, - }, -] - -/** - * ODIS SequentialDelayDomain rate limit configured to be appropriate for hardening a password. - * - * @remarks Because passwords have moderate entropy, the total number of guesses is restricted. - * * The user initially gets 5 attempts without delay. - * * Then the user gets two attempts every 5 seconds for up to 20 attempts. - * * Then the user gets two attempts every 30 seconds for up to 20 attempts. - * * Then the user gets two attempts every 5 minutes for up to 20 attempts. - * * Then the user gets two attempts every hour for up to 20 attempts. - * * Then the user gets two attempts every day for up to 20 attempts. - */ -const PASSWORD_HARDENING_RATE_LIMIT: SequentialDelayStage[] = [ - // First stage is setup, as the user will need to make a single query to create their backup. - { - delay: 0, - resetTimer: defined(true), - batchSize: defined(1), - repetitions: noNumber, - }, - // After the first 5 attempts, the user has 100 attempts with the delays increasing every 20. - { - delay: 0, - resetTimer: defined(true), - batchSize: defined(5), - repetitions: noNumber, - }, - { - delay: 5, - resetTimer: defined(true), - batchSize: defined(2), - repetitions: defined(10), - }, - { - delay: 30, - resetTimer: defined(true), - batchSize: defined(2), - repetitions: defined(10), - }, - { - delay: 300, - resetTimer: defined(true), - batchSize: defined(2), - repetitions: defined(10), - }, - { - delay: 3600, - resetTimer: defined(true), - batchSize: defined(2), - repetitions: defined(10), - }, - { - delay: 86400, - resetTimer: defined(true), - batchSize: defined(2), - repetitions: defined(10), - }, -] - -/** - * ODIS SequentialDelayDomain rate limit configured for e2e testing where no rate limit should be applied. - * - * @remarks This should only be used testing purposes - */ -const E2E_TESTING_RATE_LIMIT: SequentialDelayStage[] = [ - { - delay: 0, - resetTimer: defined(true), - batchSize: defined(1000000000), - repetitions: defined(1000000000), - }, -] - -/** - * ODIS SequentialDelayDomain rate limit configured for e2e testing where the user should have no quota. - * - * @remarks This should only be used testing purposes - */ -const NO_QUOTA_RATE_LIMIT: SequentialDelayStage[] = [ - { - delay: 0, - resetTimer: defined(true), - batchSize: defined(0), - repetitions: defined(0), - }, -] - -export enum EnvironmentIdentifier { - MAINNET = 'MAINNET', - ALFAJORES = 'ALFAJORES', -} - -export const PIN_HARDENING_MAINNET_CONFIG: HardeningConfig = { - odis: { - rateLimit: PIN_HARDENING_RATE_LIMIT, - environment: ODIS_MAINNET_CONTEXT_DOMAINS, - }, - circuitBreaker: { - environment: VALORA_MAINNET_CIRCUIT_BREAKER_ENVIRONMENT, - }, -} - -export const PIN_HARDENING_ALFAJORES_CONFIG: HardeningConfig = { - odis: { - rateLimit: PIN_HARDENING_RATE_LIMIT, - environment: ODIS_ALFAJORES_CONTEXT_DOMAINS, - }, - circuitBreaker: { - environment: VALORA_ALFAJORES_CIRCUIT_BREAKER_ENVIRONMENT, - }, -} - -export const PASSWORD_HARDENING_MAINNET_CONFIG: HardeningConfig = { - odis: { - rateLimit: PASSWORD_HARDENING_RATE_LIMIT, - environment: ODIS_MAINNET_CONTEXT_DOMAINS, - }, - computational: { - function: ComputationalHardeningFunction.SCRYPT, - cost: 32768, - blockSize: 8, - parallelization: 1, - }, -} - -export const E2E_TESTING_MAINNET_CONFIG: HardeningConfig = { - odis: { - rateLimit: E2E_TESTING_RATE_LIMIT, - environment: ODIS_MAINNET_CONTEXT_DOMAINS, - }, - computational: { - function: ComputationalHardeningFunction.SCRYPT, - cost: 32768, - blockSize: 8, - parallelization: 1, - }, -} - -export const NO_QUOTA_MAINNET_CONFIG: HardeningConfig = { - odis: { - rateLimit: NO_QUOTA_RATE_LIMIT, - environment: ODIS_MAINNET_CONTEXT_DOMAINS, - }, - computational: { - function: ComputationalHardeningFunction.SCRYPT, - cost: 32768, - blockSize: 8, - parallelization: 1, - }, -} - -export const PASSWORD_HARDENING_ALFAJORES_CONFIG: HardeningConfig = { - odis: { - rateLimit: PASSWORD_HARDENING_RATE_LIMIT, - environment: ODIS_ALFAJORES_CONTEXT_DOMAINS, - }, - computational: { - function: ComputationalHardeningFunction.SCRYPT, - cost: 32768, - blockSize: 8, - parallelization: 1, - }, -} - -export const E2E_TESTING_ALFAJORES_CONFIG: HardeningConfig = { - odis: { - rateLimit: E2E_TESTING_RATE_LIMIT, - environment: ODIS_ALFAJORES_CONTEXT_DOMAINS, - }, - computational: { - function: ComputationalHardeningFunction.SCRYPT, - cost: 32768, - blockSize: 8, - parallelization: 1, - }, -} - -export const NO_QUOTA_ALFAJORES_CONFIG: HardeningConfig = { - odis: { - rateLimit: NO_QUOTA_RATE_LIMIT, - environment: ODIS_ALFAJORES_CONTEXT_DOMAINS, - }, - computational: { - function: ComputationalHardeningFunction.SCRYPT, - cost: 32768, - blockSize: 8, - parallelization: 1, - }, -} diff --git a/packages/sdk/encrypted-backup/src/errors.ts b/packages/sdk/encrypted-backup/src/errors.ts deleted file mode 100644 index 9a348e92f98..00000000000 --- a/packages/sdk/encrypted-backup/src/errors.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { RootError } from '@celo/base/lib/result' -import { CircuitBreakerError } from '@celo/identity/lib/odis/circuit-breaker' -import { ScryptOptions } from './utils' - -export enum BackupErrorTypes { - AUTHORIZATION_ERROR = 'AUTHORIZATION_ERROR', - DECODE_ERROR = 'DECODE_ERROR', - DECRYPTION_ERROR = 'DECRYPTION_ERROR', - ENCRYPTION_ERROR = 'ENCRYPTION_ERROR', - FETCH_ERROR = 'FETCH_ERROR', - INVALID_BACKUP_ERROR = 'INVALID_BACKUP_ERROR', - ODIS_SERVICE_ERROR = 'ODIS_SERVICE_ERROR', - ODIS_RATE_LIMITING_ERROR = 'ODIS_RATE_LIMITING_ERROR', - ODIS_VERIFICATION_ERROR = 'ODIS_VERIFICATION_ERROR', - PBKDF_ERROR = 'PBKDF_ERROR', - SCRYPT_ERROR = 'SCRYPT_ERROR', - USAGE_ERROR = 'USAGE_ERROR', -} - -export class AuthorizationError extends RootError { - constructor(readonly error?: Error) { - super(BackupErrorTypes.AUTHORIZATION_ERROR) - } -} - -export class DecodeError extends RootError { - constructor(readonly error?: Error) { - super(BackupErrorTypes.DECODE_ERROR) - } -} - -export class DecryptionError extends RootError { - constructor(readonly error?: Error) { - super(BackupErrorTypes.DECRYPTION_ERROR) - } -} - -export class EncryptionError extends RootError { - constructor(readonly error?: Error) { - super(BackupErrorTypes.ENCRYPTION_ERROR) - } -} - -export class FetchError extends RootError { - constructor(readonly error?: Error) { - super(BackupErrorTypes.FETCH_ERROR) - } -} - -export class InvalidBackupError extends RootError { - constructor(readonly error?: Error) { - super(BackupErrorTypes.INVALID_BACKUP_ERROR) - } -} - -export class OdisServiceError extends RootError { - constructor(readonly error?: Error, readonly version?: string) { - super(BackupErrorTypes.ODIS_SERVICE_ERROR) - } -} - -export class OdisRateLimitingError extends RootError { - constructor(readonly notBefore?: number, readonly error?: Error) { - super(BackupErrorTypes.ODIS_RATE_LIMITING_ERROR) - } -} - -export class OdisVerificationError extends RootError { - constructor(readonly error?: Error) { - super(BackupErrorTypes.ODIS_VERIFICATION_ERROR) - } -} - -export class PbkdfError extends RootError { - constructor(readonly iterations: number, readonly error?: Error) { - super(BackupErrorTypes.PBKDF_ERROR) - } -} - -export class ScryptError extends RootError { - constructor(readonly options: ScryptOptions, readonly error?: Error) { - super(BackupErrorTypes.SCRYPT_ERROR) - } -} - -export class UsageError extends RootError { - constructor(readonly error?: Error) { - super(BackupErrorTypes.USAGE_ERROR) - } -} - -export type BackupError = - | AuthorizationError - | CircuitBreakerError - | DecodeError - | DecryptionError - | EncryptionError - | FetchError - | InvalidBackupError - | OdisServiceError - | OdisRateLimitingError - | OdisVerificationError - | PbkdfError - | ScryptError - | UsageError diff --git a/packages/sdk/encrypted-backup/src/globals.d.ts b/packages/sdk/encrypted-backup/src/globals.d.ts deleted file mode 100644 index 0318570a84f..00000000000 --- a/packages/sdk/encrypted-backup/src/globals.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare const fetchMock diff --git a/packages/sdk/encrypted-backup/src/index.ts b/packages/sdk/encrypted-backup/src/index.ts deleted file mode 100644 index 24d4b9f2dfa..00000000000 --- a/packages/sdk/encrypted-backup/src/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './backup' -export * from './config' -export * from './errors' -export * from './odis' -export * from './schema' diff --git a/packages/sdk/encrypted-backup/src/odis.mock.ts b/packages/sdk/encrypted-backup/src/odis.mock.ts deleted file mode 100644 index 1416449188d..00000000000 --- a/packages/sdk/encrypted-backup/src/odis.mock.ts +++ /dev/null @@ -1,184 +0,0 @@ -import { ServiceContext as OdisServiceContext } from '@celo/identity/lib/odis/query' -import { - checkSequentialDelayRateLimit, - DomainEndpoint, - domainHash, - DomainQuotaStatusRequest, - DomainQuotaStatusResponse, - DomainRestrictedSignatureRequest, - DomainRestrictedSignatureResponse, - PoprfServer, - SequentialDelayDomain, - SequentialDelayDomainState, - verifyDomainQuotaStatusRequestAuthenticity, - verifyDomainRestrictedSignatureRequestAuthenticity, -} from '@celo/phone-number-privacy-common' -import * as poprf from '@celo/poprf' -import debugFactory from 'debug' - -const debug = debugFactory('kit:encrypted-backup:odis:mock') - -const MOCK_ODIS_KEYPAIR = poprf.keygen(Buffer.from('MOCK ODIS KEYPAIR SEED')) - -export const MOCK_ODIS_ENVIRONMENT: OdisServiceContext = { - odisUrl: 'https://mockodis.com', - odisPubKey: Buffer.from(MOCK_ODIS_KEYPAIR.publicKey).toString('base64'), -} - -export class MockOdis { - static readonly environment = MOCK_ODIS_ENVIRONMENT - - readonly state: Record = {} - readonly poprf = new PoprfServer(MOCK_ODIS_KEYPAIR.privateKey) - - private now = () => Math.floor(Date.now() / 1000) - - private domainState(hash: Buffer) { - return ( - this.state[hash.toString('hex')] ?? { timer: 0, counter: 0, disabled: false, now: this.now() } - ) - } - - quota(req: DomainQuotaStatusRequest): { - status: number - body: DomainQuotaStatusResponse - } { - const authorized = verifyDomainQuotaStatusRequestAuthenticity(req) - if (!authorized) { - return { - status: 401, - body: { - success: false, - version: 'mock', - error: 'unauthorized', - }, - } - } - - return { - status: 200, - body: { - success: true, - version: 'mock', - status: this.domainState(domainHash(req.domain)), - }, - } - } - - sign(req: DomainRestrictedSignatureRequest): { - status: number - body: DomainRestrictedSignatureResponse - } { - const authorized = verifyDomainRestrictedSignatureRequestAuthenticity(req) - if (!authorized) { - return { - status: 401, - body: { - success: false, - version: 'mock', - error: 'unauthorized', - status: undefined, - }, - } - } - - const hash = domainHash(req.domain) - const domainState = this.domainState(hash) - const nonce = req.options.nonce.defined ? req.options.nonce.value : undefined - if (nonce !== domainState.counter) { - return { - status: 403, - body: { - success: false, - version: 'mock', - error: 'incorrect nonce', - status: domainState, - }, - } - } - - const limitCheck = checkSequentialDelayRateLimit(req.domain, this.now(), domainState) - if (!limitCheck.accepted || limitCheck.state === undefined) { - return { - status: 429, - body: { - success: false, - version: 'mock', - error: 'request limit exceeded', - status: domainState, - }, - } - } - this.state[hash.toString('hex')] = limitCheck.state - - let signature: string - try { - signature = this.poprf - .blindEval(hash, Buffer.from(req.blindedMessage, 'base64')) - .toString('base64') - } catch (error) { - return { - // TODO(victor) Note that although this is a returned as a 500, the fault my actually be the - // users because the blinded message is not validated in JS before attempting the evaluation. - // This logic is the same in the real service. When validation functions are added to the - // WASM interface for the POPRF, this can be improved. - status: 500, - body: { - success: false, - version: 'mock', - status: undefined, - error: (error as Error).toString(), - }, - } - } - - return { - status: 200, - body: { - success: true, - version: 'mock', - status: limitCheck.state, - signature, - }, - } - } - - installQuotaEndpoint(mock: typeof fetchMock, override?: any) { - mock.mock( - { - url: new URL(DomainEndpoint.DOMAIN_QUOTA_STATUS, MockOdis.environment.odisUrl).href, - method: 'POST', - }, - override ?? - ((url: string, req: { body: string }) => { - const res = this.quota( - JSON.parse(req.body) as DomainQuotaStatusRequest - ) - debug('Mocking request', JSON.stringify({ url, req, res })) - return res - }) - ) - } - - installSignEndpoint(mock: typeof fetchMock, override?: any) { - mock.mock( - { - url: new URL(DomainEndpoint.DOMAIN_SIGN, MockOdis.environment.odisUrl).href, - method: 'POST', - }, - override ?? - ((url: string, req: { body: string }) => { - const res = this.sign( - JSON.parse(req.body) as DomainRestrictedSignatureRequest - ) - debug('Mocking request', JSON.stringify({ url, req, res })) - return res - }) - ) - } - - install(mock: typeof fetchMock) { - this.installQuotaEndpoint(mock) - this.installSignEndpoint(mock) - } -} diff --git a/packages/sdk/encrypted-backup/src/odis.ts b/packages/sdk/encrypted-backup/src/odis.ts deleted file mode 100644 index e7630ebf738..00000000000 --- a/packages/sdk/encrypted-backup/src/odis.ts +++ /dev/null @@ -1,291 +0,0 @@ -import { Address } from '@celo/base/lib/address' -import { Err, Ok, Result } from '@celo/base/lib/result' -import { - ErrorMessages, - sendOdisDomainRequest, - ServiceContext as OdisServiceContext, -} from '@celo/identity/lib/odis/query' -import { - checkSequentialDelayRateLimit, - DomainEndpoint, - domainHash, - DomainIdentifiers, - DomainQuotaStatusRequest, - domainQuotaStatusRequestEIP712, - DomainQuotaStatusResponse, - domainQuotaStatusResponseSchema, - DomainQuotaStatusResponseSuccess, - DomainRequestTypeTag, - DomainRestrictedSignatureRequest, - domainRestrictedSignatureRequestEIP712, - DomainRestrictedSignatureResponse, - domainRestrictedSignatureResponseSchema, - DomainRestrictedSignatureResponseSuccess, - genSessionID, - PoprfClient, - SequentialDelayDomain, - SequentialDelayDomainStateSchema, -} from '@celo/phone-number-privacy-common' -import { defined, noNumber, noString } from '@celo/utils/lib/sign-typed-data-utils' -import { LocalWallet } from '@celo/wallet-local' -import * as crypto from 'crypto' -import { OdisHardeningConfig } from './config' -import { - AuthorizationError, - BackupError, - FetchError, - OdisRateLimitingError, - OdisServiceError, - OdisVerificationError, - UsageError, -} from './errors' -import { deriveKey, EIP712Wallet, KDFInfo } from './utils' - -/** - * Builds an ODIS SequentialDelayDomain with the given hardening configuration. - * - * @param authorizer Address of the key that should authorize requests to ODIS. - * @returns A SequentialDelayDomain with the provided rate limiting configuration. - */ -export function buildOdisDomain( - config: OdisHardeningConfig, - authorizer: Address, - salt?: string -): SequentialDelayDomain { - return { - name: DomainIdentifiers.SequentialDelay, - version: '1', - stages: config.rateLimit, - address: defined(authorizer), - salt: salt ? defined(salt) : noString, - } -} - -/** - * Returns a hardened key derived from the input key material and a POPRF evaluation on that keying - * material under the given rate limiting domain. - * - * @param key Input key material which will be the blinded input to the ODIS POPRF. - * @param domain Rate limiting configuration and domain input to the ODIS POPRF. - * @param environment Information for the targeted ODIS environment. - * @param wallet Wallet with access to the authorizer signing key specified in the domain input. - * Should be provided if the input domain is authenticated. - */ -export async function odisHardenKey( - key: Buffer, - domain: SequentialDelayDomain, - environment: OdisServiceContext, - wallet?: EIP712Wallet -): Promise> { - // Session ID for logging requests. - const sessionID = genSessionID() - - // Request the quota status for the domain to get the state, including the quota counter. - const quotaResp = await requestOdisDomainQuotaStatus(domain, environment, sessionID, wallet) - if (!quotaResp.ok) { - return quotaResp - } - - // Check locally whether or not we should expect to be able to make a query to ODIS right now. - // Note that this uses the servers timestamp through the `quotaState.now` field. This is because - // mobile clients may have a large clock drift. This prevents that clock drift from resulting in - // misinterpretations of the domain quota. - const quotaState = quotaResp.result.status - const quotaResult = checkSequentialDelayRateLimit( - domain, - // Use the local clock as a fallback. Divide by 1000 to get seconds from ms. - quotaState.now ?? Date.now() / 1000, - quotaState - ) - if (!quotaResult.accepted) { - return Err( - new OdisRateLimitingError( - quotaResult.notBefore, - new Error('client does not currently have quota based on status response.') - ) - ) - } - - // Instantiate a blinding client and blind the key derived from the users password to be hardened. - // NOTE: We do not include a response aggregation step here because it is assumed that we are - // talking to the combiner service, as opposed to talking directly to the signers. - const blindingSeed = crypto.randomBytes(16) - const poprfClient = new PoprfClient( - Buffer.from(environment.odisPubKey, 'base64'), - domainHash(domain), - key, - blindingSeed - ) - - // Request the partial oblivious signature from ODIS. - // Note that making this request will, if successful, result in quota being used in the domain. - const signatureResp = await requestOdisDomainSignature( - poprfClient.blindedMessage.toString('base64'), - quotaState.counter, - domain, - environment, - sessionID, - wallet - ) - if (!signatureResp.ok) { - return signatureResp - } - - // Unblind the signature response received from ODIS to get the POPRF output. - let odisOutput: Buffer - try { - odisOutput = await poprfClient.unblindResponse( - Buffer.from(signatureResp.result.signature, 'base64') - ) - } catch (error) { - return Err(new OdisVerificationError(error as Error)) - } - - // Mix the key with the output from ODIS to get the hardened key. - return Ok(deriveKey(KDFInfo.ODIS_KEY_HARDENING, [key, odisOutput])) -} - -/** - * Derive from the nonce a private key and use it to instantiate a wallet for request signing - * - * @remarks It is important that the auth key does not mix in entropy from the password value. If - * it did, then the derived address and signatures would act as a commitment to the underlying - * password value and would allow offline brute force attacks when combined with the other values - * mixed into the key value. - */ -export function odisQueryAuthorizer(nonce: Buffer): { address: Address; wallet: EIP712Wallet } { - // Derive the domain's request authorization key from the backup nonce. - const authKey = deriveKey(KDFInfo.ODIS_AUTH_KEY, [nonce]) - const wallet = new LocalWallet() - wallet.addAccount(authKey.toString('hex')) - const address = wallet.getAccounts()[0] - if (address === undefined) { - // Throw the error instead if returning it as this is more akin to a panic. - throw new Error('Implementation error: LocalWallet with an added account returned no accounts') - } - return { address, wallet } -} - -/** - * Returns a hardened key derived from the input key material and a POPRF evaluation on that keying - * material under the given rate limiting domain. - * - * @param domain Rate limiting configuration and domain input to the ODIS POPRF. - * @param environment Information for the targeted ODIS environment. - * @param sessionID client-defined session ID for tracking requests across services - * @param wallet Wallet with access to the authorizer signing key specified in the domain input. - * Should be provided if the input domain is authenticated. - */ -export async function requestOdisDomainQuotaStatus( - domain: SequentialDelayDomain, - environment: OdisServiceContext, - sessionID: string, - wallet?: EIP712Wallet -): Promise> { - const quotaStatusReq: DomainQuotaStatusRequest = { - type: DomainRequestTypeTag.QUOTA, - domain, - options: { - signature: noString, - nonce: noNumber, - }, - sessionID: defined(sessionID), - } - - // If a query authorizer is defined in the domain, include a signature over the request. - const authorizer = domain.address.defined ? domain.address.value : undefined - if (authorizer !== undefined) { - if (wallet === undefined || !wallet.hasAccount(authorizer)) { - return Err( - new AuthorizationError( - new Error('key for signing ODIS quota status request is unavailable') - ) - ) - } - quotaStatusReq.options.signature = defined( - await wallet.signTypedData(authorizer, domainQuotaStatusRequestEIP712(quotaStatusReq)) - ) - } else if (wallet !== undefined) { - return Err(new UsageError(new Error('wallet provided but the domain is unauthenticated'))) - } - - let quotaResp: DomainQuotaStatusResponse - try { - quotaResp = await sendOdisDomainRequest( - quotaStatusReq, - environment, - DomainEndpoint.DOMAIN_QUOTA_STATUS, - domainQuotaStatusResponseSchema(SequentialDelayDomainStateSchema) - ) - } catch (error) { - if ((error as Error).message?.includes(ErrorMessages.ODIS_FETCH_ERROR)) { - return Err(new FetchError(error as Error)) - } - return Err(new OdisServiceError(error as Error)) - } - if (!quotaResp.success) { - return Err(new OdisServiceError(new Error(quotaResp.error), quotaResp.version)) - } - - return Ok(quotaResp) -} - -async function requestOdisDomainSignature( - blindedMessage: string, - counter: number, - domain: SequentialDelayDomain, - environment: OdisServiceContext, - sessionID: string, - wallet?: EIP712Wallet -): Promise> { - const signatureReq: DomainRestrictedSignatureRequest = { - type: DomainRequestTypeTag.SIGN, - domain, - options: { - signature: noString, - nonce: defined(counter), - }, - blindedMessage, - sessionID: defined(sessionID), - } - - // If a query authorizer is defined in the domain, include a siganture over the request. - const authorizer = domain.address.defined ? domain.address.value : undefined - if (authorizer !== undefined) { - if (wallet === undefined || !wallet.hasAccount(authorizer)) { - return Err( - new AuthorizationError( - new Error('key for signing ODIS domain signature request is unavailable') - ) - ) - } - signatureReq.options.signature = defined( - await wallet.signTypedData(authorizer, domainRestrictedSignatureRequestEIP712(signatureReq)) - ) - } else if (wallet !== undefined) { - return Err(new UsageError(new Error('wallet provided but the domain is unauthenticated'))) - } - - let signatureResp: DomainRestrictedSignatureResponse - try { - signatureResp = await sendOdisDomainRequest( - signatureReq, - environment, - DomainEndpoint.DOMAIN_SIGN, - domainRestrictedSignatureResponseSchema(SequentialDelayDomainStateSchema) - ) - } catch (error) { - if ((error as Error).message?.includes(ErrorMessages.ODIS_FETCH_ERROR)) { - return Err(new FetchError(error as Error)) - } - if ((error as Error).message?.includes(ErrorMessages.ODIS_RATE_LIMIT_ERROR)) { - return Err(new OdisRateLimitingError(undefined, error as Error)) - } - return Err(new OdisServiceError(error as Error)) - } - if (!signatureResp.success) { - return Err(new OdisServiceError(new Error(signatureResp.error), signatureResp.version)) - } - - return Ok(signatureResp) -} diff --git a/packages/sdk/encrypted-backup/src/schema.ts b/packages/sdk/encrypted-backup/src/schema.ts deleted file mode 100644 index 182da1a1a63..00000000000 --- a/packages/sdk/encrypted-backup/src/schema.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { Err, Ok, parseJsonAsResult, Result } from '@celo/base/lib/result' -import { SequentialDelayDomainSchema } from '@celo/phone-number-privacy-common/lib/domains' -import { chain, isLeft } from 'fp-ts/lib/Either' -import { pipe } from 'fp-ts/lib/pipeable' -import * as t from 'io-ts' -import { Backup } from './backup' -import { ComputationalHardeningFunction } from './config' -import { DecodeError } from './errors' - -const BASE64_REGEXP = /^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/ - -/** Utility type to leverage io-ts for encoding and decoding of buffers from base64 strings. */ -export const BufferFromBase64 = new t.Type( - 'BufferFromBase64', - Buffer.isBuffer, - (unk: unknown, context: t.Context) => - pipe( - t.string.validate(unk, context), - chain((str: string) => { - // Check that the string is base64 data and return the decoding if it is. - if (!BASE64_REGEXP.test(str)) { - return t.failure(unk, context, 'provided string is not base64') - } - return t.success(Buffer.from(str, 'base64')) - }) - ), - (buffer: Buffer) => buffer.toString('base64') -) - -/** io-ts codec used to encode and decode backups from JSON objects */ -export const BackupSchema: t.Type = t.intersection([ - // Required fields - t.type({ - encryptedData: BufferFromBase64, - nonce: BufferFromBase64, - version: t.string, - }), - // Optional fields - // https://github.com/gcanti/io-ts/blob/master/index.md#mixing-required-and-optional-props - t.partial({ - odisDomain: SequentialDelayDomainSchema, - metadata: t.UnknownRecord, - encryptedFuseKey: BufferFromBase64, - computationalHardening: t.union([ - t.type({ - function: t.literal(ComputationalHardeningFunction.PBKDF), - iterations: t.number, - }), - t.intersection([ - t.type({ - function: t.literal(ComputationalHardeningFunction.SCRYPT), - cost: t.number, - }), - t.partial({ - blockSize: t.number, - parallelization: t.number, - }), - ]), - ]), - environment: t.partial({ - odis: t.type({ - odisUrl: t.string, - odisPubKey: t.string, - }), - circuitBreaker: t.type({ - url: t.string, - publicKey: t.string, - }), - }), - }), -]) - -export function serializeBackup(backup: Backup): string { - return JSON.stringify(BackupSchema.encode(backup)) -} - -export function deserializeBackup(data: string): Result { - const jsonDecode = parseJsonAsResult(data) - if (!jsonDecode.ok) { - return Err(new DecodeError(jsonDecode.error)) - } - - const decoding = BackupSchema.decode(jsonDecode.result) - if (isLeft(decoding)) { - return Err( - new DecodeError( - new Error(`error in validating backup object: ${JSON.stringify(decoding.left)}`) - ) - ) - } - const backup = decoding.right - - if (backup.nonce.length !== 32) { - return Err( - new DecodeError( - new Error(`expected backup nonce to be 32 bytes but got ${backup.nonce.length}`) - ) - ) - } - - return Ok(backup) -} diff --git a/packages/sdk/encrypted-backup/src/utils.ts b/packages/sdk/encrypted-backup/src/utils.ts deleted file mode 100644 index 0637ec4e859..00000000000 --- a/packages/sdk/encrypted-backup/src/utils.ts +++ /dev/null @@ -1,158 +0,0 @@ -import { Err, Ok, Result } from '@celo/base/lib/result' -import { ReadOnlyWallet } from '@celo/connect' -import * as crypto from 'crypto' -import { ComputationalHardeningConfig, ComputationalHardeningFunction } from './config' -import { DecryptionError, EncryptionError, PbkdfError, ScryptError } from './errors' - -// NOTE: This module is intended for use within the @celo/encrypted-backup package and so is not -// exported in the index.ts file. - -/** Pared down ReadOnlyWallet type that supports the required functions of EIP-712 signing. */ -export type EIP712Wallet = Pick - -/** Info strings to separate distinct usages of the key derivation function */ -export enum KDFInfo { - PASSWORD = 'Celo Backup Password and Nonce', - FUSE_KEY = 'Celo Backup Fuse Key', - ODIS_AUTH_KEY = 'Celo Backup ODIS Request Authorization Key', - ODIS_KEY_HARDENING = 'Celo Backup ODIS Key Hardening', - PBKDF = 'Celo Backup PBKDF Hardening', - SCRYPT = 'Celo Backup scrypt Hardening', - FINALIZE = 'Celo Backup Key Finalization', -} - -/** - * Key derivation function for mixing source keying material. - * - * @remarks This function does not add any hardening to the input keying material. It is used only - * to mix the provided key material sources. It's output should not be used to directly derive a key - * from a password or other low entropy sources. - * - * @param info Fixed string value used for domain separation. - * @param sources An array of keying material source values (e.g. a password and a nonce). - */ -export function deriveKey(info: KDFInfo, sources: Buffer[]): Buffer { - // Hash each source keying material component, and the info value, to prevent hashing collisions - // that might result if the variable length data is simply concatenated. - const chunks = [Buffer.from(info, 'utf8'), ...sources].map((source: Buffer) => { - const hash = crypto.createHash('sha256') - hash.update(source) - return hash.digest() - }) - - // NOTE: We would prefer to use HKDF here, but is only available in Node v15 and above. - return crypto.pbkdf2Sync(Buffer.concat(chunks), Buffer.alloc(0), 1, 32, 'sha256') -} - -/** - * AES-256-GCM encrypt the given data with the given 32-byte key. - * Encode the ciphertext as { iv || data || auth tag } - */ -export function encrypt(key: Buffer, data: Buffer): Result { - try { - // NOTE: AES-GCM uses a 12-byte nonce. Longer nonces get hashed before use. - const nonce = crypto.randomBytes(12) - const cipher = crypto.createCipheriv('aes-256-gcm', key, nonce) - return Ok(Buffer.concat([nonce, cipher.update(data), cipher.final(), cipher.getAuthTag()])) - } catch (error) { - return Err(new EncryptionError(error as Error)) - } -} - -/** - * AES-256-GCM decrypt the given data with the given 32-byte key. - * Ciphertext should be encoded as { iv || data || auth tag }. - */ -export function decrypt(key: Buffer, ciphertext: Buffer): Result { - const len = ciphertext.length - if (len < 28) { - return Err( - new DecryptionError( - new Error(`ciphertext is too short: expected at least 28 bytes, but got ${len}`) - ) - ) - } - - try { - // NOTE: AES-GCM uses a 12-byte nonce. Longer nonces get hashed before use. - const nonce = ciphertext.slice(0, 12) - const ciphertextData = ciphertext.slice(12, len - 16) - const auth = ciphertext.slice(len - 16, len) - const decipher = crypto.createDecipheriv('aes-256-gcm', key, nonce) - decipher.setAuthTag(auth) - return Ok(Buffer.concat([decipher.update(ciphertextData), decipher.final()])) - } catch (error) { - return Err(new DecryptionError(error as Error)) - } -} - -/** - * PBKDF2-SHA256 computational key hardening. - * - * @remarks When possible, a memory hard function such as scrypt should be used instead. - * No salt parameter is provided as the intended use case of this function is to harden a - * key value which is derived from a password but already has the salt mixed in. - * - * @see { @link - * https://nodejs.org/api/crypto.html#cryptopbkdf2password-salt-iterations-keylen-digest-callback | - * NodeJS crypto.pbkdf2 API } - * - * @param key Key buffer to compute hardening against. Should have a salt or nonce mixed in. - * @param iterations Number of PBKDF2 iterations to execute for key hardening. - */ -export function pbkdf2(key: Buffer, iterations: number): Promise> { - return new Promise((resolve) => { - crypto.pbkdf2(key, KDFInfo.PBKDF, iterations, 32, 'sha256', (error, result) => { - if (error) { - resolve(Err(new PbkdfError(iterations, error))) - } - resolve(Ok(result)) - }) - }) -} - -/** Cost parameters for the scrypt computational hardening function. */ -export interface ScryptOptions { - cost: number - blockSize?: number - parallelization?: number -} - -/** - * scrypt computational key hardening. - * - * @remarks No salt parameter is provided as the intended use case of this function is to harden a - * key value which is derived from a password but already has the salt mixed in. - * - * @see { @link - * https://nodejs.org/api/crypto.html#cryptoscryptpassword-salt-keylen-options-callback | - * NodeJS crypto.scrypt API } - * - * @param key Key buffer to compute hardening against. Should have a salt or nonce mixed in. - * @param options Options to control the cost of the scrypt function. - */ -export function scrypt(key: Buffer, options: ScryptOptions): Promise> { - // Define the maxmem parameter to be large enough to accommodate the provided options. - // See the Node JS crypto implementation of scrypt for more detail. - const maxmem = Math.max(32 * 1024 * 1024, 128 * options.cost * (options.blockSize ?? 8)) - return new Promise((resolve) => { - crypto.scrypt(key, KDFInfo.SCRYPT, 32, { maxmem, ...options }, (error, result) => { - if (error) { - resolve(Err(new ScryptError(options, error))) - } - resolve(Ok(result)) - }) - }) -} - -export function computationalHardenKey( - key: Buffer, - config: ComputationalHardeningConfig -): Promise> { - switch (config.function) { - case ComputationalHardeningFunction.PBKDF: - return pbkdf2(key, config.iterations) - case ComputationalHardeningFunction.SCRYPT: - return scrypt(key, config) - } -} diff --git a/packages/sdk/encrypted-backup/tsconfig.json b/packages/sdk/encrypted-backup/tsconfig.json deleted file mode 100644 index 25d848de425..00000000000 --- a/packages/sdk/encrypted-backup/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "@celo/typescript/tsconfig.library.json", - "compilerOptions": { - "rootDir": "src", - "outDir": "lib" - }, - "include": ["src/**/*", "types/**/*"] -} diff --git a/packages/sdk/encrypted-backup/tslint.json b/packages/sdk/encrypted-backup/tslint.json deleted file mode 100644 index 036f000683b..00000000000 --- a/packages/sdk/encrypted-backup/tslint.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": ["@celo/typescript/tslint.json"], - "rules": { - "no-global-arrow-functions": false, - "no-console": false, - "member-ordering": false, - "max-classes-per-file": false - } -} diff --git a/packages/sdk/encrypted-backup/typedoc.json b/packages/sdk/encrypted-backup/typedoc.json deleted file mode 100644 index 6947ff82523..00000000000 --- a/packages/sdk/encrypted-backup/typedoc.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "mode": "modules", - "exclude": ["**/generated/*.ts", "**/*+(index|.test).ts"], - "excludeNotExported": true, - "excludePrivate": true, - "excludeProtected": true, - "includeDeclarations": false, - "ignoreCompilerErrors": true, - "hideGenerator": "true", - "out": "../../docs/sdk/docs/encrypted-backup", - "gitRevision": "master", - "readme": "none" - } \ No newline at end of file diff --git a/packages/sdk/explorer/CHANGELOG.md b/packages/sdk/explorer/CHANGELOG.md new file mode 100644 index 00000000000..cac02fd6055 --- /dev/null +++ b/packages/sdk/explorer/CHANGELOG.md @@ -0,0 +1,35 @@ +# @celo/explorer + +## 5.0.5 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- d48c68afc: Calls to getContractMappingFromSourcify() are now memoized in the same structure (this.addressMapping) as getContractMappingFromCore, getContractMappingWithSelector now runs in parallel +- Updated dependencies [d48c68afc] +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [d48c68afc] + - @celo/contractkit@5.1.0 + - @celo/connect@5.1.0 + - @celo/utils@5.0.5 + - @celo/base@5.0.5 + +## 5.0.5-beta.0 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- d48c68afc: Calls to getContractMappingFromSourcify() are now memoized in the same structure (this.addressMapping) as getContractMappingFromCore, getContractMappingWithSelector now runs in parallel +- Updated dependencies [d48c68afc] +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [d48c68afc] + - @celo/contractkit@5.1.0-beta.0 + - @celo/connect@5.1.0-beta.0 + - @celo/utils@5.0.5-beta.0 + - @celo/base@5.0.5-beta.0 diff --git a/packages/sdk/explorer/jest.config.js b/packages/sdk/explorer/jest.config.js index ea4971b7402..31e6b5e7485 100644 --- a/packages/sdk/explorer/jest.config.js +++ b/packages/sdk/explorer/jest.config.js @@ -1,13 +1,6 @@ -const { nodeFlakeTracking } = require('@celo/flake-tracker/src/jest/config.js') - module.exports = { preset: 'ts-jest', - ...nodeFlakeTracking, testMatch: ['/src/**/?(*.)+(spec|test).ts?(x)'], - setupFilesAfterEnv: [ - '@celo/dev-utils/lib/matchers', - '/jestSetup.ts', - ...nodeFlakeTracking.setupFilesAfterEnv, - ], + setupFilesAfterEnv: ['@celo/dev-utils/lib/matchers', '/jestSetup.ts'], verbose: true, } diff --git a/packages/sdk/explorer/package.json b/packages/sdk/explorer/package.json index 32f50dd017d..f262bb90c8a 100644 --- a/packages/sdk/explorer/package.json +++ b/packages/sdk/explorer/package.json @@ -1,6 +1,6 @@ { "name": "@celo/explorer", - "version": "4.1.1-dev", + "version": "5.0.5", "description": "Celo's block explorer consumer", "main": "./lib/index.js", "types": "./lib/index.d.ts", @@ -22,12 +22,13 @@ "prepublishOnly": "yarn build" }, "dependencies": { - "@celo/base": "4.1.1-dev", - "@celo/connect": "4.1.1-dev", - "@celo/contractkit": "4.1.1-dev", - "@celo/utils": "4.1.1-dev", + "@celo/base": "^5.0.5", + "@celo/connect": "^5.1.0", + "@celo/contractkit": "^5.1.0", + "@celo/utils": "^5.0.5", "@types/debug": "^4.1.5", "cross-fetch": "3.0.6", + "bignumber.js": "9.0.0", "debug": "^4.1.1" }, "devDependencies": { diff --git a/packages/sdk/explorer/src/block-explorer.ts b/packages/sdk/explorer/src/block-explorer.ts index 8c612da6545..a7b84489087 100644 --- a/packages/sdk/explorer/src/block-explorer.ts +++ b/packages/sdk/explorer/src/block-explorer.ts @@ -315,8 +315,16 @@ export class BlockExplorer { getContractMappingFromSourcify = async ( address: string ): Promise => { + const cached = this.addressMapping.get(address) + if (cached) { + return cached + } const metadata = await fetchMetadata(this.kit.connection, address) - return metadata?.toContractMapping() + const mapping = metadata?.toContractMapping() + if (mapping) { + this.addressMapping.set(address, mapping) + } + return mapping } /** @@ -369,11 +377,14 @@ export class BlockExplorer { this.getContractMappingFromSourcifyAsProxy, ] ): Promise { - for (const strategy of strategies) { - const contractMapping = await strategy(address) - if (contractMapping && contractMapping.fnMapping.get(selector)) { - return contractMapping - } - } + const mappings = await Promise.all( + strategies.map(async (strategy) => { + const contractMapping = await strategy(address) + if (contractMapping && contractMapping.fnMapping.get(selector)) { + return contractMapping + } + }) + ) + return mappings.find((mapping) => mapping !== undefined) } } diff --git a/packages/sdk/governance/CHANGELOG.md b/packages/sdk/governance/CHANGELOG.md new file mode 100644 index 00000000000..9f4a5a1fc05 --- /dev/null +++ b/packages/sdk/governance/CHANGELOG.md @@ -0,0 +1,39 @@ +# @celo/governance + +## 5.0.5 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- d48c68afc: Parallelize Fetching Proposal Info +- Updated dependencies [d48c68afc] +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [d48c68afc] +- Updated dependencies [d48c68afc] + - @celo/contractkit@5.1.0 + - @celo/connect@5.1.0 + - @celo/explorer@5.0.5 + - @celo/utils@5.0.5 + - @celo/base@5.0.5 + +## 5.0.5-beta.0 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- d48c68afc: Parallelize Fetching Proposal Info +- Updated dependencies [d48c68afc] +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [d48c68afc] +- Updated dependencies [d48c68afc] + - @celo/contractkit@5.1.0-beta.0 + - @celo/connect@5.1.0-beta.0 + - @celo/explorer@5.0.5-beta.0 + - @celo/utils@5.0.5-beta.0 + - @celo/base@5.0.5-beta.0 diff --git a/packages/sdk/governance/package.json b/packages/sdk/governance/package.json index 84abfe0cd70..c3a6e59eaca 100644 --- a/packages/sdk/governance/package.json +++ b/packages/sdk/governance/package.json @@ -1,6 +1,6 @@ { "name": "@celo/governance", - "version": "4.1.1-dev", + "version": "5.0.5", "description": "Celo's governance proposals", "main": "./lib/index.js", "types": "./lib/index.d.ts", @@ -21,15 +21,16 @@ "prepublishOnly": "yarn build" }, "dependencies": { - "@celo/base": "4.1.1-dev", - "@celo/utils": "4.1.1-dev", - "@celo/connect": "4.1.1-dev", - "@celo/contractkit": "4.1.1-dev", - "@celo/explorer": "4.1.1-dev", + "@celo/base": "^5.0.5", + "@celo/utils": "^5.0.5", + "@celo/connect": "^5.1.0", + "@celo/contractkit": "^5.1.0", + "@celo/explorer": "^5.0.5", "@ethereumjs/util": "8.0.5", "@types/debug": "^4.1.5", "@types/inquirer": "^6.5.0", "debug": "^4.1.1", + "bignumber.js": "^9.0.0", "ethereum-cryptography": "1.2.0", "inquirer": "^7.0.5" }, diff --git a/packages/sdk/governance/src/proposals.ts b/packages/sdk/governance/src/proposals.ts index c1329e17f51..bb54d2290a6 100644 --- a/packages/sdk/governance/src/proposals.ts +++ b/packages/sdk/governance/src/proposals.ts @@ -136,91 +136,91 @@ export const proposalToJSON = async ( debug(`updating registry to reflect ${name} => ${address}`) await blockExplorer.updateContractDetailsMapping(stripProxy(name), address) } - if (registryAdditions) { // Update the registry mapping with registry additions prior to processing the proposal. - for (const nameStr of Object.keys(registryAdditions)) { - const name = nameStr as CeloContract - if (CeloContract[name]) { - await updateRegistryMapping(name, registryAdditions[name]) - } else { - debug(`Name ${nameStr} in registry additions not a CeloContract`) - } - } + await Promise.all( + Object.keys(registryAdditions).map(async (nameStr) => { + const name = nameStr as CeloContract + if (CeloContract[name]) { + await updateRegistryMapping(name, registryAdditions[name]) + } else { + debug(`Name ${nameStr} in registry additions not a CeloContract`) + } + }) + ) } - const abiCoder = kit.connection.getAbiCoder() - const proposalJson: ProposalTransactionJSON[] = [] - - for (const tx of proposal) { - debug(`decoding tx ${JSON.stringify(tx)}`) - const parsedTx = await blockExplorer.tryParseTx(tx as CeloTxPending) - if (parsedTx == null) { - throw new Error(`Unable to parse ${JSON.stringify(tx)} with block explorer`) - } - if (isRegistryRepointRaw(abiCoder, tx) && parsedTx.callDetails.isCoreContract) { - const args = registryRepointRawArgs(abiCoder, tx) - await updateRegistryMapping(args.name, args.address) - } + const proposalJson: ProposalTransactionJSON[] = await Promise.all( + proposal.map(async (tx) => { + debug(`decoding tx ${JSON.stringify(tx)}`) + const parsedTx = await blockExplorer.tryParseTx(tx as CeloTxPending) + if (parsedTx == null) { + throw new Error(`Unable to parse ${JSON.stringify(tx)} with block explorer`) + } + if (isRegistryRepointRaw(abiCoder, tx) && parsedTx.callDetails.isCoreContract) { + const args = registryRepointRawArgs(abiCoder, tx) + await updateRegistryMapping(args.name, args.address) + } - const jsonTx: ProposalTransactionJSON = { - contract: parsedTx.callDetails.contract as CeloContract, - address: parsedTx.callDetails.contractAddress, - function: parsedTx.callDetails.function, - args: parsedTx.callDetails.argList, - params: parsedTx.callDetails.paramMap, - value: parsedTx.tx.value, - } + const jsonTx: ProposalTransactionJSON = { + contract: parsedTx.callDetails.contract as CeloContract, + address: parsedTx.callDetails.contractAddress, + function: parsedTx.callDetails.function, + args: parsedTx.callDetails.argList, + params: parsedTx.callDetails.paramMap, + value: parsedTx.tx.value, + } - if (isProxySetFunction(jsonTx)) { - jsonTx.contract = suffixProxy(jsonTx.contract) - await blockExplorer.setProxyOverride(tx.to!, jsonTx.args[0]) - } else if (isProxySetAndInitFunction(jsonTx)) { - await blockExplorer.setProxyOverride(tx.to!, jsonTx.args[0]) - let initAbi - if (parsedTx.callDetails.isCoreContract) { + if (isProxySetFunction(jsonTx)) { jsonTx.contract = suffixProxy(jsonTx.contract) - initAbi = getInitializeAbiOfImplementation(jsonTx.contract as any) - } else { - const implAddress = jsonTx.args[0] - const metadata = await fetchMetadata(kit.connection, implAddress) - if (metadata && metadata.abi) { - initAbi = metadata?.abiForMethod('initialize')[0] + await blockExplorer.setProxyOverride(tx.to!, jsonTx.args[0]) + } else if (isProxySetAndInitFunction(jsonTx)) { + await blockExplorer.setProxyOverride(tx.to!, jsonTx.args[0]) + let initAbi + if (parsedTx.callDetails.isCoreContract) { + jsonTx.contract = suffixProxy(jsonTx.contract) + initAbi = getInitializeAbiOfImplementation(jsonTx.contract as any) + } else { + const implAddress = jsonTx.args[0] + const metadata = await fetchMetadata(kit.connection, implAddress) + if (metadata && metadata.abi) { + initAbi = metadata?.abiForMethod('initialize')[0] + } } - } - if (initAbi !== undefined) { - // Transform delegate call initialize args into a readable params map - // 8 bytes for function sig - const initSig = trimLeading0x(jsonTx.args[1]).slice(0, 8) - const initArgs = trimLeading0x(jsonTx.args[1]).slice(8) + if (initAbi !== undefined) { + // Transform delegate call initialize args into a readable params map + // 8 bytes for function sig + const initSig = trimLeading0x(jsonTx.args[1]).slice(0, 8) + const initArgs = trimLeading0x(jsonTx.args[1]).slice(8) - const { params: initParams } = parseDecodedParams( - kit.connection.getAbiCoder().decodeParameters(initAbi.inputs!, initArgs) - ) - jsonTx.params![`initialize@${initSig}`] = initParams - } - } else if (isGovernanceConstitutionSetter(jsonTx)) { - const [address, functionId, threshold] = jsonTx.args - const contractMapping = await blockExplorer.getContractMappingWithSelector( - address, - functionId - ) - if (contractMapping === undefined) { - throw new Error( - `Governance.setConstitution targets unknown address ${address} and function id ${functionId}` + const { params: initParams } = parseDecodedParams( + kit.connection.getAbiCoder().decodeParameters(initAbi.inputs!, initArgs) + ) + jsonTx.params![`initialize@${initSig}`] = initParams + } + } else if (isGovernanceConstitutionSetter(jsonTx)) { + const [address, functionId, threshold] = jsonTx.args + const contractMapping = await blockExplorer.getContractMappingWithSelector( + address, + functionId ) + if (contractMapping === undefined) { + throw new Error( + `Governance.setConstitution targets unknown address ${address} and function id ${functionId}` + ) + } + jsonTx.params![`setConstitution[${address}][${functionId}]`] = { + contract: contractMapping.details.name, + method: contractMapping.fnMapping.get(functionId)?.name, + threshold: fromFixed(new BigNumber(threshold)), + } } - jsonTx.params![`setConstitution[${address}][${functionId}]`] = { - contract: contractMapping.details.name, - method: contractMapping.fnMapping.get(functionId)?.name, - threshold: fromFixed(new BigNumber(threshold)), - } - } + return jsonTx + }) + ) - proposalJson.push(jsonTx) - } return proposalJson } diff --git a/packages/sdk/identity/.gitignore b/packages/sdk/identity/.gitignore deleted file mode 100644 index 7fabe89f619..00000000000 --- a/packages/sdk/identity/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -lib/ -tmp/ -.tmp/ -.env \ No newline at end of file diff --git a/packages/sdk/identity/.npmignore b/packages/sdk/identity/.npmignore deleted file mode 100644 index 026e06c0cde..00000000000 --- a/packages/sdk/identity/.npmignore +++ /dev/null @@ -1,23 +0,0 @@ -/.devchain/ -/.devchain.tar.gz -/coverage/ -/node_modules/ -/src/ -/tmp/ -/.tmp/ - -/tslint.json -/tsconfig.* -/jest.config.* -*.tgz - -/src - -/lib/**/*.test.* -/lib/test-utils -# exclude ts files and sourcemaps -*.map -*.ts - -# include the .d.ts files -!lib/**/*.d.ts \ No newline at end of file diff --git a/packages/sdk/identity/README.md b/packages/sdk/identity/README.md deleted file mode 100644 index 2fa8d9b209d..00000000000 --- a/packages/sdk/identity/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# @celo/identity - -@celo/identity simplifies interacting with ODIS, Celo’s lightweight identity layer based on phone numbers. - -You can find an example of how to use this package to do ODIS lookups [here](https://github.com/critesjosh/register-number). diff --git a/packages/sdk/identity/jest.config.js b/packages/sdk/identity/jest.config.js deleted file mode 100644 index eadc69611a1..00000000000 --- a/packages/sdk/identity/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -const { nodeFlakeTracking } = require('@celo/flake-tracker/src/jest/config.js') - -module.exports = { - preset: 'ts-jest', - ...nodeFlakeTracking, - rootDir: './src/', - testMatch: ['/**/?(*.)+(spec|test).ts?(x)'], - setupFilesAfterEnv: ['@celo/dev-utils/lib/matchers', ...nodeFlakeTracking.setupFilesAfterEnv], - globalSetup: '/test-utils/setup.global.ts', - globalTeardown: '/test-utils/teardown.global.ts', - verbose: true, -} diff --git a/packages/sdk/identity/package.json b/packages/sdk/identity/package.json deleted file mode 100644 index 5ba44dd05c1..00000000000 --- a/packages/sdk/identity/package.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "name": "@celo/identity", - "version": "4.1.1-dev", - "description": "Utilities for interacting with Celo's identity protocol", - "main": "./lib/index.js", - "types": "./lib/index.d.ts", - "author": "Celo", - "license": "Apache-2.0", - "homepage": "https://celo-sdk-docs.readthedocs.io/en/latest/identity", - "repository": "https://github.com/celo-org/celo-monorepo/tree/master/packages/sdk/identity", - "keywords": [ - "celo", - "blockchain", - "contractkit", - "odis" - ], - "scripts": { - "build": "tsc -b .", - "clean": "tsc -b . --clean", - "docs": "typedoc", - "test:reset": "yarn --cwd ../../protocol devchain:reset --migration_override ../../dev-utils/src/migration-override.json", - "test:livechain": "yarn --cwd ../../protocol devchain run-tar .tmp/devchain.tar.gz", - "test": "jest --runInBand --testPathIgnorePatterns src/odis/identifier-backwards-compatibility.test.ts", - "lint": "tslint -c tslint.json --project .", - "prepublishOnly": "yarn build" - }, - "dependencies": { - "@celo/base": "4.1.1-dev", - "@celo/utils": "4.1.1-dev", - "@celo/contractkit": "4.1.1-dev", - "@celo/phone-number-privacy-common": "^3.0.0-dev", - "@types/debug": "^4.1.5", - "bignumber.js": "^9.0.0", - "blind-threshold-bls": "https://github.com/celo-org/blind-threshold-bls-wasm#e1e2f8a", - "cross-fetch": "3.0.4", - "debug": "^4.1.1", - "elliptic": "^6.5.4", - "ethereum-cryptography": "1.2.0", - "fp-ts": "2.1.1", - "io-ts": "2.0.1" - }, - "devDependencies": { - "@celo/dev-utils": "0.0.1-dev", - "@celo/flake-tracker": "0.0.1-dev", - "@celo/wallet-local": "4.1.1-dev", - "@types/elliptic": "^6.4.12", - "fetch-mock": "9.10.4", - "ganache": "npm:@soloseng/ganache@7.8.0-beta.1", - "old-identity-sdk": "npm:@celo/identity@1.5.2" - }, - "engines": { - "node": ">=12.9.0" - } -} diff --git a/packages/sdk/identity/src/__mocks__/cross-fetch.ts b/packages/sdk/identity/src/__mocks__/cross-fetch.ts deleted file mode 100644 index 7435c07d5eb..00000000000 --- a/packages/sdk/identity/src/__mocks__/cross-fetch.ts +++ /dev/null @@ -1,3 +0,0 @@ -const fetchMockSandbox = require('fetch-mock').sandbox() - -export default fetchMockSandbox diff --git a/packages/sdk/identity/src/index.ts b/packages/sdk/identity/src/index.ts deleted file mode 100644 index 4f398dd4648..00000000000 --- a/packages/sdk/identity/src/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { OdisUtils } from './odis' diff --git a/packages/sdk/identity/src/odis/bls-blinding-client.ts b/packages/sdk/identity/src/odis/bls-blinding-client.ts deleted file mode 100644 index 9ceedf633c3..00000000000 --- a/packages/sdk/identity/src/odis/bls-blinding-client.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { randomBytes } from 'crypto' - -export interface BlsBlindingClient { - blindMessage: (base64PhoneNumber: string, seed?: Buffer) => Promise - unblindAndVerifyMessage: (blindedMessage: string) => Promise -} - -// The following interfaces should match https://github.com/celo-org/blind-threshold-bls-wasm/blob/master/src/blind_threshold_bls.d.ts - -interface ThresholdBlsLib { - blind: (message: Uint8Array, seed: Uint8Array) => BlindedMessage - unblind: (blindedSignature: Uint8Array, blindingFactor: Uint8Array) => Uint8Array - verify: (publicKey: Uint8Array, message: Uint8Array, signature: Uint8Array) => void // throws on failure -} - -interface BlindedMessage { - blindingFactor: Uint8Array - message: Uint8Array -} - -export class WasmBlsBlindingClient implements BlsBlindingClient { - private thresholdBls: ThresholdBlsLib - private odisPubKey: Uint8Array - private blindedValue: BlindedMessage | undefined - private rawMessage: Buffer | undefined - - constructor(odisPubKey: string) { - this.odisPubKey = Buffer.from(odisPubKey, 'base64') - // Dynamically load the Wasm library - // Checkout out documentation for alternative runtime environments: - // https://github.com/celo-org/identity/tree/ASv2/asv2#runtime-environments - if (this.isReactNativeEnvironment()) { - throw new Error('Cannot use WasmBlsBlindingClient in a React Native app') - } else if (this.isBrowserEnvironment()) { - throw new Error('Cannot use WasmBlsBlindingClient in a browser environment') - } else { - this.thresholdBls = require('blind-threshold-bls') - } - } - - async blindMessage(base64PhoneNumber: string, seed?: Buffer): Promise { - const userSeed = seed ?? randomBytes(32) - if (!seed) { - console.warn( - 'Warning: Use a private deterministic seed (e.g. DEK private key) to preserve user quota when requests are replayed.' - ) - } - this.rawMessage = Buffer.from(base64PhoneNumber, 'base64') - this.blindedValue = await this.thresholdBls.blind(this.rawMessage, userSeed) - const blindedMessage = this.blindedValue.message - return Buffer.from(blindedMessage).toString('base64') - } - - async unblindAndVerifyMessage(base64BlindSig: string): Promise { - if (!this.rawMessage || !this.blindedValue) { - throw new Error('Must call blind before unblinding') - } - - const blindedSignature = Buffer.from(base64BlindSig, 'base64') - const unblindMessage = await this.thresholdBls.unblind( - blindedSignature, - this.blindedValue.blindingFactor - ) - // this throws on error - await this.thresholdBls.verify(this.odisPubKey, this.rawMessage, unblindMessage) - return Buffer.from(unblindMessage).toString('base64') - } - - private isReactNativeEnvironment(): boolean { - return typeof navigator !== 'undefined' && navigator.product === 'ReactNative' - } - - // https://stackoverflow.com/questions/17575790/environment-detection-node-js-or-browser - // tslint:disable-next-line: function-constructor - private isBrowserEnvironment = new Function('try {return this===window;}catch(e){ return false;}') -} diff --git a/packages/sdk/identity/src/odis/circuit-breaker.mock.ts b/packages/sdk/identity/src/odis/circuit-breaker.mock.ts deleted file mode 100644 index cc08051b777..00000000000 --- a/packages/sdk/identity/src/odis/circuit-breaker.mock.ts +++ /dev/null @@ -1,139 +0,0 @@ -import * as crypto from 'crypto' -import debugFactory from 'debug' -import fetchMock from '../__mocks__/cross-fetch' -import { - BASE64_REGEXP, - CircuitBreakerEndpoints, - CircuitBreakerKeyStatus, - CircuitBreakerServiceContext, - CircuitBreakerStatusResponse, - CircuitBreakerUnwrapKeyRequest, - CircuitBreakerUnwrapKeyResponse, -} from './circuit-breaker' - -const debug = debugFactory('kit:identity:odis:circuit-breaker:mock') - -export const MOCK_CIRCUIT_BREAKER_PUBLIC_KEY = `-----BEGIN PUBLIC KEY----- -MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDGtxUPljt+oHFBf3RrDZHN9TbT -iI0kK4bv02Z2WP7kU/PQCikWqNl9/VjGVXuGMlfwpcWZrjWwJa+kBlUYRXH/inXW -UKO5PqTnaUXS1ALasGAUvzRz3VvzCxpjKsjVS8/gAoJbY2Imwor432OLrOssNoK7 -jbl1TgaV47yGCKwF9wIDAQAB ------END PUBLIC KEY-----` - -export const MOCK_CIRCUIT_BREAKER_PRIVATE_KEY = `-----BEGIN PRIVATE KEY----- -MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMa3FQ+WO36gcUF/ -dGsNkc31NtOIjSQrhu/TZnZY/uRT89AKKRao2X39WMZVe4YyV/ClxZmuNbAlr6QG -VRhFcf+KddZQo7k+pOdpRdLUAtqwYBS/NHPdW/MLGmMqyNVLz+ACgltjYibCivjf -Y4us6yw2gruNuXVOBpXjvIYIrAX3AgMBAAECgYBGPqv8QZAweAjxLVv7B+X112wV -JN033wcpOiKrTVR1ZFP4w864iuGvTuKV4dvzmVJK6F7Mr6+c4AWRxwdHuCzOlwxj -O9RySFAXhoENu70zg8W2w4i8GMHsmdnNk045cF01Mb3GtQ6Y3uGb637XYTIwMEbC -Q74TbkrfPZPcSIpPEQJBAP4VModTr47oNvdyJITQ3fzIarRSDU0deZTpn6MXB3a1 -abOAzlqYK3CSvLyyM9GOB9C5wvIZev+aNU9SkqPzU38CQQDINu7nOqS2X8UXQ5sS -wFrnoBQcU78i7Jaopvw0kOvkvklHlKVvXVkWP8PaWYdUAO9fpEdKdRnfaOEnqBwT -aymJAkEAgTXmbEtyjAoracryJ1jQiyyglvLjMMQ8gC4OsLGVahj3mAF47zlTXfxB -XvSAxaCk+NB/Av9SPYn+ckhbqmSjoQJAYb6H1bVIkoyg0OG9hGMKPkhlaQrtpmQw -jTewqw0RTQQlDGAigALnqjgJKsFIkxc9xciS0WPn9KzkNxMYWdaYWQJBAI8asXXb -XF5Lg2AAM2xJ/SS+h+si4f70eZey4vo9pWB3Q+VKbtRZu2pCjlR1A1nIqigJxdlc -1jHX+4GiW+t0w8Q= ------END PRIVATE KEY-----` - -const MOCK_CIRCUIT_BREAKER_ENVIRONMENT: CircuitBreakerServiceContext = { - url: 'https://mockcircuitbreaker.com/', - publicKey: MOCK_CIRCUIT_BREAKER_PUBLIC_KEY, -} - -/** - * Mock circuit breaker implementation based on Valora implementaion - * github.com/valora-inc/wallet/tree/main/packages/cloud-functions/src/circuitBreaker/circuitBreaker.ts - */ -export class MockCircuitBreaker { - static readonly publicKey = MOCK_CIRCUIT_BREAKER_PUBLIC_KEY - static readonly privateKey = MOCK_CIRCUIT_BREAKER_PRIVATE_KEY - static readonly environment = MOCK_CIRCUIT_BREAKER_ENVIRONMENT - - public keyStatus: CircuitBreakerKeyStatus = CircuitBreakerKeyStatus.ENABLED - - status(): { status: number; body: CircuitBreakerStatusResponse } { - return { - status: 200, - body: { status: this.keyStatus }, - } - } - - unwrapKey(req: CircuitBreakerUnwrapKeyRequest): { - status: number - body: CircuitBreakerUnwrapKeyResponse - } { - const { ciphertext } = req - if (!ciphertext) { - return { - status: 400, - body: { error: '"ciphertext" parameter must be provided' }, - } - } else if (!BASE64_REGEXP.test(ciphertext)) { - return { - status: 400, - body: { error: '"ciphertext" parameter must be a base64 encoded buffer' }, - } - } - - if (this.keyStatus !== CircuitBreakerKeyStatus.ENABLED) { - return { - status: 503, - body: { status: this.keyStatus }, - } - } - - let plaintext: Buffer - try { - plaintext = crypto.privateDecrypt( - // @ts-ignore support for OAEP hash option, was added in Node 12.9.0. - { key: MOCK_CIRCUIT_BREAKER_PRIVATE_KEY, oaepHash: 'sha256' }, - Buffer.from(ciphertext, 'base64') - ) - } catch (error) { - return { - status: 500, - body: { error: 'Error while decrypting ciphertext' }, - } - } - - return { - status: 200, - body: { plaintext: plaintext.toString('base64') }, - } - } - - installStatusEndpoint(mock: typeof fetchMock, override?: any) { - mock.mock( - { - url: new URL(CircuitBreakerEndpoints.STATUS, MockCircuitBreaker.environment.url).href, - method: 'GET', - }, - override ?? - ((url: string, req: unknown) => { - debug('Mocking request', { url, req }) - return this.status() - }) - ) - } - - installUnwrapKeyEndpoint(mock: typeof fetchMock, override?: any) { - mock.mock( - { - url: new URL(CircuitBreakerEndpoints.UNWRAP_KEY, MockCircuitBreaker.environment.url).href, - method: 'POST', - }, - override ?? - ((url: string, req: { body: string }) => { - debug('Mocking request', { url, req }) - return this.unwrapKey(JSON.parse(req.body) as CircuitBreakerUnwrapKeyRequest) - }) - ) - } - - install(mock: typeof fetchMock) { - this.installStatusEndpoint(mock) - this.installUnwrapKeyEndpoint(mock) - } -} diff --git a/packages/sdk/identity/src/odis/circuit-breaker.test.ts b/packages/sdk/identity/src/odis/circuit-breaker.test.ts deleted file mode 100644 index 11ef00efdbb..00000000000 --- a/packages/sdk/identity/src/odis/circuit-breaker.test.ts +++ /dev/null @@ -1,178 +0,0 @@ -import * as crypto from 'crypto' -import { - CircuitBreakerClient, - CircuitBreakerErrorTypes, - CircuitBreakerKeyStatus, -} from './circuit-breaker' -import { MockCircuitBreaker } from './circuit-breaker.mock' -import fetchMock from '../__mocks__/cross-fetch' - -describe('CircuitBreakerClient', () => { - const client = new CircuitBreakerClient(MockCircuitBreaker.environment) - let mockService: MockCircuitBreaker | undefined - - beforeEach(() => { - fetchMock.reset() - fetchMock.config.overwriteRoutes = true - - // Mock the circuit breaker service using the mock implementation defined above. - mockService = new MockCircuitBreaker() - mockService.install(fetchMock) - }) - - afterEach(() => { - fetchMock.reset() - }) - - describe('.status()', () => { - it('should fetch the current circuit breaker status', async () => { - for (const status of Object.values(CircuitBreakerKeyStatus)) { - mockService!.keyStatus = status - const result = await client.status() - expect(result.ok).toBe(true) - if (!result.ok) { - continue - } - expect(result.result).toEqual(status) - } - }) - - it('should return an error if fetch throws', async () => { - mockService!.installStatusEndpoint(fetchMock, { throws: new Error('fetch error') }) - const result = await client.status() - expect(result.ok).toBe(false) - if (result.ok) { - return - } - expect(result.error.errorType).toEqual(CircuitBreakerErrorTypes.FETCH_ERROR) - }) - - it('should return an error if the fetch returns an HTTP error status', async () => { - mockService!.installStatusEndpoint(fetchMock, { status: 501 }) - const result = await client.status() - expect(result.ok).toBe(false) - if (result.ok) { - return - } - expect(result.error.errorType).toEqual(CircuitBreakerErrorTypes.SERVICE_ERROR) - }) - - it('should return an error if fetch results in invalid json', async () => { - mockService!.installStatusEndpoint(fetchMock, { status: 200, body: '' }) - const result = await client.status() - expect(result.ok).toBe(false) - if (result.ok) { - return - } - expect(result.error.errorType).toEqual(CircuitBreakerErrorTypes.SERVICE_ERROR) - }) - - it('should return an error if fetch results in an invalid status', async () => { - mockService!.installStatusEndpoint(fetchMock, { - status: 200, - body: { status: 'invalid status' }, - }) - const result = await client.status() - expect(result.ok).toBe(false) - if (result.ok) { - return - } - expect(result.error.errorType).toEqual(CircuitBreakerErrorTypes.SERVICE_ERROR) - }) - }) - - describe('.wrapKey()', () => { - it('should return an encryption of the given plaintext', async () => { - const testData = 'test circuit breaker plaintext' - const ciphertext = client.wrapKey(Buffer.from(testData)) - expect(ciphertext.ok).toBe(true) - if (!ciphertext.ok) { - return - } - - const plaintext = crypto.privateDecrypt( - //@ts-ignore support for OAEP hash option, was added in Node 12.9.0. - { key: MockCircuitBreaker.privateKey, oaepHash: 'sha256' }, - ciphertext.result - ) - expect(plaintext.toString('utf8')).toEqual(testData) - }) - }) - - describe('.unwrapKey()', () => { - const testData = 'test circuit breaker plaintext' - const wrapResult = client.wrapKey(Buffer.from(testData)) - if (!wrapResult.ok) { - throw new Error('failed to produce test ciphertext for unwrapKey') - } - const testCiphertext = wrapResult.result - - it('should decrypt the given ciphertext when the service is enabled', async () => { - const result = await client.unwrapKey(testCiphertext) - expect(result.ok).toBe(true) - if (!result.ok) { - return - } - expect(result.result.toString('utf8')).toEqual(testData) - }) - - it('should return an error status response when the service is disabled', async () => { - for (const status of Object.values(CircuitBreakerKeyStatus)) { - if (status === CircuitBreakerKeyStatus.ENABLED) { - continue - } - - mockService!.keyStatus = status - const result = await client.unwrapKey(testCiphertext) - expect(result.ok).toBe(false) - if (result.ok) { - continue - } - expect(result.error.errorType).toEqual(CircuitBreakerErrorTypes.UNAVAILABLE_ERROR) - } - }) - - it('should return an error if fetch throws', async () => { - mockService!.installUnwrapKeyEndpoint(fetchMock, { throws: new Error('fetch error') }) - const result = await client.unwrapKey(testCiphertext) - expect(result.ok).toBe(false) - if (result.ok) { - return - } - expect(result.error.errorType).toEqual(CircuitBreakerErrorTypes.FETCH_ERROR) - }) - - it('should return an error if the fetch returns an HTTP error status', async () => { - mockService!.installUnwrapKeyEndpoint(fetchMock, { status: 501 }) - const result = await client.unwrapKey(testCiphertext) - expect(result.ok).toBe(false) - if (result.ok) { - return - } - expect(result.error.errorType).toEqual(CircuitBreakerErrorTypes.SERVICE_ERROR) - }) - - it('should return an error if fetch results in invalid json', async () => { - mockService!.installUnwrapKeyEndpoint(fetchMock, { status: 200, body: '' }) - const result = await client.unwrapKey(testCiphertext) - expect(result.ok).toBe(false) - if (result.ok) { - return - } - expect(result.error.errorType).toEqual(CircuitBreakerErrorTypes.SERVICE_ERROR) - }) - - it('should return an error if fetch results in an invalid plaintext', async () => { - mockService!.installUnwrapKeyEndpoint(fetchMock, { - status: 200, - body: { plaintext: '' }, - }) - const result = await client.unwrapKey(testCiphertext) - expect(result.ok).toBe(false) - if (result.ok) { - return - } - expect(result.error.errorType).toEqual(CircuitBreakerErrorTypes.SERVICE_ERROR) - }) - }) -}) diff --git a/packages/sdk/identity/src/odis/circuit-breaker.ts b/packages/sdk/identity/src/odis/circuit-breaker.ts deleted file mode 100644 index 58084724db1..00000000000 --- a/packages/sdk/identity/src/odis/circuit-breaker.ts +++ /dev/null @@ -1,254 +0,0 @@ -import { Err, Ok, Result, RootError } from '@celo/base/lib/result' -import fetch from 'cross-fetch' -import * as crypto from 'crypto' - -export const BASE64_REGEXP = /^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/ - -export interface CircuitBreakerServiceContext { - url: string - publicKey: string -} - -export const VALORA_ALFAJORES_CIRCUIT_BREAKER_ENVIRONMENT: CircuitBreakerServiceContext = { - url: 'https://us-central1-celo-mobile-alfajores.cloudfunctions.net/circuitBreaker/', - publicKey: `-----BEGIN PUBLIC KEY----- -MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAsYkNg3iY1ha4KGCGvHLl -mOMKV63lq+WsHIgUGfEuyfOWEBetVux9gvQEEPYpKbHgVQrfcegp28LoZYehWZHC -dIHSACcW0SGZagSOFEgxVSY6MgZZjmbTdlUtLac2cvxIDx8qhkoBjWRWu4g5LfdW -9QA0tiM3dR/pmA8YWcIYtyjGY1zglA/YqHClKsDRY+dbhshfILfohdFsVNJ3CWLS -J4yGvVe78AE/WiaXISV5ol+bqve4QlxzbBLIV4s44YONCh18/YhmGHCuSn8yy1/0 -q3YW7COaFEGd7m8VnV2rU/dFLKyF0XEanS6xk9ciL9uafR9dMryEQ7AW+yKmfQBG -H2i5uiKnWW2a3a873ShG2Qphl9mw1Kcrdxug4qk9y7RoKlMnG3Wdr4HMQb9S8KYf -07ZyVEbFip26ANWGo8dCA8fWvVtU5DByoWPI+PuglOB22z2noXov98imSFJfz9vu -yGAQt3CUOwUQvt+RObDXiHHIxJjU+6/81X3Jdnt3dFEfAgMBAAE= ------END PUBLIC KEY-----`, -} - -export const VALORA_MAINNET_CIRCUIT_BREAKER_ENVIRONMENT: CircuitBreakerServiceContext = { - url: 'https://us-central1-celo-mobile-mainnet.cloudfunctions.net/circuitBreaker/', - publicKey: `-----BEGIN PUBLIC KEY----- -MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEArQ89m/HIGECXR7ceZZRS -b6MZEw1S1o5qgi6sLEejBMUQhM/wgySoo5ydiW7S4iyiqEksQNAlOs5Mrv1aE9Ul -bG+rpglOA1xYLyjY7xUZE2tyPksPXcSKgu6d+G9gVtbmFld1Kr0jVx4qOLejtH3S -dGbX6g9GshgB1W4iEDZ4qEJBuvItSTudK3BFM1mBfEq1w3kDxNzYKC1zFlw+DWWh -BgIPB7zEp+MJNTwel2z7H02wsEMJMXzKwaAWaDp8PYfF3RwgCDIFkf+QteYIEUrG -C9bFhdYpDGY9Ldiz7kca9G9dvXWpZUQOYyOY7CFx0k2XcTBwx4Lq524lNR8waIDu -OT5jj2SIwXf5eKtyFMUqRNnqgs+IHHcWgh0CH7mfhPlFBMivKlwHgQqCJH3rHlgu -CMi3ENv4+p7+svshngntxGkEzZcLV3YVW7BG6xSOAqC1tjkM1PkmXENQOq+bxAL6 -bg3W6cTRQAQxoicu6+1c5Tdb/K36TXx0mHan7/Z8JCqfAgMBAAE= ------END PUBLIC KEY-----`, -} - -export enum CircuitBreakerKeyStatus { - ENABLED = 'ENABLED', - DISABLED = 'DISABLED', - DESTROYED = 'DESTROYED', - UNKNOWN = 'UNKNOWN', -} - -export enum CircuitBreakerEndpoints { - HEALTH = 'health', - STATUS = 'status', - UNWRAP_KEY = 'unwrap-key', -} - -export interface CircuitBreakerStatusResponse { - /** Status of the circuit breaker service */ - status: CircuitBreakerKeyStatus -} - -export interface CircuitBreakerUnwrapKeyRequest { - /** RSA-OAEP-256 encrypted data to be unwrapped by the circuit breaker. Encoded as base64 */ - ciphertext: string -} - -export interface CircuitBreakerUnwrapKeyResponse { - /** Decryption of the ciphertext provided to the circuit breaker service */ - plaintext?: string - - /** Error message indicating what went wrong if the ciphertext could not be decrypted */ - error?: string - - /** Status of the circuit breaker service. Included if the service is not enabled. */ - status?: CircuitBreakerKeyStatus -} - -export enum CircuitBreakerErrorTypes { - FETCH_ERROR = 'FETCH_ERROR', - SERVICE_ERROR = 'CIRCUIT_BREAKER_SERVICE_ERROR', - UNAVAILABLE_ERROR = 'CIRCUIT_BREAKER_UNAVAILABLE_ERROR', - ENCRYPTION_ERROR = 'ENCRYPTION_ERROR', -} - -export class CircuitBreakerServiceError extends RootError { - constructor(readonly status: number, readonly error?: Error) { - super(CircuitBreakerErrorTypes.SERVICE_ERROR) - } -} - -export class CircuitBreakerUnavailableError extends RootError { - constructor(readonly status: CircuitBreakerKeyStatus) { - super(CircuitBreakerErrorTypes.UNAVAILABLE_ERROR) - } -} - -export class EncryptionError extends RootError { - constructor(readonly error?: Error) { - super(CircuitBreakerErrorTypes.ENCRYPTION_ERROR) - } -} - -export class FetchError extends RootError { - constructor(readonly error?: Error) { - super(CircuitBreakerErrorTypes.FETCH_ERROR) - } -} - -export type CircuitBreakerError = - | CircuitBreakerServiceError - | CircuitBreakerUnavailableError - | EncryptionError - | FetchError - -/** - * Client for interacting with a circuit breaker service for encrypted cloud backups. - * - * @remarks A circuit breaker is a service supporting a public decryption function backed by an HSM - * key. If the need arises, the circuit breaker operator may take the decryption function offline. - * A client can encrypt data to the circuit breaker public key and store it in a non-public place. - * This data will then be available under normal circumstances, but become unavailable in the case - * of an emergency. - * - * It is intended for use in password-based key derivation when ODIS is used as a key hardening - * function. Clients may include in their key dervivation a random value which they encrypt to the - * circuit breaker public key. This allows the circuit breaker operator to disable key derivation, - * by restricting access to the encrypted keying material, in the event that ODIS is conpromised. - * This acts as a safety measure to allow wallet providers, or other users of ODIS key hardening, to - * prevent attackers from being able to brute force their users' derived keys in the event that - * ODIS is compromised such that it can no longer add to the key hardening. - * - * The circuit breaker service is designed for use in the encrypted cloud backup protocol. More - * information about encrypted cloud backup and the circuit breaker service can be found in the - * official {@link https://docs.celo.org/celo-codebase/protocol/identity/encrypted-cloud-backup | - * Celo documentation} - */ -export class CircuitBreakerClient { - constructor(readonly environment: CircuitBreakerServiceContext) {} - - protected url(endpoint: CircuitBreakerEndpoints): string { - // Note that if the result of this is an invalid URL, the URL constructor will throw. This is - // caught and reported as a fetch error, as a request could not be made. - return new URL(endpoint, this.environment.url).href - } - - /** - * Check the current status of the circuit breaker service. Result will reflect whether or not - * the circuit breaker keys are currently available. - */ - async status(): Promise> { - let response: Response - try { - response = await fetch(this.url(CircuitBreakerEndpoints.STATUS), { - method: 'GET', - headers: { - Accept: 'application/json', - }, - }) - } catch (error) { - return Err(new FetchError(error as Error)) - } - - let obj: any - try { - obj = await response.json() - } catch (error) { - return Err(new CircuitBreakerServiceError(response.status, error as Error)) - } - - // If the response was an error code, return an error to the user. - // We do not expect an error message to be included with the response from the status endpoint. - if (!response.ok) { - return Err(new CircuitBreakerServiceError(response.status)) - } - - if (!Object.values(CircuitBreakerKeyStatus).includes(obj.status)) { - return Err( - new CircuitBreakerServiceError( - response.status, - new Error(`circuit breaker service returned unexpected response: ${obj.status}`) - ) - ) - } - - return Ok(obj.status as CircuitBreakerKeyStatus) - } - - /** - * RSA-OAEP-256 Encrypt the provided key value against the public key of the circuit breaker. - * - * @remarks Note that this is an entirely local procedure and does not require interaction with - * the circuit breaker service. Encryption occurs only against the service public key. - */ - wrapKey(plaintext: Buffer): Result { - let ciphertext: Buffer - try { - ciphertext = crypto.publicEncrypt( - { - key: this.environment.publicKey, - oaepHash: 'sha256', - }, - plaintext - ) - } catch (error) { - return Err(new EncryptionError(error as Error)) - } - return Ok(ciphertext) - } - - /** Request the circuit breaker service to decrypt the provided encrypted key value */ - async unwrapKey(ciphertext: Buffer): Promise> { - const request: CircuitBreakerUnwrapKeyRequest = { - ciphertext: ciphertext.toString('base64'), - } - - let response: Response - try { - response = await fetch(this.url(CircuitBreakerEndpoints.UNWRAP_KEY), { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - }, - body: JSON.stringify(request), - }) - } catch (error) { - return Err(new FetchError(error as Error)) - } - - let obj: any - try { - obj = await response.json() - } catch (error) { - return Err(new CircuitBreakerServiceError(response.status, error as Error)) - } - - // If the response was an error code, return an error to the user after trying to parse the - // error from the service response. Either an error message or a status value may be returned. - if (!response.ok) { - if (obj.error !== undefined || obj.status === undefined) { - return Err(new CircuitBreakerServiceError(response.status, obj.error)) - } else { - return Err(new CircuitBreakerUnavailableError(obj.status)) - } - } - - const plaintext = obj.plaintext - if (plaintext === undefined || !BASE64_REGEXP.test(plaintext)) { - // Plaintext value is not returned in the error as it may has sensitive information. - const error = new Error('circuit breaker returned invalid plaintext response') - return Err(new CircuitBreakerServiceError(response.status, error)) - } - - return Ok(Buffer.from(plaintext, 'base64')) - } -} diff --git a/packages/sdk/identity/src/odis/identifier-backwards-compatibility.test.ts b/packages/sdk/identity/src/odis/identifier-backwards-compatibility.test.ts deleted file mode 100644 index 5e7d4f455ef..00000000000 --- a/packages/sdk/identity/src/odis/identifier-backwards-compatibility.test.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { getPhoneHash } from '@celo/base' -import { soliditySha3 } from '@celo/utils/lib/solidity' -import { OdisUtils as OdisUtilsOld } from 'old-identity-sdk' -import { OdisUtils } from '../../lib' -import { WasmBlsBlindingClient } from './bls-blinding-client' -import { AuthenticationMethod, AuthSigner, getServiceContext, OdisContextName } from './query' -import fetchMock from '../__mocks__/cross-fetch' - -const { getBlindedIdentifier, getIdentifierHash, getObfuscatedIdentifier, IdentifierPrefix } = - OdisUtils.Identifier - -const mockE164Number = '+14155550000' -const mockAccount = '0x755dB5fF7B82e9a96e0dDDD143293dc2ADeC0050' -// const mockPrivateKey = '0x2cacaf965ae80da49d5b1fc4b4c9b08ffc35971a584aedcc1cb8322b9d5fd9c9' - -// this DEK has been registered to the above account on alfajores -const dekPrivateKey = '0xc2bbdabb440141efed205497a41d5fb6114e0435fd541e368dc628a8e086bfee' - -const authSigner: AuthSigner = { - authenticationMethod: AuthenticationMethod.ENCRYPTION_KEY, - rawKey: dekPrivateKey, -} -const oldServiceContext = OdisUtilsOld.Query.getServiceContext('alfajores') -const currentServiceContext = getServiceContext(OdisContextName.ALFAJORES) - -const expectedObfuscatedIdentifier = - '0xf82c6272fd57d3e5d4e291be16b3ebac5c616084a5e6f3730c73f62efd39c6ae' -const expectedPepper = 'Pi4Z1NQnfsdvJ' - -describe('backwards compatibility of phone number identifiers', () => { - beforeAll(() => { - fetchMock.reset() - // disables the mock, lets all calls fall through to the actual network - fetchMock.spy() - }) - - it('should match when using EncryptionSigner', async () => { - const oldRes = await OdisUtilsOld.PhoneNumberIdentifier.getPhoneNumberIdentifier( - mockE164Number, - mockAccount, - authSigner, - oldServiceContext - ) - - const currRes = await getObfuscatedIdentifier( - mockE164Number, - IdentifierPrefix.PHONE_NUMBER, - mockAccount, - authSigner, - currentServiceContext - ) - - expect(oldRes.e164Number).toEqual(currRes.plaintextIdentifier) - expect(oldRes.phoneHash).toEqual(expectedObfuscatedIdentifier) - expect(currRes.obfuscatedIdentifier).toEqual(expectedObfuscatedIdentifier) - expect(oldRes.pepper).toEqual(expectedPepper) - expect(currRes.pepper).toEqual(expectedPepper) - }, 20000) - - it('blinded identifier should match', async () => { - const blsBlindingClient = new WasmBlsBlindingClient('') - const seed = Buffer.from( - '44714c0a2b2bacec757a67822a4fbbdfe043cca8c6ae936545ef992f246df1a9', - 'hex' - ) - const oldRes = await OdisUtilsOld.PhoneNumberIdentifier.getBlindedPhoneNumber( - mockE164Number, - blsBlindingClient, - seed - ) - const currentRes = await getBlindedIdentifier( - mockE164Number, - IdentifierPrefix.PHONE_NUMBER, - blsBlindingClient, - seed - ) - - const expectedBlindedIdentifier = - 'fuN6SmbxkYBqVbKZu4SizdyDjavNLK/XguIlwsWUhsWA0hQDoZtsZjQCbXqTnUiA' - - expect(oldRes).toEqual(expectedBlindedIdentifier) - expect(currentRes).toEqual(expectedBlindedIdentifier) - }) - - it('obfuscated identifier should match', async () => { - const sha3 = (v: string) => soliditySha3({ type: 'string', value: v }) - const oldRes = getPhoneHash(sha3, mockE164Number, expectedPepper) - - const currRes = getIdentifierHash(mockE164Number, IdentifierPrefix.PHONE_NUMBER, expectedPepper) - - expect(oldRes).toEqual(expectedObfuscatedIdentifier) - expect(currRes).toEqual(expectedObfuscatedIdentifier) - }) - - it('should not match when different prefix used', async () => { - const oldRes = await OdisUtilsOld.PhoneNumberIdentifier.getPhoneNumberIdentifier( - mockE164Number, - mockAccount, - authSigner, - oldServiceContext - ) - - const currRes = await getObfuscatedIdentifier( - mockE164Number, - '' as typeof IdentifierPrefix.PHONE_NUMBER, - mockAccount, - authSigner, - currentServiceContext - ) - - expect(oldRes.e164Number).toEqual(currRes.plaintextIdentifier) - expect(oldRes.phoneHash).not.toEqual(currRes.obfuscatedIdentifier) - expect(oldRes.pepper).not.toEqual(currRes.pepper) - }) -}) diff --git a/packages/sdk/identity/src/odis/identifier.test.ts b/packages/sdk/identity/src/odis/identifier.test.ts deleted file mode 100644 index 16ea0ce6575..00000000000 --- a/packages/sdk/identity/src/odis/identifier.test.ts +++ /dev/null @@ -1,157 +0,0 @@ -import { CombinerEndpoint } from '@celo/phone-number-privacy-common' -import { WasmBlsBlindingClient } from './bls-blinding-client' -import { - getBlindedIdentifier, - getBlindedIdentifierSignature, - getObfuscatedIdentifier, - getObfuscatedIdentifierFromSignature, - getPepperFromThresholdSignature, - IdentifierPrefix, -} from './identifier' -import { AuthenticationMethod, EncryptionKeySigner, ErrorMessages, ServiceContext } from './query' -import fetchMock from '../__mocks__/cross-fetch' - -jest.mock('./bls-blinding-client', () => { - // tslint:disable-next-line:no-shadowed-variable - class WasmBlsBlindingClient { - blindMessage = (m: string) => m - unblindAndVerifyMessage = (m: string) => m - } - return { - WasmBlsBlindingClient, - } -}) - -const mockOffchainIdentifier = 'twitterHandle' -const mockAccount = '0x0000000000000000000000000000000000007E57' -const expectedIdentifierHash = '0x8d1f580d4e49568883df9092285c0f8336e50d592b944607a613aff804e0b48f' -const expectedPepper = 'nHIvMC9B4j2+H' - -const serviceContext: ServiceContext = { - odisUrl: 'https://mockodis.com', - odisPubKey: - '7FsWGsFnmVvRfMDpzz95Np76wf/1sPaK0Og9yiB+P8QbjiC8FV67NBans9hzZEkBaQMhiapzgMR6CkZIZPvgwQboAxl65JWRZecGe5V3XO4sdKeNemdAZ2TzQuWkuZoA', -} -const endpoint = serviceContext.odisUrl + CombinerEndpoint.PNP_SIGN -const rawKey = '41e8e8593108eeedcbded883b8af34d2f028710355c57f4c10a056b72486aa04' - -const authSigner: EncryptionKeySigner = { - authenticationMethod: AuthenticationMethod.ENCRYPTION_KEY, - rawKey, -} - -describe(getObfuscatedIdentifier, () => { - afterEach(() => { - fetchMock.reset() - }) - - describe('Retrieves a pepper correctly', () => { - it('Using EncryptionKeySigner', async () => { - fetchMock.mock(endpoint, { - success: true, - signature: '0Uj+qoAu7ASMVvm6hvcUGx2eO/cmNdyEgGn0mSoZH8/dujrC1++SZ1N6IP6v2I8A', - performedQueryCount: 5, - totalQuota: 10, - version: '', - }) - - const blsBlindingClient = new WasmBlsBlindingClient(serviceContext.odisPubKey) - const base64BlindedMessage = await getBlindedIdentifier( - mockOffchainIdentifier, - IdentifierPrefix.TWITTER, - blsBlindingClient - ) - const base64BlindSig = await getBlindedIdentifierSignature( - mockAccount, - authSigner, - serviceContext, - base64BlindedMessage - ) - const base64UnblindedSig = await blsBlindingClient.unblindAndVerifyMessage(base64BlindSig) - - await expect( - getObfuscatedIdentifier( - mockOffchainIdentifier, - IdentifierPrefix.TWITTER, - mockAccount, - authSigner, - serviceContext - ) - ).resolves.toMatchObject({ - plaintextIdentifier: mockOffchainIdentifier, - pepper: expectedPepper, - obfuscatedIdentifier: expectedIdentifierHash, - unblindedSignature: base64UnblindedSig, - }) - }) - - it('Preblinding the off-chain identifier', async () => { - fetchMock.mock(endpoint, { - success: true, - signature: '0Uj+qoAu7ASMVvm6hvcUGx2eO/cmNdyEgGn0mSoZH8/dujrC1++SZ1N6IP6v2I8A', - performedQueryCount: 5, - totalQuota: 10, - version: '', - }) - - const blsBlindingClient = new WasmBlsBlindingClient(serviceContext.odisPubKey) - const base64BlindedMessage = await getBlindedIdentifier( - mockOffchainIdentifier, - IdentifierPrefix.TWITTER, - blsBlindingClient - ) - - const base64BlindSig = await getBlindedIdentifierSignature( - mockAccount, - authSigner, - serviceContext, - base64BlindedMessage - ) - - const obfuscatedIdentifierDetails = await getObfuscatedIdentifierFromSignature( - mockOffchainIdentifier, - IdentifierPrefix.TWITTER, - base64BlindSig, - blsBlindingClient - ) - - expect(obfuscatedIdentifierDetails.obfuscatedIdentifier).toEqual(expectedIdentifierHash) - expect(obfuscatedIdentifierDetails.pepper).toEqual(expectedPepper) - }) - }) - - it('Throws quota error', async () => { - fetchMock.mock(endpoint, 403) - - await expect( - getObfuscatedIdentifier( - mockOffchainIdentifier, - IdentifierPrefix.TWITTER, - mockAccount, - authSigner, - serviceContext - ) - ).rejects.toThrow(ErrorMessages.ODIS_QUOTA_ERROR) - }) - - it('Throws auth error', async () => { - fetchMock.mock(endpoint, 401) - await expect( - getObfuscatedIdentifier( - mockOffchainIdentifier, - IdentifierPrefix.TWITTER, - mockAccount, - authSigner, - serviceContext - ) - ).rejects.toThrow(ErrorMessages.ODIS_AUTH_ERROR) - }) -}) - -describe(getPepperFromThresholdSignature, () => { - it('Hashes sigs correctly', () => { - const base64Sig = 'vJeFZJ3MY5KlpI9+kIIozKkZSR4cMymLPh2GHZUatWIiiLILyOcTiw2uqK/LBReA' - const signature = Buffer.from(base64Sig, 'base64') - expect(getPepperFromThresholdSignature(signature)).toBe('piWqRHHYWtfg9') - }) -}) diff --git a/packages/sdk/identity/src/odis/identifier.ts b/packages/sdk/identity/src/odis/identifier.ts deleted file mode 100644 index ac8be6e91a1..00000000000 --- a/packages/sdk/identity/src/odis/identifier.ts +++ /dev/null @@ -1,304 +0,0 @@ -import { - getIdentifierHash as baseGetIdentifierHash, - getPrefixedIdentifier, - IdentifierPrefix, - isE164Number, -} from '@celo/base' -import { - CombinerEndpointPNP, - KEY_VERSION_HEADER, - SignMessageRequest, - SignMessageResponseSchema, -} from '@celo/phone-number-privacy-common' -import { soliditySha3 } from '@celo/utils/lib/solidity' -import { createHash } from 'crypto' -import debugFactory from 'debug' -import { BlsBlindingClient, WasmBlsBlindingClient } from './bls-blinding-client' -import { - AuthenticationMethod, - AuthSigner, - EncryptionKeySigner, - getOdisPnpRequestAuth, - queryOdis, - ServiceContext, -} from './query' - -const debug = debugFactory('kit:odis:identifier') -const sha3 = (v: string) => soliditySha3({ type: 'string', value: v }) - -const PEPPER_CHAR_LENGTH = 13 - -// Docstring is duplicated in @celo/base; make sure to update in both places. -/** - * Standardized prefixes for ODIS identifiers. - * - * @remarks These prefixes prevent collisions between off-chain identifiers. - * i.e. if a user's instagram and twitter handles are the same, - * these prefixes prevent the ODIS identifers from being the same. - * - * If you would like to use a prefix that isn't included, please put up a PR - * adding it to @celo/base (in celo-monorepo/packages/sdk/base/src/identifier.ts) - * to ensure interoperability with other projects. When adding new prefixes, - * please use either the full platform name in all lowercase (e.g. 'facebook') - * or DID methods https://w3c.github.io/did-spec-registries/#did-methods. - * Make sure to add the expected value for the unit test case in - * `celo-monorepo/packages/sdk/base/src/identifier.test.ts`, - * otherwise the test will fail. - * - * The NULL prefix is included to allow projects to use the sdk without selecting - * a predefined prefix or adding their own. Production use of the NULL prefix is - * discouraged since identifiers will not be interoperable with other projects. - * Please think carefully before using the NULL prefix. - */ -export { IdentifierPrefix } -// Docstring is duplicated in @celo/base; make sure to update in both places. -/** - * Concatenates the identifierPrefix and plaintextIdentifier with the separator '://' - * - * @param plaintextIdentifier Off-chain identifier, ex: phone number, twitter handle, email, etc. - * @param identifierPrefix Standardized prefix used to prevent collisions between identifiers - */ -export { getPrefixedIdentifier } - -/** - * Steps from the private plaintext identifier to the obfuscated identifier, which can be made public. - * - * plaintext identifier: off-chain information, ex: phone number, twitter handle, email, etc. - * blinded identifier: obtained by blinding the plaintext identifier - * blinded signature: blinded identifier signed by ODIS - * unblinded signatue: obtained by unblinding the blinded signature - * pepper: unique secret, obtained by hashing the unblinded signature - * obfuscated identifier: identifier used for on-chain attestations, obtained by hashing the plaintext identifier and pepper - */ - -export interface IdentifierHashDetails { - // plaintext off-chain phone number, twitter handle, email, etc. - plaintextIdentifier: string - // identifier obtained after hashing, used for on-chain attestations - obfuscatedIdentifier: string - // unique pepper obtained through ODIS - pepper: string - // raw signature from ODIS - unblindedSignature?: string -} - -/** - * Retrieve the obfuscated identifier for the provided plaintext identifier - * Performs blinding, querying, and unblinding - * - * @remarks This function will send a request to ODIS, authorized by the provided signer. - * This method consumes ODIS quota on the account provided by the signer. - * You can use the DEK as your signer to decrease quota usage - * - * @param plaintextIdentifier Off-chain identifier, ex: phone number, twitter handle, email, etc. - * @param identifierPrefix Standardized prefix used to prevent collisions between identifiers - * @param account The address making the request to ODIS, from which quota will be charged - * @param signer Object containing the private key used to authenticate the ODIS request - * @param context Specifies which ODIS combiner url should be queried (i.e. mainnet or alfajores) - * @param blindingFactor Optional Private seed used to blind identifers before they are sent to ODIS - * @param clientVersion Optional Specifies the client software version - * @param blsBlindingClient Optional Performs blinding and unblinding, defaults to WasmBlsBlindingClient - * @param sessionID Optional Used to track user sessions across the client and ODIS - * @param keyVersion Optional For testing. Specifies which version key ODIS should use - * @param endpoint Optional Allows client to specify the legacy endpoint if they desire (will be deprecated) - * @param abortController Optional Allows client to specify a timeout for the ODIS request - */ -export async function getObfuscatedIdentifier( - plaintextIdentifier: string, - identifierPrefix: IdentifierPrefix, - account: string, - signer: AuthSigner, - context: ServiceContext, - blindingFactor?: string, - clientVersion?: string, - blsBlindingClient?: BlsBlindingClient, - sessionID?: string, - keyVersion?: number, - endpoint?: CombinerEndpointPNP.LEGACY_PNP_SIGN | CombinerEndpointPNP.PNP_SIGN, - abortController?: AbortController -): Promise { - debug('Getting identifier pepper') - - let seed: Buffer | undefined - if (blindingFactor) { - seed = Buffer.from(blindingFactor) - } else if (signer.authenticationMethod === AuthenticationMethod.ENCRYPTION_KEY) { - seed = Buffer.from((signer as EncryptionKeySigner).rawKey) - } - - // Fallback to using Wasm version if not specified - if (!blsBlindingClient) { - debug('No BLSBlindingClient found, using WasmBlsBlindingClient') - blsBlindingClient = new WasmBlsBlindingClient(context.odisPubKey) - } - - const base64BlindedMessage = await getBlindedIdentifier( - plaintextIdentifier, - identifierPrefix, - blsBlindingClient, - seed - ) - - const base64BlindSig = await getBlindedIdentifierSignature( - account, - signer, - context, - base64BlindedMessage, - clientVersion, - sessionID, - keyVersion, - endpoint ?? CombinerEndpointPNP.PNP_SIGN, - abortController - ) - - return getObfuscatedIdentifierFromSignature( - plaintextIdentifier, - identifierPrefix, - base64BlindSig, - blsBlindingClient - ) -} - -/** - * Blinds the plaintext identifier in preparation for the ODIS request - * - * @remarks Caller should use the same blsBlindingClient instance for unblinding - * - * @param plaintextIdentifier Off-chain identifier, ex: phone number, twitter handle, email, etc. - * @param identifierPrefix Standardized prefix used to prevent collisions between identifiers - * @param blsBlindingClient Optional Performs blinding and unblinding, defaults to WasmBlsBlindingClient - * @param seed Optional Buffer generated from the blindingFactor, if provided - */ -export async function getBlindedIdentifier( - plaintextIdentifier: string, - identifierPrefix: IdentifierPrefix, - blsBlindingClient: BlsBlindingClient, - seed?: Buffer -): Promise { - debug('Retrieving blinded message') - // phone number identifiers don't have prefixes in the blinding stage - // to maintain backwards compatibility wih ASv1 - let identifier = getPrefixedIdentifier(plaintextIdentifier, identifierPrefix) - if (identifierPrefix === IdentifierPrefix.PHONE_NUMBER) { - if (!isE164Number(plaintextIdentifier)) { - throw new Error(`Invalid phone number: ${plaintextIdentifier}`) - } - identifier = plaintextIdentifier - } - return blsBlindingClient.blindMessage(Buffer.from(identifier).toString('base64'), seed) -} - -/** - * Query ODIS for the blinded signature - * - * @remarks - * Response can be passed into getObfuscatedIdentifierFromSignature - * to retrieve the obfuscated identifier - * - * @param account The address making the request to ODIS, from which quota will be charged - * @param signer Object containing the private key used to authenticate the ODIS request - * @param context Specifies which ODIS combiner url should be queried (i.e. mainnet or alfajores) - * @param base64BlindedMessage The blinded prefixed identifier to be sent to ODIS - * @param clientVersion Optional Specifies the client software version - * @param sessionID Optional Used to track user sessions across the client and ODIS - * @param keyVersion Optional For testing. Specifies which version key ODIS should use - * @param endpoint Optional Allows client to specify the legacy endpoint if they desire (will be deprecated) - * @param abortController Optional Allows client to specify a timeout for the ODIS request - */ -export async function getBlindedIdentifierSignature( - account: string, - signer: AuthSigner, - context: ServiceContext, - base64BlindedMessage: string, - clientVersion?: string, - sessionID?: string, - keyVersion?: number, - endpoint?: CombinerEndpointPNP.LEGACY_PNP_SIGN | CombinerEndpointPNP.PNP_SIGN, - abortControlller?: AbortController -): Promise { - const body: SignMessageRequest = { - account, - blindedQueryPhoneNumber: base64BlindedMessage, - version: clientVersion, - authenticationMethod: signer.authenticationMethod, - sessionID, - } - - const response = await queryOdis( - body, - context, - endpoint ?? CombinerEndpointPNP.PNP_SIGN, - SignMessageResponseSchema, - { - [KEY_VERSION_HEADER]: keyVersion?.toString(), - Authorization: await getOdisPnpRequestAuth(body, signer), - }, - abortControlller - ) - - if (!response.success) { - throw new Error(response.error) - } - - return response.signature -} - -/** - * Unblind the response and return the obfuscated identifier - * - * @param plaintextIdentifier Off-chain identifier, ex: phone number, twitter handle, email, etc. - * @param identifierPrefix Standardized prefix used to prevent collisions between identifiers - * @param base64BlindedSignature The blinded signed identifier returned by ODIS - * @param blsBlindingClient Optional Performs blinding and unblinding, defaults to WasmBlsBlindingClient - */ -export async function getObfuscatedIdentifierFromSignature( - plaintextIdentifier: string, - identifierPrefix: IdentifierPrefix, - base64BlindedSignature: string, - blsBlindingClient: BlsBlindingClient -): Promise { - debug('Retrieving unblinded signature') - const base64UnblindedSig = await blsBlindingClient.unblindAndVerifyMessage(base64BlindedSignature) - const sigBuf = Buffer.from(base64UnblindedSig, 'base64') - - debug('Converting sig to pepper') - const pepper = getPepperFromThresholdSignature(sigBuf) - const obfuscatedIdentifier = getIdentifierHash(plaintextIdentifier, identifierPrefix, pepper) - return { - plaintextIdentifier, - obfuscatedIdentifier, - pepper, - unblindedSignature: base64UnblindedSig, - } -} - -/** - * Generates final identifier that is published on-chain. - * - * @remarks - * Concatenates the plaintext prefixed identifier with the pepper derived by hashing the unblinded - * signature returned by ODIS. - * - * @param plaintextIdentifier Off-chain identifier, ex: phone number, twitter handle, email, etc. - * @param identifierPrefix Standardized prefix used to prevent collisions between identifiers - * @param pepper Hash of the unblinded signature returned by ODIS - */ -export const getIdentifierHash = ( - plaintextIdentifier: string, - identifierPrefix: IdentifierPrefix, - pepper: string -): string => { - return baseGetIdentifierHash(sha3, plaintextIdentifier, identifierPrefix, pepper) -} - -/** - * This is the algorithm that creates a pepper from the unblinded message signatures - * It simply hashes it with sha256 and encodes it to hex - * - * @remarks Currently uses 13 chars for a 78 bit pepper - * - * @param sigBuf Unblinded signature returned by ODIS - */ -export function getPepperFromThresholdSignature(sigBuf: Buffer) { - return createHash('sha256').update(sigBuf).digest('base64').slice(0, PEPPER_CHAR_LENGTH) -} diff --git a/packages/sdk/identity/src/odis/index.ts b/packages/sdk/identity/src/odis/index.ts deleted file mode 100644 index b4a93a2e0e8..00000000000 --- a/packages/sdk/identity/src/odis/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -import * as BlsBlindingClient from './bls-blinding-client' -import * as CircuitBreaker from './circuit-breaker' -import * as Identifier from './identifier' -import * as PhoneNumberIdentifier from './phone-number-identifier' -import * as Query from './query' -import * as Quota from './quota' - -export const OdisUtils = { - Identifier, - BlsBlindingClient, - Query, - PhoneNumberIdentifier, - CircuitBreaker, - Quota, -} diff --git a/packages/sdk/identity/src/odis/phone-number-identifier.test.ts b/packages/sdk/identity/src/odis/phone-number-identifier.test.ts deleted file mode 100644 index c3418ff0fef..00000000000 --- a/packages/sdk/identity/src/odis/phone-number-identifier.test.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { Endpoint } from '@celo/phone-number-privacy-common' -import { WasmBlsBlindingClient } from './bls-blinding-client' -import { - getBlindedPhoneNumber, - getBlindedPhoneNumberSignature, - getPhoneNumberIdentifier, - getPhoneNumberIdentifierFromSignature, - isBalanceSufficientForSigRetrieval, -} from './phone-number-identifier' -import { AuthenticationMethod, EncryptionKeySigner, ErrorMessages, ServiceContext } from './query' -import fetchMock from '../__mocks__/cross-fetch' - -jest.mock('./bls-blinding-client', () => { - // tslint:disable-next-line:no-shadowed-variable - class WasmBlsBlindingClient { - blindMessage = (m: string) => m - unblindAndVerifyMessage = (m: string) => m - } - return { - WasmBlsBlindingClient, - } -}) - -const mockE164Number = '+14155550000' -const mockAccount = '0x0000000000000000000000000000000000007E57' -const expectedPhoneHash = '0xf3ddadd1f488cdd42b9fa10354fdcae67c303ce182e71b30855733b50dce8301' -const expectedPepper = 'nHIvMC9B4j2+H' - -const serviceContext: ServiceContext = { - odisUrl: 'https://mockodis.com', - odisPubKey: - '7FsWGsFnmVvRfMDpzz95Np76wf/1sPaK0Og9yiB+P8QbjiC8FV67NBans9hzZEkBaQMhiapzgMR6CkZIZPvgwQboAxl65JWRZecGe5V3XO4sdKeNemdAZ2TzQuWkuZoA', -} -const endpoint = serviceContext.odisUrl + Endpoint.PNP_SIGN -const rawKey = '41e8e8593108eeedcbded883b8af34d2f028710355c57f4c10a056b72486aa04' - -const authSigner: EncryptionKeySigner = { - authenticationMethod: AuthenticationMethod.ENCRYPTION_KEY, - rawKey, -} - -describe(isBalanceSufficientForSigRetrieval, () => { - it('identifies sufficient balance correctly', () => { - expect(isBalanceSufficientForSigRetrieval(0.009, 0.004)).toBe(false) - expect(isBalanceSufficientForSigRetrieval(0.01, 0)).toBe(true) - expect(isBalanceSufficientForSigRetrieval(0, 0.005)).toBe(true) - }) -}) - -describe(getPhoneNumberIdentifier, () => { - afterEach(() => { - fetchMock.reset() - }) - - describe('Retrieves a pepper correctly', () => { - it('Using EncryptionKeySigner', async () => { - fetchMock.mock(endpoint, { - success: true, - signature: '0Uj+qoAu7ASMVvm6hvcUGx2eO/cmNdyEgGn0mSoZH8/dujrC1++SZ1N6IP6v2I8A', - performedQueryCount: 5, - totalQuota: 10, - version: '', - }) - - const blsBlindingClient = new WasmBlsBlindingClient(serviceContext.odisPubKey) - const base64BlindedMessage = await getBlindedPhoneNumber(mockE164Number, blsBlindingClient) - const base64BlindSig = await getBlindedPhoneNumberSignature( - mockAccount, - authSigner, - serviceContext, - base64BlindedMessage - ) - const base64UnblindedSig = await blsBlindingClient.unblindAndVerifyMessage(base64BlindSig) - - await expect( - getPhoneNumberIdentifier(mockE164Number, mockAccount, authSigner, serviceContext) - ).resolves.toMatchObject({ - e164Number: mockE164Number, - pepper: expectedPepper, - phoneHash: expectedPhoneHash, - unblindedSignature: base64UnblindedSig, - }) - }) - - it('Preblinding the phone number', async () => { - fetchMock.mock(endpoint, { - success: true, - signature: '0Uj+qoAu7ASMVvm6hvcUGx2eO/cmNdyEgGn0mSoZH8/dujrC1++SZ1N6IP6v2I8A', - performedQueryCount: 5, - totalQuota: 10, - version: '', - }) - - const blsBlindingClient = new WasmBlsBlindingClient(serviceContext.odisPubKey) - const base64BlindedMessage = await getBlindedPhoneNumber(mockE164Number, blsBlindingClient) - - const base64BlindSig = await getBlindedPhoneNumberSignature( - mockAccount, - authSigner, - serviceContext, - base64BlindedMessage - ) - - const phoneNumberHashDetails = await getPhoneNumberIdentifierFromSignature( - mockE164Number, - base64BlindSig, - blsBlindingClient - ) - - expect(phoneNumberHashDetails.phoneHash).toEqual(expectedPhoneHash) - expect(phoneNumberHashDetails.pepper).toEqual(expectedPepper) - }) - }) - - it('Throws quota error', async () => { - fetchMock.mock(endpoint, 403) - - await expect( - getPhoneNumberIdentifier(mockE164Number, mockAccount, authSigner, serviceContext) - ).rejects.toThrow(ErrorMessages.ODIS_QUOTA_ERROR) - }) - - it('Throws auth error', async () => { - fetchMock.mock(endpoint, 401) - await expect( - getPhoneNumberIdentifier(mockE164Number, mockAccount, authSigner, serviceContext) - ).rejects.toThrow(ErrorMessages.ODIS_AUTH_ERROR) - }) -}) diff --git a/packages/sdk/identity/src/odis/phone-number-identifier.ts b/packages/sdk/identity/src/odis/phone-number-identifier.ts deleted file mode 100644 index cc4871dbfa7..00000000000 --- a/packages/sdk/identity/src/odis/phone-number-identifier.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { CombinerEndpointPNP } from '@celo/phone-number-privacy-common' -import BigNumber from 'bignumber.js' -import debugFactory from 'debug' -import { BlsBlindingClient } from './bls-blinding-client' -import { - getBlindedIdentifier, - getBlindedIdentifierSignature, - getObfuscatedIdentifier, - getObfuscatedIdentifierFromSignature, - IdentifierPrefix, -} from './identifier' -import { AuthSigner, ServiceContext } from './query' - -// ODIS minimum dollar balance for sig retrieval -export const ODIS_MINIMUM_DOLLAR_BALANCE = 0.01 -// ODIS minimum celo balance for sig retrieval -export const ODIS_MINIMUM_CELO_BALANCE = 0.005 - -const debug = debugFactory('kit:odis:phone-number-identifier') - -export interface PhoneNumberHashDetails { - e164Number: string - phoneHash: string - pepper: string - unblindedSignature?: string -} - -/** - * Retrieve the on-chain identifier for the provided phone number - * Performs blinding, querying, and unblinding - * @deprecated use getObfuscatedIdentifier instead - */ -export async function getPhoneNumberIdentifier( - e164Number: string, - account: string, - signer: AuthSigner, - context: ServiceContext, - blindingFactor?: string, - clientVersion?: string, - blsBlindingClient?: BlsBlindingClient, - sessionID?: string, - keyVersion?: number, - endpoint?: CombinerEndpointPNP.LEGACY_PNP_SIGN | CombinerEndpointPNP.PNP_SIGN -): Promise { - debug('Getting phone number pepper') - - const { plaintextIdentifier, obfuscatedIdentifier, pepper, unblindedSignature } = - await getObfuscatedIdentifier( - e164Number, - IdentifierPrefix.PHONE_NUMBER, - account, - signer, - context, - blindingFactor, - clientVersion, - blsBlindingClient, - sessionID, - keyVersion, - endpoint - ) - return { - e164Number: plaintextIdentifier, - phoneHash: obfuscatedIdentifier, - pepper, - unblindedSignature, - } -} - -/** - * Blinds the phone number in preparation for the ODIS request - * Caller should use the same blsBlindingClient instance for unblinding - * @deprecated use getBlindedIdentifier instead - */ -export async function getBlindedPhoneNumber( - e164Number: string, - blsBlindingClient: BlsBlindingClient, - seed?: Buffer -): Promise { - return getBlindedIdentifier(e164Number, IdentifierPrefix.PHONE_NUMBER, blsBlindingClient, seed) -} - -/** - * Query ODIS for the blinded signature - * Response can be passed into getPhoneNumberIdentifierFromSignature - * to retrieve the on-chain identifier - * @deprecated use getBlindedIdentifierSignature instead - */ -export async function getBlindedPhoneNumberSignature( - account: string, - signer: AuthSigner, - context: ServiceContext, - base64BlindedMessage: string, - clientVersion?: string, - sessionID?: string, - keyVersion?: number, - endpoint?: CombinerEndpointPNP.LEGACY_PNP_SIGN | CombinerEndpointPNP.PNP_SIGN -): Promise { - return getBlindedIdentifierSignature( - account, - signer, - context, - base64BlindedMessage, - clientVersion, - sessionID, - keyVersion, - endpoint - ) -} - -/** - * Unblind the response and return the on-chain identifier - * @deprecated use getObfuscatedIdentifieriFromSignature instead - */ -export async function getPhoneNumberIdentifierFromSignature( - e164Number: string, - base64BlindedSignature: string, - blsBlindingClient: BlsBlindingClient -): Promise { - const { plaintextIdentifier, obfuscatedIdentifier, pepper, unblindedSignature } = - await getObfuscatedIdentifierFromSignature( - e164Number, - IdentifierPrefix.PHONE_NUMBER, - base64BlindedSignature, - blsBlindingClient - ) - return { - e164Number: plaintextIdentifier, - phoneHash: obfuscatedIdentifier, - pepper, - unblindedSignature, - } -} - -/** - * Check if balance is sufficient for quota retrieval - * @deprecated use getPnpQuotaStatus instead - */ -export function isBalanceSufficientForSigRetrieval( - dollarBalance: BigNumber.Value, - celoBalance: BigNumber.Value -) { - return ( - new BigNumber(dollarBalance).isGreaterThanOrEqualTo(ODIS_MINIMUM_DOLLAR_BALANCE) || - new BigNumber(celoBalance).isGreaterThanOrEqualTo(ODIS_MINIMUM_CELO_BALANCE) - ) -} diff --git a/packages/sdk/identity/src/odis/query.test.ts b/packages/sdk/identity/src/odis/query.test.ts deleted file mode 100644 index 77ba10f75a2..00000000000 --- a/packages/sdk/identity/src/odis/query.test.ts +++ /dev/null @@ -1,69 +0,0 @@ -import crypto from 'crypto' -import { hexToBuffer, trimLeading0x } from '../../../base/lib' -import { signWithRawKey } from './query' - -const rawKey = '41e8e8593108eeedcbded883b8af34d2f028710355c57f4c10a056b72486aa04' - -describe(signWithRawKey, () => { - it('Signs message digest', async () => { - const msg = `${'a'.repeat(64)} + ${'b'.repeat(16)}` - - // NOTE: Elliptic will truncate the raw msg to 64 bytes before signing, - // so make sure to always pass the hex encoded msgDigest instead. - const msgDigest = crypto.createHash('sha256').update(JSON.stringify(msg)).digest('hex') - - // NOTE: elliptic is disabled elsewhere in this library to prevent - // accidental signing of truncated messages. - // tslint:disable-next-line:import-blacklist - const EC = require('elliptic').ec - const ec = new EC('secp256k1') - - // Sign - const key = ec.keyFromPrivate(hexToBuffer(rawKey)) - const expectedSig = JSON.stringify(key.sign(msgDigest).toDER()) - const receivedSig = signWithRawKey(msg, rawKey) - const badSig = JSON.stringify(key.sign(msg).toDER()) - - // Verify - const pub = key.getPublic(true, 'hex') - const pubKey = ec.keyFromPublic(trimLeading0x(pub), 'hex') - const isValid = (input: string, sig: string) => pubKey.verify(input, JSON.parse(sig)) - expect(isValid(msgDigest, expectedSig)).toBeTruthy() - expect(isValid(msg, badSig)).toBeTruthy() - expect(isValid(msg, expectedSig)).toBeFalsy() - expect(isValid(msg, receivedSig)).toBeFalsy() - expect(isValid(msgDigest, receivedSig)).toBeTruthy() - expect(receivedSig).toEqual(expectedSig) - }) - - it('Signs full message', async () => { - const msg1 = `${'a'.repeat(64)} + ${'b'.repeat(16)}` - const msg2 = `${'a'.repeat(64)} + ${'c'.repeat(16)}` - - const sig1 = signWithRawKey(msg1, rawKey) - const sig2 = signWithRawKey(msg2, rawKey) - - // NOTE: Elliptic will truncate the raw msg to 64 bytes before signing, - // so make sure to always pass the hex encoded msgDigest instead. - const msgDigest1 = crypto.createHash('sha256').update(JSON.stringify(msg1)).digest('hex') - const msgDigest2 = crypto.createHash('sha256').update(JSON.stringify(msg2)).digest('hex') - - // NOTE: elliptic is disabled elsewhere in this library to prevent - // accidental signing of truncated messages. - // tslint:disable-next-line:import-blacklist - const EC = require('elliptic').ec - const ec = new EC('secp256k1') - - // Sign - const key = ec.keyFromPrivate(hexToBuffer(rawKey)) - - // Verify - const pub = key.getPublic(true, 'hex') - const pubKey = ec.keyFromPublic(trimLeading0x(pub), 'hex') - const isValid = (input: string, sig: string) => pubKey.verify(input, JSON.parse(sig)) - expect(isValid(msgDigest1, sig1)).toBeTruthy() - expect(isValid(msgDigest2, sig2)).toBeTruthy() - expect(isValid(msgDigest1, sig2)).toBeFalsy() - expect(isValid(msgDigest2, sig1)).toBeFalsy() - }) -}) diff --git a/packages/sdk/identity/src/odis/query.ts b/packages/sdk/identity/src/odis/query.ts deleted file mode 100644 index 8cbdf00e39c..00000000000 --- a/packages/sdk/identity/src/odis/query.ts +++ /dev/null @@ -1,245 +0,0 @@ -import { selectiveRetryAsyncWithBackOff } from '@celo/base/lib/async' -import { ContractKit } from '@celo/contractkit' -import { - AuthenticationMethod, - CombinerEndpoint, - DomainEndpoint, - DomainRequest, - DomainRequestHeader, - DomainResponse, - OdisRequest, - OdisRequestHeader, - OdisResponse, - PhoneNumberPrivacyRequest, - signWithRawKey, -} from '@celo/phone-number-privacy-common' -import fetch from 'cross-fetch' -import debugFactory from 'debug' -import { isLeft } from 'fp-ts/lib/Either' -import * as t from 'io-ts' - -const debug = debugFactory('kit:odis:query') - -export interface WalletKeySigner { - authenticationMethod: AuthenticationMethod.WALLET_KEY - contractKit: ContractKit -} - -export interface EncryptionKeySigner { - authenticationMethod: AuthenticationMethod.ENCRYPTION_KEY - rawKey: string -} - -// Support signing with the DEK or with the -export type AuthSigner = WalletKeySigner | EncryptionKeySigner - -// Re-export types and aliases to maintain backwards compatibility. -export { AuthenticationMethod, PhoneNumberPrivacyRequest, signWithRawKey } - -export enum ErrorMessages { - ODIS_QUOTA_ERROR = 'odisQuotaError', - ODIS_RATE_LIMIT_ERROR = 'odisRateLimitError', - ODIS_INPUT_ERROR = 'odisBadInputError', - ODIS_AUTH_ERROR = 'odisAuthError', - ODIS_CLIENT_ERROR = 'Unknown Client Error', - ODIS_FETCH_ERROR = 'odisFetchError', - ODIS_RESPONSE_ERROR = 'odisResponseError', -} - -export interface ServiceContext { - odisUrl: string // combiner url - odisPubKey: string -} - -export const ODIS_STAGING_CONTEXT: ServiceContext = { - odisUrl: 'https://us-central1-celo-phone-number-privacy-stg.cloudfunctions.net/combiner', - odisPubKey: - '7FsWGsFnmVvRfMDpzz95Np76wf/1sPaK0Og9yiB+P8QbjiC8FV67NBans9hzZEkBaQMhiapzgMR6CkZIZPvgwQboAxl65JWRZecGe5V3XO4sdKeNemdAZ2TzQuWkuZoA', -} - -export const ODIS_ALFAJORES_CONTEXT_PNP: ServiceContext = { - odisUrl: 'https://us-central1-celo-phone-number-privacy.cloudfunctions.net/combiner', - odisPubKey: - 'kPoRxWdEdZ/Nd3uQnp3FJFs54zuiS+ksqvOm9x8vY6KHPG8jrfqysvIRU0wtqYsBKA7SoAsICMBv8C/Fb2ZpDOqhSqvr/sZbZoHmQfvbqrzbtDIPvUIrHgRS0ydJCMsA', -} - -export const ODIS_ALFAJORES_CONTEXT_DOMAINS: ServiceContext = { - odisUrl: 'https://us-central1-celo-phone-number-privacy.cloudfunctions.net/combiner', - odisPubKey: - '+ZrxyPvLChWUX/DyPw6TuGwQH0glDJEbSrSxUARyP5PuqYyP/U4WZTV1e0bAUioBZ6QCJMiLpDwTaFvy8VnmM5RBbLQUMrMg5p4+CBCqj6HhsMfcyUj8V0LyuNdStlCB', -} - -export const ODIS_MAINNET_CONTEXT_PNP: ServiceContext = { - odisUrl: 'https://us-central1-celo-pgpnp-mainnet.cloudfunctions.net/combiner', - odisPubKey: - 'FvreHfLmhBjwxHxsxeyrcOLtSonC9j7K3WrS4QapYsQH6LdaDTaNGmnlQMfFY04Bp/K4wAvqQwO9/bqPVCKf8Ze8OZo8Frmog4JY4xAiwrsqOXxug11+htjEe1pj4uMA', -} - -export const ODIS_MAINNET_CONTEXT_DOMAINS: ServiceContext = { - odisUrl: 'https://us-central1-celo-pgpnp-mainnet.cloudfunctions.net/combiner', - odisPubKey: - 'LX4tLiuYm8geZ3ztmH7oIWz4ohXt3ePRTd9BbG9RO86NMrApflioiOzKYtIsyjEA0uarnX8Emo+luTY4bwEWpgZDyPYE6UMWAoBaZBdy6NDMgAxSbdNtaQEq51fBjCUA', -} - -export enum OdisAPI { - PNP = 'pnp', - DOMAIN = 'domain', -} - -export enum OdisContextName { - STAGING = 'alfajoresstaging', - ALFAJORES = 'alfajores', - MAINNET = 'mainnet', -} - -export function getServiceContext( - contextName: OdisContextName = OdisContextName.MAINNET, - api: OdisAPI = OdisAPI.PNP -) { - switch (contextName) { - case OdisContextName.ALFAJORES: - return { - [OdisAPI.PNP]: ODIS_ALFAJORES_CONTEXT_PNP, - [OdisAPI.DOMAIN]: ODIS_ALFAJORES_CONTEXT_DOMAINS, - }[api] - case OdisContextName.STAGING: - return { - // Intentionally the same on staging - [OdisAPI.PNP]: ODIS_STAGING_CONTEXT, - [OdisAPI.DOMAIN]: ODIS_STAGING_CONTEXT, - }[api] - case OdisContextName.MAINNET: - return { - [OdisAPI.PNP]: ODIS_MAINNET_CONTEXT_PNP, - [OdisAPI.DOMAIN]: ODIS_MAINNET_CONTEXT_DOMAINS, - }[api] - default: - return ODIS_MAINNET_CONTEXT_PNP - } -} - -export function signWithDEK(msg: string, signer: EncryptionKeySigner) { - return signWithRawKey(msg, signer.rawKey) -} - -export async function getOdisPnpRequestAuth( - body: PhoneNumberPrivacyRequest, - signer: AuthSigner -): Promise { - // Sign payload using provided account and authentication method. - const bodyString = JSON.stringify(body) - if (signer.authenticationMethod === AuthenticationMethod.ENCRYPTION_KEY) { - return signWithDEK(bodyString, signer as EncryptionKeySigner) - } - if (signer.authenticationMethod === AuthenticationMethod.WALLET_KEY) { - return signer.contractKit.connection.sign(bodyString, body.account) - } - throw new Error('AuthenticationMethod not supported') -} - -/** - * Send any OdisRequest to the specified CombinerEndpoint for the given ServiceContext - * - * @param body OdisRequest to send in the body of the HTTP request. - * @param context Contains service URL and public to determine which instance to contact. - * @param endpoint Endpoint to query - * @param responseSchema io-ts schema to ensure type safety of responses - * @param headers custom request headers corresponding to the type of OdisRequest (keyVersion, Authentication, etc.) - */ -export async function queryOdis( - body: R, - context: ServiceContext, - endpoint: CombinerEndpoint, - responseSchema: t.Type, OdisResponse, unknown>, - headers: OdisRequestHeader, - abortController?: AbortController -): Promise> { - debug(`Posting to ${endpoint}`) - - const dontRetry = [ - ErrorMessages.ODIS_QUOTA_ERROR, - ErrorMessages.ODIS_RATE_LIMIT_ERROR, - ErrorMessages.ODIS_AUTH_ERROR, - ErrorMessages.ODIS_INPUT_ERROR, - ErrorMessages.ODIS_CLIENT_ERROR, - ] - - return selectiveRetryAsyncWithBackOff( - async () => { - let res: Response - try { - res = await fetch(context.odisUrl + endpoint, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - ...headers, - }, - body: JSON.stringify(body), - signal: abortController?.signal, - }) - } catch (error) { - throw new Error(`${ErrorMessages.ODIS_FETCH_ERROR}: ${error}`) - } - - if (res.ok) { - debug('Response ok. Parsing.') - const response = await res.json() - - // Verify that the response is the type we expected, then return it. - const decoding = responseSchema.decode(response) - if (isLeft(decoding)) { - throw new Error(ErrorMessages.ODIS_RESPONSE_ERROR) - } - return decoding.right - } - - debug(`Response not okay. Status ${res.status}`) - - switch (res.status) { - case 403: - throw new Error(ErrorMessages.ODIS_QUOTA_ERROR) - case 429: - throw new Error(ErrorMessages.ODIS_RATE_LIMIT_ERROR) - case 400: - throw new Error(ErrorMessages.ODIS_INPUT_ERROR) - case 401: - throw new Error(ErrorMessages.ODIS_AUTH_ERROR) - default: - if (res.status >= 400 && res.status < 500) { - // Don't retry error codes in 400s - throw new Error(`${ErrorMessages.ODIS_CLIENT_ERROR} ${res.status}`) - } - throw new Error(`Unknown failure ${res.status}`) - } - }, - 3, - dontRetry, - [] - ) -} - -/** - * Send the given domain request to ODIS (e.g. to get a POPRF evaluation or check quota). - * - * @param body Request to send in the body of the HTTP request. - * @param context Contains service URL and public to determine which instance to contact. - * @param endpoint Endpoint to query (e.g. '/domain/sign', '/domain/quotaStatus'). - * @param responseSchema io-ts type for the expected response type. Provided to ensure type safety. - * @param headers optional header fields relevant to the given request type (keyVersion, Authentication, etc.) - */ -export async function sendOdisDomainRequest( - body: R, - context: ServiceContext, - endpoint: DomainEndpoint, - responseSchema: t.Type>, - headers?: DomainRequestHeader -): Promise> { - return queryOdis( - body, - context, - endpoint, - responseSchema, - headers as OdisRequestHeader - ) as Promise> -} diff --git a/packages/sdk/identity/src/odis/quota.test.ts b/packages/sdk/identity/src/odis/quota.test.ts deleted file mode 100644 index 1e568a19897..00000000000 --- a/packages/sdk/identity/src/odis/quota.test.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { AuthenticationMethod, CombinerEndpoint } from '@celo/phone-number-privacy-common' -import { EncryptionKeySigner, ServiceContext } from './query' -import { getPnpQuotaStatus, PnpClientQuotaStatus } from './quota' -import fetchMock from '../__mocks__/cross-fetch' - -const mockAccount = '0x0000000000000000000000000000000000007E57' -const serviceContext: ServiceContext = { - odisUrl: 'https://mockodis.com', - odisPubKey: - '7FsWGsFnmVvRfMDpzz95Np76wf/1sPaK0Og9yiB+P8QbjiC8FV67NBans9hzZEkBaQMhiapzgMR6CkZIZPvgwQboAxl65JWRZecGe5V3XO4sdKeNemdAZ2TzQuWkuZoA', -} -const endpoint = serviceContext.odisUrl + CombinerEndpoint.PNP_QUOTA -const rawKey = '41e8e8593108eeedcbded883b8af34d2f028710355c57f4c10a056b72486aa04' - -const authSigner: EncryptionKeySigner = { - authenticationMethod: AuthenticationMethod.ENCRYPTION_KEY, - rawKey, -} - -describe(getPnpQuotaStatus, () => { - afterEach(() => { - fetchMock.reset() - }) - it('returns the correct remaining quota amount', async () => { - const performedQueryCount = 5 - const totalQuota = 10 - const version = '' - fetchMock.mock(endpoint, { - success: true, - totalQuota, - performedQueryCount, - version, - }) - - await expect( - getPnpQuotaStatus(mockAccount, authSigner, serviceContext) - ).resolves.toStrictEqual({ - performedQueryCount, - totalQuota, - remainingQuota: totalQuota - performedQueryCount, - version, - warnings: undefined, - blockNumber: undefined, - }) - }) - - it('throws quota error on failure response', async () => { - fetchMock.mock(endpoint, { - success: false, - version: '', - }) - - await expect(getPnpQuotaStatus(mockAccount, authSigner, serviceContext)).rejects.toThrow() - }) -}) diff --git a/packages/sdk/identity/src/odis/quota.ts b/packages/sdk/identity/src/odis/quota.ts deleted file mode 100644 index 95fd85ff1eb..00000000000 --- a/packages/sdk/identity/src/odis/quota.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { Address } from '@celo/base' -import { - CombinerEndpoint, - PnpQuotaRequest, - PnpQuotaResponseSchema, -} from '@celo/phone-number-privacy-common' -import { AuthSigner, getOdisPnpRequestAuth, queryOdis, ServiceContext } from './query' - -export interface PnpClientQuotaStatus { - version: string - performedQueryCount: number - totalQuota: number - remainingQuota: number - blockNumber?: number - warnings?: string[] -} - -/** - * Query the ODIS quota status of a given account - * - * @param account The address whose ODIS quota we are querying - * @param signer Object containing the private key used to authenticate the ODIS request - * @param context Specifies which ODIS combiner url should be queried (i.e. mainnet or alfajores) - * @param clientVersion Optional Specifies the client software version - * @param sessionID Optional Used to track user sessions across the client and ODIS - * @param abortController Optional Allows client to specify a timeout for the ODIS request - */ -export async function getPnpQuotaStatus( - account: Address, - signer: AuthSigner, - context: ServiceContext, - clientVersion?: string, - sessionID?: string, - abortController?: AbortController -): Promise { - const body: PnpQuotaRequest = { - account, - version: clientVersion, - authenticationMethod: signer.authenticationMethod, - sessionID, - } - - const response = await queryOdis( - body, - context, - CombinerEndpoint.PNP_QUOTA, - PnpQuotaResponseSchema, - { - Authorization: await getOdisPnpRequestAuth(body, signer), - }, - abortController - ) - - if (response.success) { - return { - version: response.version, - performedQueryCount: response.performedQueryCount, - totalQuota: response.totalQuota, - remainingQuota: response.totalQuota - response.performedQueryCount, - warnings: response.warnings, - blockNumber: response.blockNumber, - } - } - - throw new Error(response.error) -} diff --git a/packages/sdk/identity/src/offchain-data-wrapper.test.ts b/packages/sdk/identity/src/offchain-data-wrapper.test.ts deleted file mode 100644 index aa26010c45b..00000000000 --- a/packages/sdk/identity/src/offchain-data-wrapper.test.ts +++ /dev/null @@ -1,376 +0,0 @@ -import { Result } from '@celo/base' -import { ContractKit, newKitFromWeb3 } from '@celo/contractkit' -import { createStorageClaim } from '@celo/contractkit/lib/identity/claims/claim' -import { IdentityMetadataWrapper } from '@celo/contractkit/lib/identity/metadata' -import { AccountsWrapper } from '@celo/contractkit/lib/wrappers/Accounts' -import { ACCOUNT_PRIVATE_KEYS } from '@celo/dev-utils/lib/ganache-setup' -import { testWithGanache } from '@celo/dev-utils/lib/ganache-test' -import { - ensureLeading0x, - privateKeyToAddress, - privateKeyToPublicKey, - publicKeyToAddress, - toChecksumAddress, -} from '@celo/utils/lib/address' -import { ensureCompressed } from '@celo/utils/lib/ecdh' -import { NativeSigner, serializeSignature } from '@celo/utils/lib/signatureUtils' -import { LocalWallet } from '@celo/wallet-local' -import { randomBytes } from 'crypto' -import { BasicDataWrapper, OffchainDataWrapper, OffchainErrorTypes } from './offchain-data-wrapper' -import { AuthorizedSignerAccessor } from './offchain/accessors/authorized-signer' -import { SchemaErrors, SchemaErrorTypes } from './offchain/accessors/errors' -import { PrivateNameAccessor, PublicNameAccessor } from './offchain/accessors/name' -import { MockStorageWriter } from './offchain/storage-writers' -import fetchMock from './__mocks__/cross-fetch' - -const testname = 'test' -const testPayload = { name: testname } - -interface RegisteredAccount { - wrapper: OffchainDataWrapper - privateKey: string - publicKey: string - address: string - storageRoot: string - localStorageRoot: string - kit: ContractKit -} - -testWithGanache('Offchain Data', (web3) => { - const kit = newKitFromWeb3(web3, new LocalWallet()) - - const writerPrivate = ACCOUNT_PRIVATE_KEYS[0] - const readerPrivate = ACCOUNT_PRIVATE_KEYS[1] - const reader2Private = ACCOUNT_PRIVATE_KEYS[2] - const signerPrivate = ACCOUNT_PRIVATE_KEYS[3] - - const writerEncryptionKeyPrivate = ensureLeading0x(randomBytes(32).toString('hex')) - const readerEncryptionKeyPrivate = ensureLeading0x(randomBytes(32).toString('hex')) - const reader2EncryptionKeyPrivate = ensureLeading0x(randomBytes(32).toString('hex')) - - let accounts: AccountsWrapper - let writer: RegisteredAccount - let reader: RegisteredAccount - let reader2: RegisteredAccount - let signer: RegisteredAccount - - async function setupAccount( - privateKey: string, - dek?: string, - compressedDEK = false - ): Promise { - const publicKey = privateKeyToPublicKey(privateKey) - const address = publicKeyToAddress(publicKey) - const metadataURL = `http://example.com/${address}/metadata` - const storageRoot = `http://example.com/${address}/root` - const localStorageRoot = `/tmp/offchain/${address}` - - accounts = await kit.contracts.getAccounts() - await accounts.createAccount().sendAndWaitForReceipt({ from: address }) - - if (dek) { - await accounts - .setAccountDataEncryptionKey( - compressedDEK - ? ensureLeading0x(ensureCompressed(privateKeyToPublicKey(dek))) - : privateKeyToPublicKey(dek) - ) - .sendAndWaitForReceipt({ from: address }) - kit.connection.addAccount(dek) - } - - const metadata = IdentityMetadataWrapper.fromEmpty(address) - await metadata.addClaim(createStorageClaim(storageRoot), NativeSigner(web3.eth.sign, address)) - - fetchMock.mock(metadataURL, metadata.toString()) - await accounts.setMetadataURL(metadataURL).sendAndWaitForReceipt({ from: address }) - - kit.connection.addAccount(privateKey) - - const wrapper = new BasicDataWrapper(address, kit) - wrapper.storageWriter = new MockStorageWriter(localStorageRoot, storageRoot, fetchMock) - - return { wrapper, privateKey, publicKey, address, storageRoot, localStorageRoot, kit } - } - - beforeEach(async () => { - writer = await setupAccount(writerPrivate, writerEncryptionKeyPrivate) - reader = await setupAccount(readerPrivate, readerEncryptionKeyPrivate) - reader2 = await setupAccount(reader2Private, reader2EncryptionKeyPrivate) - signer = await setupAccount(signerPrivate) - }) - - afterEach(() => { - fetchMock.reset() - writer.kit.getWallet()!.removeAccount(writer.address) - reader.kit.getWallet()!.removeAccount(reader.address) - reader2.kit.getWallet()!.removeAccount(reader2.address) - signer.kit.getWallet()!.removeAccount(signer.address) - }) - - const assertValidNameResponse = (resp: Result<{ name: string }, SchemaErrors>) => { - if (resp.ok) { - expect(resp.result.name).toEqual(testname) - } else { - const error = resp.error - switch (error.errorType) { - case SchemaErrorTypes.InvalidDataError: - console.log("Something was wrong with the schema, can't try again") - break - case SchemaErrorTypes.OffchainError: - const offchainError = error.error - switch (offchainError.errorType) { - case OffchainErrorTypes.FetchError: - console.log('Something went wrong with fetching, try again') - break - case OffchainErrorTypes.InvalidSignature: - console.log('Signature was wrong') - break - case OffchainErrorTypes.NoStorageRootProvidedData: - console.log("Account doesn't have data for this type") - break - } - - default: - break - } - throw new Error(error.message) - } - } - - describe('with the account being the signer', () => { - it('can write a name', async () => { - const nameAccessor = new PublicNameAccessor(writer.wrapper) - await nameAccessor.write(testPayload) - - const readerNameAccessor = new PublicNameAccessor(reader.wrapper) - const resp = await readerNameAccessor.readAsResult(writer.address) - assertValidNameResponse(resp) - }) - }) - - describe('with the DEK being the signer', () => { - it('can write a name', async () => { - const writerPrivateKey = ACCOUNT_PRIVATE_KEYS[5] - const writerDEK = randomBytes(32).toString('hex') - const compressedWriter = await setupAccount(writerPrivateKey, writerDEK, true) - const DEKAddress = privateKeyToAddress(writerDEK) - compressedWriter.wrapper.kit.connection.addAccount(writerDEK) - compressedWriter.wrapper.signer = DEKAddress - - const nameAccessor = new PublicNameAccessor(compressedWriter.wrapper) - await nameAccessor.write(testPayload) - - const readerNameAccessor = new PublicNameAccessor(reader.wrapper) - const resp = await readerNameAccessor.readAsResult(compressedWriter.address) - assertValidNameResponse(resp) - }) - }) - - it('cannot write with a signer that is not authorized', async () => { - // Mock the 404 - fetchMock.mock( - writer.storageRoot + `/account/authorizedSigners/${toChecksumAddress(signer.address)}`, - 404 - ) - - const wrapper = new BasicDataWrapper(signer.address, kit) - wrapper.storageWriter = new MockStorageWriter( - writer.localStorageRoot, - writer.storageRoot, - fetchMock - ) - const nameAccessor = new PublicNameAccessor(wrapper) - await nameAccessor.write(testPayload) - - const receivedName = await nameAccessor.readAsResult(writer.address) - expect(receivedName.ok).toEqual(false) - const authorizedSignerAccessor = new AuthorizedSignerAccessor(writer.wrapper) - const authorization = await authorizedSignerAccessor.readAsResult( - writer.address, - signer.address - ) - expect(authorization.ok).toEqual(false) - }) - - describe('with a different key being authorized to sign off-chain', () => { - beforeEach(async () => { - const pop = await accounts.generateProofOfKeyPossession(writer.address, signer.address) - const authorizedSignerAccessor = new AuthorizedSignerAccessor(writer.wrapper) - await authorizedSignerAccessor.write(signer.address, serializeSignature(pop), '.*') - }) - - it('can read the authorization', async () => { - const authorizedSignerAccessor = new AuthorizedSignerAccessor(reader.wrapper) - const authorization = await authorizedSignerAccessor.readAsResult( - writer.address, - signer.address - ) - expect(authorization).toBeDefined() - }) - - it('can write a name', async () => { - const nameAccessor = new PublicNameAccessor(signer.wrapper) - await nameAccessor.write(testPayload) - - const readerNameAccessor = new PublicNameAccessor(reader.wrapper) - const resp = await readerNameAccessor.readAsResult(writer.address) - if (resp.ok) { - expect(resp.result.name).toEqual(testname) - } - }) - }) - - describe('with a reader that has a dataEncryptionKey registered', () => { - it('encrypted data can be read and written', async () => { - const writerNameAccessor = new PrivateNameAccessor(writer.wrapper) - await writerNameAccessor.write(testPayload, [reader.address]) - - const readerNameAccessor = new PrivateNameAccessor(reader.wrapper) - const receivedName = await readerNameAccessor.readAsResult(writer.address) - - if (receivedName.ok) { - expect(receivedName.result.name).toEqual(testname) - } else { - console.error(receivedName.error) - throw new Error('should not get here') - } - }) - - it('trying to read encrypted data without an encrypted accessor fails', async () => { - const nameAccessor = new PrivateNameAccessor(writer.wrapper) - await nameAccessor.write(testPayload, [reader.address]) - - const readerNameAccessor = new PublicNameAccessor(reader.wrapper) - const receivedName = await readerNameAccessor.readAsResult(writer.address) - - if (receivedName.ok) { - throw new Error('Should not be able to read data without encrypted name accessor') - } - expect(receivedName.error.errorType).toBe(SchemaErrorTypes.OffchainError) - // @ts-ignore - expect(receivedName.error.error.errorType).toBe(OffchainErrorTypes.NoStorageRootProvidedData) - }) - - it('can re-encrypt data to more recipients', async () => { - const nameAccessor = new PrivateNameAccessor(writer.wrapper) - await nameAccessor.write(testPayload, [reader.address]) - await nameAccessor.allowAccess([reader2.address]) - - const readerNameAccessor = new PrivateNameAccessor(reader.wrapper) - const receivedName = await readerNameAccessor.readAsResult(writer.address) - const reader2NameAccessor = new PrivateNameAccessor(reader2.wrapper) - const receivedName2 = await reader2NameAccessor.readAsResult(writer.address) - - if (receivedName.ok && receivedName2.ok) { - expect(receivedName.result.name).toEqual(testname) - expect(receivedName2.result.name).toEqual(testname) - } else { - throw new Error('should not get here') - } - }) - - it('can encrypt data with user defined symmetric key', async () => { - const symmetricKey = randomBytes(16) - - const nameAccessor = new PrivateNameAccessor(writer.wrapper) - await nameAccessor.write(testPayload, [reader.address], symmetricKey) - - const readerNameAccessor = new PrivateNameAccessor(reader.wrapper) - const receivedName = await readerNameAccessor.readAsResult(writer.address) - - if (receivedName.ok) { - expect(receivedName.result.name).toEqual(testname) - } else { - console.error(receivedName.error) - throw new Error('should not get here') - } - }) - - describe('when the key is not added to the wallet', () => { - beforeEach(() => { - reader.kit - .getWallet()! - .removeAccount(publicKeyToAddress(privateKeyToPublicKey(readerEncryptionKeyPrivate))) - }) - - it('the reader cannot decrypt the data', async () => { - const nameAccessor = new PrivateNameAccessor(writer.wrapper) - await nameAccessor.write(testPayload, [reader.address]) - - const readerNameAccessor = new PrivateNameAccessor(reader.wrapper) - const receivedName = await readerNameAccessor.readAsResult(writer.address) - - if (receivedName.ok) { - throw new Error('Should not get here') - } - - expect(receivedName.error.errorType).toEqual(SchemaErrorTypes.UnavailableKey) - }) - }) - }) - - describe('when data encryption keys are compressed', () => { - it('works when the writer has a compressed key', async () => { - const writerPrivateKey = ACCOUNT_PRIVATE_KEYS[4] - const writerDEK = randomBytes(32).toString('hex') - const compressedWriter = await setupAccount(writerPrivateKey, writerDEK, true) - compressedWriter.wrapper.kit.connection.addAccount(writerDEK) - - const writerNameAccessor = new PrivateNameAccessor(compressedWriter.wrapper) - const readerNameAccessor = new PrivateNameAccessor(reader.wrapper) - - await writerNameAccessor.write(testPayload, [reader.address]) - const receivedName = await readerNameAccessor.readAsResult(compressedWriter.address) - if (receivedName.ok) { - expect(receivedName.result.name).toEqual(testname) - } else { - console.error(receivedName.error) - throw new Error('should not get here') - } - }) - - it('works when the reader has a compressed key', async () => { - const readerPrivateKey = ACCOUNT_PRIVATE_KEYS[7] - const readerDEK = randomBytes(32).toString('hex') - const compressedReader = await setupAccount(readerPrivateKey, readerDEK, true) - compressedReader.wrapper.kit.connection.addAccount(readerDEK) - - const writerNameAccessor = new PrivateNameAccessor(writer.wrapper) - const readerNameAccessor = new PrivateNameAccessor(compressedReader.wrapper) - - await writerNameAccessor.write(testPayload, [compressedReader.address]) - const receivedName = await readerNameAccessor.readAsResult(writer.address) - if (receivedName.ok) { - expect(receivedName.result.name).toEqual(testname) - } else { - console.error(receivedName.error) - throw new Error('should not get here') - } - }) - - it('works when both the reader and the writer have compressed keys', async () => { - const writerPrivateKey = ACCOUNT_PRIVATE_KEYS[8] - const writerDEK = randomBytes(32).toString('hex') - const compressedWriter = await setupAccount(writerPrivateKey, writerDEK, true) - compressedWriter.wrapper.kit.connection.addAccount(writerDEK) - - const readerPrivateKey = ACCOUNT_PRIVATE_KEYS[9] - const readerDEK = randomBytes(32).toString('hex') - const compressedReader = await setupAccount(readerPrivateKey, readerDEK, true) - compressedReader.wrapper.kit.connection.addAccount(readerDEK) - - const writerNameAccessor = new PrivateNameAccessor(compressedWriter.wrapper) - const readerNameAccessor = new PrivateNameAccessor(compressedReader.wrapper) - - await writerNameAccessor.write(testPayload, [compressedReader.address]) - const receivedName = await readerNameAccessor.readAsResult(compressedWriter.address) - if (receivedName.ok) { - expect(receivedName.result.name).toEqual(testname) - } else { - console.error(receivedName.error) - throw new Error('should not get here') - } - }) - }) -}) diff --git a/packages/sdk/identity/src/offchain-data-wrapper.ts b/packages/sdk/identity/src/offchain-data-wrapper.ts deleted file mode 100644 index 8a1e5c72eb9..00000000000 --- a/packages/sdk/identity/src/offchain-data-wrapper.ts +++ /dev/null @@ -1,215 +0,0 @@ -import { Address, ensureLeading0x } from '@celo/base/lib/address' -import { Err, makeAsyncThrowable, Ok, Result, RootError } from '@celo/base/lib/result' -import { ContractKit } from '@celo/contractkit' -import { ClaimTypes } from '@celo/contractkit/lib/identity/claims/types' -import { IdentityMetadataWrapper } from '@celo/contractkit/lib/identity/metadata' -import { publicKeyToAddress } from '@celo/utils/lib/address' -import { ensureUncompressed } from '@celo/utils/lib/ecdh' -import { - recoverEIP712TypedDataSignerRsv, - recoverEIP712TypedDataSignerVrs, - verifyEIP712TypedDataSigner, -} from '@celo/utils/lib/signatureUtils' -import fetch from 'cross-fetch' -import debugFactory from 'debug' -import * as t from 'io-ts' -import { AuthorizedSignerAccessor } from './offchain/accessors/authorized-signer' -import { StorageWriter } from './offchain/storage-writers' -import { buildEIP712TypedData, resolvePath } from './offchain/utils' - -const debug = debugFactory('offchaindata') - -export enum OffchainErrorTypes { - FetchError = 'FetchError', - InvalidSignature = 'InvalidSignature', - NoStorageRootProvidedData = 'NoStorageRootProvidedData', - NoStorageProvider = 'NoStorageProvider', -} - -export class FetchError extends RootError { - constructor(error: Error) { - super(OffchainErrorTypes.FetchError) - this.message = error.message - } -} - -export class InvalidSignature extends RootError { - constructor() { - super(OffchainErrorTypes.InvalidSignature) - } -} - -export class NoStorageRootProvidedData extends RootError { - constructor() { - super(OffchainErrorTypes.NoStorageRootProvidedData) - } -} - -export class NoStorageProvider extends RootError { - constructor() { - super(OffchainErrorTypes.NoStorageProvider) - } -} - -export type OffchainErrors = - | FetchError - | InvalidSignature - | NoStorageRootProvidedData - | NoStorageProvider - -export interface OffchainDataWrapper { - kit: ContractKit - signer: Address - self: Address - writeDataTo(data: Buffer, signature: Buffer, dataPath: string): Promise - readDataFromAsResult( - account: Address, - dataPath: string, - checkOffchainSigners: boolean, - type?: t.Type - ): Promise> -} - -export class BasicDataWrapper implements OffchainDataWrapper { - storageWriter: StorageWriter | undefined - signer: string - - constructor(readonly self: string, readonly kit: ContractKit, signer?: string) { - this.signer = signer || self - } - - async readDataFromAsResult( - account: Address, - dataPath: string, - checkOffchainSigners: boolean, - type?: t.Type - ): Promise> { - const accounts = await this.kit.contracts.getAccounts() - const metadataURL = await accounts.getMetadataURL(account) - debug({ account, metadataURL }) - const metadata = await IdentityMetadataWrapper.fetchFromURL(accounts, metadataURL) - // TODO: Filter StorageRoots with the datapath glob - const storageRoots = metadata - .filterClaims(ClaimTypes.STORAGE) - .map((_) => new StorageRoot(this, account, _.address)) - - if (storageRoots.length === 0) { - return Err(new NoStorageRootProvidedData()) - } - - const results = await Promise.all( - storageRoots.map(async (s) => s.readAndVerifySignature(dataPath, checkOffchainSigners, type)) - ) - const item = results.find((s) => s.ok) - - if (item === undefined) { - return Err(new NoStorageRootProvidedData()) - } - - return item - } - - readDataFrom = makeAsyncThrowable(this.readDataFromAsResult.bind(this)) - - async writeDataTo( - data: Buffer, - signature: Buffer, - dataPath: string - ): Promise { - if (this.storageWriter === undefined) { - return new NoStorageProvider() - } - - try { - await Promise.all([ - this.storageWriter.write(data, dataPath), - this.storageWriter.write(signature, `${dataPath}.signature`), - ]) - } catch (e: any) { - return new FetchError(e instanceof Error ? e : new Error(e)) - } - } -} - -class StorageRoot { - constructor( - readonly wrapper: OffchainDataWrapper, - readonly account: Address, - readonly root: string - ) {} - - async readAndVerifySignature( - dataPath: string, - checkOffchainSigners: boolean, - type?: t.Type - ): Promise> { - let dataResponse, signatureResponse - - try { - ;[dataResponse, signatureResponse] = await Promise.all([ - fetch(resolvePath(this.root, dataPath)), - fetch(resolvePath(this.root, `${dataPath}.signature`)), - ]) - } catch (error: any) { - const fetchError = error instanceof Error ? error : new Error(error) - return Err(new FetchError(fetchError)) - } - - if (!dataResponse.ok) { - return Err(new FetchError(new Error(dataResponse.statusText))) - } - if (!signatureResponse.ok) { - return Err(new FetchError(new Error(signatureResponse.statusText))) - } - - const [dataBody, signatureBody] = await Promise.all([ - dataResponse.arrayBuffer(), - signatureResponse.arrayBuffer(), - ]) - const body = Buffer.from(dataBody) - const signature = ensureLeading0x(Buffer.from(signatureBody).toString('hex')) - - const toParse = type ? JSON.parse(body.toString()) : body - const typedData = await buildEIP712TypedData(this.wrapper, dataPath, toParse, type) - - if (verifyEIP712TypedDataSigner(typedData, signature, this.account)) { - return Ok(body) - } - - const accounts = await this.wrapper.kit.contracts.getAccounts() - if (await accounts.isAccount(this.account)) { - const keys = await Promise.all([ - accounts.getVoteSigner(this.account), - accounts.getValidatorSigner(this.account), - accounts.getAttestationSigner(this.account), - accounts.getDataEncryptionKey(this.account), - ]) - - const dekAddress = keys[3] ? publicKeyToAddress(ensureUncompressed(keys[3])) : '0x0' - const signers = [keys[0], keys[1], keys[2], dekAddress] - - if (signers.some((signer) => verifyEIP712TypedDataSigner(typedData, signature, signer))) { - return Ok(body) - } - - if (checkOffchainSigners) { - let guessedSigner: string - try { - guessedSigner = recoverEIP712TypedDataSignerRsv(typedData, signature) - } catch (error) { - guessedSigner = recoverEIP712TypedDataSignerVrs(typedData, signature) - } - const authorizedSignerAccessor = new AuthorizedSignerAccessor(this.wrapper) - const authorizedSigner = await authorizedSignerAccessor.readAsResult( - this.account, - guessedSigner - ) - if (authorizedSigner.ok) { - return Ok(body) - } - } - } - - return Err(new InvalidSignature()) - } -} diff --git a/packages/sdk/identity/src/offchain/accessors/authorized-signer.ts b/packages/sdk/identity/src/offchain/accessors/authorized-signer.ts deleted file mode 100644 index 366480b804d..00000000000 --- a/packages/sdk/identity/src/offchain/accessors/authorized-signer.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { Address, trimLeading0x } from '@celo/base' -import { Err, makeAsyncThrowable } from '@celo/base/lib/result' -import { toChecksumAddress } from '@celo/utils/lib/address' -import { AddressType, SignatureType } from '@celo/utils/lib/io' -import * as t from 'io-ts' -import { OffchainDataWrapper, OffchainErrors } from '../../offchain-data-wrapper' -import { buildEIP712TypedData, deserialize } from '../utils' -import { OffchainError } from './errors' - -const AuthorizedSignerSchema = t.type({ - address: AddressType, - proofOfPossession: SignatureType, - filteredDataPaths: t.string, -}) - -export class AuthorizedSignerAccessor { - basePath = '/account/authorizedSigners' - constructor(readonly wrapper: OffchainDataWrapper) {} - - async readAsResult(account: Address, signer: Address) { - const dataPath = this.basePath + '/' + toChecksumAddress(signer) - const rawData = await this.wrapper.readDataFromAsResult( - account, - dataPath, - false, - AuthorizedSignerSchema - ) - if (!rawData.ok) { - return Err(new OffchainError(rawData.error)) - } - - return deserialize(AuthorizedSignerSchema, rawData.result) - } - - read = makeAsyncThrowable(this.readAsResult.bind(this)) - - async write( - signer: Address, - proofOfPossession: string, - filteredDataPaths: string - ): Promise { - const payload = { - address: toChecksumAddress(signer), - proofOfPossession, - filteredDataPaths, - } - const dataPath = this.basePath + '/' + toChecksumAddress(signer) - const typedData = await buildEIP712TypedData( - this.wrapper, - dataPath, - payload, - AuthorizedSignerSchema - ) - const signature = await this.wrapper.kit - .getWallet()! - .signTypedData(this.wrapper.self, typedData) - return this.wrapper.writeDataTo( - Buffer.from(JSON.stringify(payload)), - Buffer.from(trimLeading0x(signature), 'hex'), - dataPath - ) - } -} diff --git a/packages/sdk/identity/src/offchain/accessors/binary.ts b/packages/sdk/identity/src/offchain/accessors/binary.ts deleted file mode 100644 index 71cfb41e03e..00000000000 --- a/packages/sdk/identity/src/offchain/accessors/binary.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { Address, trimLeading0x } from '@celo/base/lib/address' -import { Err, makeAsyncThrowable, Ok } from '@celo/base/lib/result' -import { OffchainDataWrapper } from '../../offchain-data-wrapper' -import { readEncrypted, signBuffer, writeEncrypted, writeSymmetricKeys } from '../utils' -import { OffchainError } from './errors' -import { PrivateAccessor, PublicAccessor } from './interfaces' - -/** - * Schema for writing any generic binary data - */ -export class PublicBinaryAccessor implements PublicAccessor { - constructor(readonly wrapper: OffchainDataWrapper, readonly dataPath: string) {} - - async write(data: Buffer) { - const signature = await signBuffer(this.wrapper, this.dataPath, data) - const error = await this.wrapper.writeDataTo( - data, - Buffer.from(trimLeading0x(signature), 'hex'), - this.dataPath - ) - if (error) { - return new OffchainError(error) - } - } - - async readAsResult(account: Address) { - const rawData = await this.wrapper.readDataFromAsResult(account, this.dataPath, true) - if (!rawData.ok) { - return Err(new OffchainError(rawData.error)) - } - - return Ok(rawData.result) - } - - read = makeAsyncThrowable(this.readAsResult.bind(this)) -} - -/** - * Schema for writing any encrypted binary data. - */ -export class PrivateBinaryAccessor implements PrivateAccessor { - constructor(readonly wrapper: OffchainDataWrapper, readonly dataPath: string) {} - - async write(data: Buffer, toAddresses: Address[], symmetricKey?: Buffer) { - return writeEncrypted(this.wrapper, this.dataPath, data, toAddresses, symmetricKey) - } - - async allowAccess(toAddresses: Address[], symmetricKey?: Buffer) { - return writeSymmetricKeys(this.wrapper, this.dataPath, toAddresses, symmetricKey) - } - - async readAsResult(account: Address) { - return readEncrypted(this.wrapper, this.dataPath, account) - } - - read = makeAsyncThrowable(this.readAsResult.bind(this)) -} diff --git a/packages/sdk/identity/src/offchain/accessors/errors.ts b/packages/sdk/identity/src/offchain/accessors/errors.ts deleted file mode 100644 index 0edddc8ed08..00000000000 --- a/packages/sdk/identity/src/offchain/accessors/errors.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { Address } from '@celo/base' -import { RootError } from '@celo/base/lib/result' -import { OffchainErrors } from '../../offchain-data-wrapper' - -export enum SchemaErrorTypes { - InvalidDataError = 'InvalidDataError', - OffchainError = 'OffchainError', - UnknownCiphertext = 'UnknownCiphertext', - UnavailableKey = 'UnavailableKey', - InvalidKey = 'InvalidKey', -} - -export class InvalidDataError extends RootError { - constructor() { - super(SchemaErrorTypes.InvalidDataError) - } -} - -export class OffchainError extends RootError { - constructor(readonly error: OffchainErrors) { - super(SchemaErrorTypes.OffchainError) - } -} - -export class UnknownCiphertext extends RootError { - constructor() { - super(SchemaErrorTypes.UnknownCiphertext) - } -} - -export class UnavailableKey extends RootError { - constructor(readonly account: Address) { - super(SchemaErrorTypes.UnavailableKey) - this.message = `Unable to find account ${account}` - } -} - -export class InvalidKey extends RootError { - constructor() { - super(SchemaErrorTypes.InvalidKey) - } -} - -export type SchemaErrors = - | InvalidDataError - | OffchainError - | UnknownCiphertext - | UnavailableKey - | InvalidKey diff --git a/packages/sdk/identity/src/offchain/accessors/interfaces.ts b/packages/sdk/identity/src/offchain/accessors/interfaces.ts deleted file mode 100644 index c73ac438069..00000000000 --- a/packages/sdk/identity/src/offchain/accessors/interfaces.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Result } from '@celo/base' -import { SchemaErrors } from './errors' - -export interface PublicAccessor { - write: (data: DataType) => Promise - read: (from: string) => Promise - readAsResult: (from: string) => Promise> -} - -export interface PrivateAccessor { - write: (data: DataType, to: string[], symmetricKey?: Buffer) => Promise - read: (from: string) => Promise - readAsResult: (from: string) => Promise> -} diff --git a/packages/sdk/identity/src/offchain/accessors/name.ts b/packages/sdk/identity/src/offchain/accessors/name.ts deleted file mode 100644 index ca4d53eb719..00000000000 --- a/packages/sdk/identity/src/offchain/accessors/name.ts +++ /dev/null @@ -1,21 +0,0 @@ -import * as t from 'io-ts' -import { OffchainDataWrapper } from '../../offchain-data-wrapper' -import { PrivateSimpleAccessor, PublicSimpleAccessor } from './simple' - -const NameSchema = t.type({ - name: t.string, -}) - -export type NameType = t.TypeOf - -export class PublicNameAccessor extends PublicSimpleAccessor { - constructor(readonly wrapper: OffchainDataWrapper) { - super(wrapper, NameSchema, '/account/name') - } -} - -export class PrivateNameAccessor extends PrivateSimpleAccessor { - constructor(readonly wrapper: OffchainDataWrapper) { - super(wrapper, NameSchema, '/account/name') - } -} diff --git a/packages/sdk/identity/src/offchain/accessors/pictures.ts b/packages/sdk/identity/src/offchain/accessors/pictures.ts deleted file mode 100644 index cf60ecd0c25..00000000000 --- a/packages/sdk/identity/src/offchain/accessors/pictures.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { OffchainDataWrapper } from '../../offchain-data-wrapper' -import { PrivateBinaryAccessor, PublicBinaryAccessor } from './binary' - -export class PublicPictureAccessor extends PublicBinaryAccessor { - constructor(readonly wrapper: OffchainDataWrapper) { - super(wrapper, '/account/picture') - } -} - -export class PrivatePictureAccessor extends PrivateBinaryAccessor { - constructor(readonly wrapper: OffchainDataWrapper) { - super(wrapper, '/account/picture') - } -} diff --git a/packages/sdk/identity/src/offchain/accessors/simple.ts b/packages/sdk/identity/src/offchain/accessors/simple.ts deleted file mode 100644 index 4089900c1a2..00000000000 --- a/packages/sdk/identity/src/offchain/accessors/simple.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { Address, trimLeading0x } from '@celo/base' -import { Err, makeAsyncThrowable, Result } from '@celo/base/lib/result' -import * as t from 'io-ts' -import { OffchainDataWrapper } from '../../offchain-data-wrapper' -import { - buildEIP712TypedData, - deserialize, - readEncrypted, - writeEncrypted, - writeSymmetricKeys, -} from '../utils' -import { InvalidDataError, OffchainError, SchemaErrors } from './errors' -import { PrivateAccessor, PublicAccessor } from './interfaces' - -function serialize(data: DataType) { - return Buffer.from(JSON.stringify(data)) -} - -/** - * A generic schema for reading and writing objects to and from storage. Passing - * in a type parameter is supported for runtime type safety. - */ -export class PublicSimpleAccessor implements PublicAccessor { - constructor( - readonly wrapper: OffchainDataWrapper, - readonly type: t.Type, - readonly dataPath: string - ) {} - - private async sign(data: DataType) { - const typedData = await buildEIP712TypedData(this.wrapper, this.dataPath, data, this.type) - const wallet = this.wrapper.kit.getWallet()! - return wallet.signTypedData(this.wrapper.signer, typedData) - } - - async write(data: DataType) { - if (!this.type.is(data)) { - return new InvalidDataError() - } - - const signature = await this.sign(data) - const error = await this.wrapper.writeDataTo( - serialize(data), - Buffer.from(trimLeading0x(signature), 'hex'), - this.dataPath - ) - if (error) { - return new OffchainError(error) - } - } - - async readAsResult(account: Address): Promise> { - const rawData = await this.wrapper.readDataFromAsResult(account, this.dataPath, true, this.type) - - if (!rawData.ok) { - return Err(new OffchainError(rawData.error)) - } - - const deserializedResult = deserialize(this.type, rawData.result) - if (deserializedResult.ok) { - return deserializedResult - } - - return deserializedResult - } - - read = makeAsyncThrowable(this.readAsResult.bind(this)) -} - -/** - * A generic schema for writing and reading encrypted objects to and from storage. Passing - * in a type parameter is supported for runtime type safety. - */ -export class PrivateSimpleAccessor implements PrivateAccessor { - constructor( - readonly wrapper: OffchainDataWrapper, - readonly type: t.Type, - readonly dataPath: string - ) {} - - write(data: DataType, toAddresses: Address[], symmetricKey?: Buffer) { - if (!this.type.is(data)) { - return Promise.resolve(new InvalidDataError()) - } - - return writeEncrypted(this.wrapper, this.dataPath, serialize(data), toAddresses, symmetricKey) - } - - async allowAccess(toAddresses: Address[], symmetricKey?: Buffer) { - return writeSymmetricKeys(this.wrapper, this.dataPath, toAddresses, symmetricKey) - } - - async readAsResult(account: Address): Promise> { - const encryptedResult = await readEncrypted(this.wrapper, this.dataPath, account) - - if (encryptedResult.ok) { - return deserialize(this.type, encryptedResult.result) - } - - return encryptedResult - } - - read = makeAsyncThrowable(this.readAsResult.bind(this)) -} diff --git a/packages/sdk/identity/src/offchain/storage-writers.ts b/packages/sdk/identity/src/offchain/storage-writers.ts deleted file mode 100644 index 3b6fc8bbe4b..00000000000 --- a/packages/sdk/identity/src/offchain/storage-writers.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { spawnSync } from 'child_process' -import { promises } from 'fs' -import { join, parse } from 'path' -import { resolvePath } from './utils' -export abstract class StorageWriter { - abstract write(_data: Buffer, _dataPath: string): Promise -} - -export class LocalStorageWriter extends StorageWriter { - constructor(readonly root: string) { - super() - } - async write(data: Buffer, dataPath: string): Promise { - return this.writeToFs(data, dataPath) - } - - protected async writeToFs(data: string | Buffer, dataPath: string): Promise { - const directory = parse(dataPath).dir - await promises.mkdir(join(this.root, directory), { recursive: true }) - await promises.writeFile(join(this.root, dataPath), data) - } -} - -export class GitStorageWriter extends LocalStorageWriter { - async write(data: Buffer, dataPath: string): Promise { - await this.writeToFs(data, dataPath) - spawnSync('git', ['add', dataPath], { - cwd: this.root, - }) - spawnSync('git', ['commit', '--message', `"Upload ${dataPath}"`], { cwd: this.root }) - spawnSync('git', ['push', 'origin', 'master'], { cwd: this.root }) - return - } -} - -export class GoogleStorageWriter extends LocalStorageWriter { - private readonly bucket: string - - constructor(readonly local: string, bucket: string) { - super(local) - this.bucket = bucket - } - - async write(data: Buffer, dataPath: string): Promise { - await this.writeToFs(data, dataPath) - spawnSync('gsutil', ['cp', join(this.root, dataPath), `gs://${this.bucket}${dataPath}`], { - cwd: this.root, - }) - } -} - -export class AwsStorageWriter extends LocalStorageWriter { - private readonly bucket: string - - constructor(readonly local: string, bucket: string) { - super(local) - this.bucket = bucket - } - - async write(data: Buffer, dataPath: string): Promise { - await this.writeToFs(data, dataPath) - spawnSync('aws', ['s3', 'cp', join(this.root, dataPath), `s3://${this.bucket}${dataPath}`], { - cwd: this.root, - }) - } -} - -export class MockStorageWriter extends LocalStorageWriter { - constructor(readonly root: string, readonly mockedStorageRoot: string, readonly fetchMock: any) { - super(root) - } - async write(data: Buffer, dataPath: string): Promise { - await this.writeToFs(data, dataPath) - this.fetchMock.mock(resolvePath(this.mockedStorageRoot, dataPath), data, { - sendAsJson: false, - overwriteRoutes: true, - }) - } -} diff --git a/packages/sdk/identity/src/offchain/utils.ts b/packages/sdk/identity/src/offchain/utils.ts deleted file mode 100644 index af8674bbdc3..00000000000 --- a/packages/sdk/identity/src/offchain/utils.ts +++ /dev/null @@ -1,407 +0,0 @@ -import { ensureLeading0x, Err, Ok, parseJsonAsResult, Result, trimLeading0x } from '@celo/base' -import { Address, publicKeyToAddress } from '@celo/utils/lib/address' -import { ensureCompressed, ensureUncompressed, trimUncompressedPrefix } from '@celo/utils/lib/ecdh' -import { AES128Decrypt, AES128Encrypt, Encrypt, IV_LENGTH } from '@celo/utils/lib/ecies' -import { EIP712Object, EIP712TypedData } from '@celo/utils/lib/sign-typed-data-utils' -import { createHmac, randomBytes } from 'crypto' -import { keccak256 } from 'ethereum-cryptography/keccak' -import { toHex } from 'ethereum-cryptography/utils' -import { isLeft } from 'fp-ts/lib/Either' -import * as t from 'io-ts' -import { join, sep } from 'path' -import { OffchainDataWrapper, OffchainErrorTypes } from '../offchain-data-wrapper' -import { - InvalidDataError, - InvalidKey, - OffchainError, - SchemaErrors, - SchemaErrorTypes, - UnavailableKey, -} from './accessors/errors' - -const KEY_LENGTH = 16 - -// label = PRF(ECDH(A, B), A || B || data path) -// ciphertext path = "/cosmetic path/" || base64(label) -function getCiphertextLabel( - path: string, - sharedSecret: Buffer, - senderPublicKey: string, - receiverPublicKey: string -) { - const senderPublicKeyBuffer = Buffer.from(ensureCompressed(senderPublicKey), 'hex') - const receiverPublicKeyBuffer = Buffer.from(ensureCompressed(receiverPublicKey), 'hex') - - const label = createHmac('sha256', sharedSecret) - .update(Buffer.concat([senderPublicKeyBuffer, receiverPublicKeyBuffer, Buffer.from(path)])) - .digest('hex') - return join(sep, 'ciphertexts', label) -} - -// Assumes that the wallet has the dataEncryptionKey of wrapper.self available -// TODO: Should check and throw a more meaningful error if not - -/** - * Encrypts the symmetric key `key` to `toAddress`'s data encryption key and uploads it - * to the computed storage path. - * - * @param wrapper the offchain data wrapper - * @param dataPath logical path for the data. Used to derive the key location - * @param key the symmetric key to distribute - * @param toAddress address to encrypt symmetric key to - */ -const distributeSymmetricKey = async ( - wrapper: OffchainDataWrapper, - dataPath: string, - key: Buffer, - toAddress: Address -): Promise => { - const accounts = await wrapper.kit.contracts.getAccounts() - const [fromPubKey, toPubKey] = await Promise.all([ - accounts.getDataEncryptionKey(wrapper.self), - accounts.getDataEncryptionKey(toAddress), - ]) - if (fromPubKey === null) { - return new UnavailableKey(wrapper.self) - } - if (toPubKey === null) { - return new UnavailableKey(toAddress) - } - - const wallet = wrapper.kit.getWallet()! - const sharedSecret = await wallet.computeSharedSecret(publicKeyToAddress(fromPubKey), toPubKey) - - const computedDataPath = getCiphertextLabel(`${dataPath}.key`, sharedSecret, fromPubKey, toPubKey) - const encryptedData = Encrypt( - Buffer.from(trimUncompressedPrefix(ensureUncompressed(toPubKey)), 'hex'), - key - ) - - const signature = await signBuffer(wrapper, computedDataPath, encryptedData) - const writeError = await wrapper.writeDataTo( - encryptedData, - Buffer.from(trimLeading0x(signature), 'hex'), - computedDataPath - ) - if (writeError) { - return new OffchainError(writeError) - } -} - -/** - * Handles choosing the symmetric key to use. - * If we're explicitly passing in a key, use that, - * If a key has already been generated for this dataPath, use that, - * Else generate a new one - * - * @param wrapper the offchain data wrapper - * @param dataPath path to where the encrypted data is stored. Used to derive the key location - * @param symmetricKey - */ -async function fetchOrGenerateKey( - wrapper: OffchainDataWrapper, - dataPath: string, - symmetricKey?: Buffer -) { - if (symmetricKey) { - return Ok(symmetricKey) - } - - const existingKey = await readSymmetricKey(wrapper, dataPath, wrapper.self) - if (existingKey.ok) { - return Ok(existingKey.result) - } - - if ( - existingKey.error.errorType === SchemaErrorTypes.OffchainError && - (existingKey.error.error.errorType === OffchainErrorTypes.NoStorageRootProvidedData || - existingKey.error.error.errorType === OffchainErrorTypes.FetchError) - ) { - return Ok(randomBytes(16)) - } - - return Err(existingKey.error) -} - -/** - * Handles encrypting the data with a symmetric key, then distributing said key to each address - * in the `toAddresses` array. - * - * @param wrapper the offchain data wrapper - * @param dataPath path to where the encrypted data is stored. Used to derive the key location - * @param data the data to encrypt - * @param toAddresses the addresses to distribute the symmetric key to - * @param symmetricKey the symmetric key to use to encrypt the data. One will be found or generated if not provided - */ -export const writeEncrypted = async ( - wrapper: OffchainDataWrapper, - dataPath: string, - data: Buffer, - toAddresses: Address[], - symmetricKey?: Buffer -): Promise => { - const fetchKey = await fetchOrGenerateKey(wrapper, dataPath, symmetricKey) - if (!fetchKey.ok) { - return fetchKey.error - } - - const iv = randomBytes(16) - const payload = AES128Encrypt(fetchKey.result, iv, data) - const signature = await signBuffer(wrapper, `${dataPath}.enc`, payload) - - const writeError = await wrapper.writeDataTo( - payload, - Buffer.from(trimLeading0x(signature), 'hex'), - `${dataPath}.enc` - ) - if (writeError) { - return new OffchainError(writeError) - } - - const firstWriteError = ( - await Promise.all( - // here we encrypt the key to ourselves so we can retrieve it later - [wrapper.self, ...toAddresses].map(async (toAddress) => - distributeSymmetricKey(wrapper, dataPath, fetchKey.result, toAddress) - ) - ) - ).find(Boolean) - return firstWriteError -} - -export const writeSymmetricKeys = async ( - wrapper: OffchainDataWrapper, - dataPath: string, - toAddresses: Address[], - symmetricKey?: Buffer -): Promise => { - const fetchKey = await fetchOrGenerateKey(wrapper, dataPath, symmetricKey) - if (!fetchKey.ok) { - return fetchKey.error - } - - const firstWriteError = ( - await Promise.all( - toAddresses.map(async (toAddress) => - distributeSymmetricKey(wrapper, dataPath, fetchKey.result, toAddress) - ) - ) - ).find(Boolean) - return firstWriteError -} - -/** - * Reads and decrypts a symmetric key that has been encrypted to your - * data encryption key. - * - * @param wrapper the offchain data wrapper - * @param dataPath path to where the encrypted data is stored. Used to derive the key location - * @param senderAddress the address that encrypted this key to you - */ -const readSymmetricKey = async ( - wrapper: OffchainDataWrapper, - dataPath: string, - senderAddress: Address -): Promise> => { - const accounts = await wrapper.kit.contracts.getAccounts() - const wallet = wrapper.kit.getWallet()! - const [readerPubKey, senderPubKey] = await Promise.all([ - accounts.getDataEncryptionKey(wrapper.self), - accounts.getDataEncryptionKey(senderAddress), - ]) - - if (readerPubKey === null) { - return Err(new UnavailableKey(wrapper.self)) - } - if (senderPubKey === null) { - return Err(new UnavailableKey(senderAddress)) - } - - const readerPublicKeyAddress = publicKeyToAddress(readerPubKey) - if (!wallet.hasAccount(readerPublicKeyAddress)) { - return Err(new UnavailableKey(readerPublicKeyAddress)) - } - - const sharedSecret = await wallet.computeSharedSecret(readerPublicKeyAddress, senderPubKey) - const computedDataPath = getCiphertextLabel( - `${dataPath}.key`, - sharedSecret, - senderPubKey, - readerPubKey - ) - const encryptedPayload = await wrapper.readDataFromAsResult(senderAddress, computedDataPath, true) - - if (!encryptedPayload.ok) { - return Err(new OffchainError(encryptedPayload.error)) - } - - const payload = await wallet.decrypt(readerPublicKeyAddress, encryptedPayload.result) - return Ok(payload) -} - -/** - * Reads and decrypts a payload that has been encrypted to your data encryption key. Will - * resolve the symmetric key used to encrypt the payload. - * - * @param wrapper the offchain data wrapper - * @param dataPath path to where the encrypted data is stored. Used to derive the key location - * @param senderAddress the address that encrypted this key to you - */ -export const readEncrypted = async ( - wrapper: OffchainDataWrapper, - dataPath: string, - senderAddress: Address -): Promise> => { - const encryptedPayloadPath = `${dataPath}.enc` - const [payload, key] = await Promise.all([ - wrapper.readDataFromAsResult(senderAddress, encryptedPayloadPath, true), - readSymmetricKey(wrapper, dataPath, senderAddress), - ]) - - if (!payload.ok) { - return Err(new OffchainError(payload.error)) - } - if (!key.ok) { - return Err(key.error) - } - - if (key.result.length !== KEY_LENGTH) { - return Err(new InvalidKey()) - } - if (payload.result.length < IV_LENGTH) { - return Err(new InvalidDataError()) - } - - return Ok( - AES128Decrypt(key.result, payload.result.slice(0, IV_LENGTH), payload.result.slice(IV_LENGTH)) - ) -} - -export const deserialize = ( - type: t.Type, - buf: Buffer -): Result => { - const dataAsJson = parseJsonAsResult(buf.toString()) - if (!dataAsJson.ok) { - return Err(new InvalidDataError()) - } - - const parsedDataAsType = type.decode(dataAsJson.result) - if (isLeft(parsedDataAsType)) { - return Err(new InvalidDataError()) - } - - return Ok(parsedDataAsType.right) -} - -export const buildEIP712TypedData = async ( - wrapper: OffchainDataWrapper, - path: string, - data: DataType | Buffer, - type?: t.Type -): Promise => { - const chainId = await wrapper.kit.connection.chainId() - const EIP712Domain = [ - { name: 'name', type: 'string' }, - { name: 'version', type: 'string' }, - { name: 'chainId', type: 'uint256' }, - ] - - let types = {} - let message = {} - if (Buffer.isBuffer(data)) { - types = { - ClaimWithPath: [ - { name: 'path', type: 'string' }, - { name: 'hash', type: 'string' }, - ], - } - message = { - hash: ensureLeading0x(toHex(keccak256(data))), - } - } else { - const Claim = buildEIP712Schema(type!) - types = { - Claim, - ClaimWithPath: [ - { name: 'path', type: 'string' }, - { name: 'payload', type: 'Claim' }, - ], - } - message = { - payload: data as unknown as EIP712Object, - } - } - - return { - types: { - EIP712Domain, - ...types, - }, - domain: { - name: 'CIP8 Claim', - version: '1.0.0', - chainId, - }, - primaryType: 'ClaimWithPath', - message: { - path, - ...message, - }, - } -} - -export const signBuffer = async (wrapper: OffchainDataWrapper, dataPath: string, buf: Buffer) => { - const typedData = await buildEIP712TypedData(wrapper, dataPath, buf) - return wrapper.kit.getWallet()!.signTypedData(wrapper.signer, typedData) -} - -const ioTsToSolidityTypeMapping: { [x: string]: string } = { - [t.string._tag]: 'string', - [t.number._tag]: 'uint256', - [t.boolean._tag]: 'bool', -} - -type EIP712Schema = Array<{ name: string; type: string }> -const buildEIP712Schema = (type: t.Type): EIP712Schema => { - // @ts-ignore - const shape = type.props - // @ts-ignore - return Object.entries(shape).reduce((accum, [key, value]) => { - // @ts-ignore - return [ - ...accum, - { - name: key, - // @ts-ignore - type: ioTsToSolidityTypeMapping[value._tag] || 'string', - }, - ] - }, []) -} - -function ensureTrailingSeparator(str: string) { - if (str[str.length - 1] !== '/') { - return `${str}/` - } - return str -} -function trimLeadingSeparator(str: string) { - if (str[0] === '/') { - return str.slice(1) - } - return str -} - -/** - * We want users to be able to specify a root + path as their base - * storage url, https://example.com/store-under/path, for example. Constructing - * a URL doesn't respect these paths if the appended path is absolute, so we ensure - * it's not and ensure the base is - * - * @param base root or base of the domain - * @param path the path to append - */ -export function resolvePath(base: string, path: string) { - return new URL(trimLeadingSeparator(path), ensureTrailingSeparator(base)).href -} diff --git a/packages/sdk/identity/src/test-utils/setup.global.ts b/packages/sdk/identity/src/test-utils/setup.global.ts deleted file mode 100644 index fa48ed85f07..00000000000 --- a/packages/sdk/identity/src/test-utils/setup.global.ts +++ /dev/null @@ -1,26 +0,0 @@ -import baseSetup from '@celo/dev-utils/lib/ganache-setup' -// Has to import the matchers somewhere so that typescript knows the matchers have been made available -import _unused from '@celo/dev-utils/lib/matchers' -import { waitForPortOpen } from '@celo/dev-utils/lib/network' -// @ts-ignore -import flakeTrackerSetup from '@celo/flake-tracker/src/jest/setup.global' -import * as path from 'path' - -// Warning: There should be an unused import of '@celo/dev-utils/lib/matchers' above. -// If there is not, then your editor probably deleted it automatically. - -const USE_GANACHE = process.env.NO_GANACHE?.toLowerCase() !== 'true' - -export default async function globalSetup() { - await flakeTrackerSetup() - if (USE_GANACHE) { - console.log('\nstarting ganache... set NO_GANACHE=true to disable') - await baseSetup(path.resolve(path.join(__dirname, '../..')), '.tmp/devchain.tar.gz', { - from_targz: true, - }) - await waitForPortOpen('localhost', 8545, 60) - console.log('...ganache started') - } else { - console.log('skipping ganache setup') - } -} diff --git a/packages/sdk/identity/src/test-utils/teardown.global.ts b/packages/sdk/identity/src/test-utils/teardown.global.ts deleted file mode 100644 index 399c6aac6d3..00000000000 --- a/packages/sdk/identity/src/test-utils/teardown.global.ts +++ /dev/null @@ -1,12 +0,0 @@ -import teardown from '@celo/dev-utils/lib/ganache-teardown' -// @ts-ignore -import flakeTrackerTeardown from '@celo/flake-tracker/src/jest/teardown.global.js' - -const USE_GANACHE = process.env.NO_GANACHE?.toLowerCase() !== 'true' - -export default async function globalTeardown() { - await flakeTrackerTeardown() - if (USE_GANACHE) { - await teardown() - } -} diff --git a/packages/sdk/identity/tsconfig.json b/packages/sdk/identity/tsconfig.json deleted file mode 100644 index b15de292905..00000000000 --- a/packages/sdk/identity/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "@celo/typescript/tsconfig.library.json", - "compilerOptions": { - "rootDir": "src", - "outDir": "lib" - }, - "include": ["src/**/*", "types/**/*"], - "exclude": ["**/*.test.ts"] -} diff --git a/packages/sdk/identity/tslint.json b/packages/sdk/identity/tslint.json deleted file mode 100644 index e3916a756e8..00000000000 --- a/packages/sdk/identity/tslint.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": ["@celo/typescript/tslint.json"], - "rules": { - "no-global-arrow-functions": false, - "no-console": false, - "member-ordering": false, - "max-classes-per-file": false, - "import-blacklist": [true, { "elliptic": ["ec"]}] - } -} diff --git a/packages/sdk/identity/typedoc.json b/packages/sdk/identity/typedoc.json deleted file mode 100644 index ff159d9983c..00000000000 --- a/packages/sdk/identity/typedoc.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "mode": "modules", - "exclude": ["**/generated/*.ts", "**/*+(index|.test).ts"], - "excludeNotExported": true, - "excludePrivate": true, - "excludeProtected": true, - "includeDeclarations": false, - "ignoreCompilerErrors": true, - "hideGenerator": "true", - "out": "../../docs/sdk/docs/identity", - "gitRevision": "master", - "readme": "none" - } \ No newline at end of file diff --git a/packages/sdk/keystores/CHANGELOG.md b/packages/sdk/keystores/CHANGELOG.md new file mode 100644 index 00000000000..b735f8e5fd1 --- /dev/null +++ b/packages/sdk/keystores/CHANGELOG.md @@ -0,0 +1,21 @@ +# @celo/keystores + +## 5.0.5 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- Updated dependencies [53bbd4958] +- Updated dependencies [53bbd4958] + - @celo/wallet-local@5.1.0 + - @celo/utils@5.0.5 + +## 5.0.5-beta.0 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- Updated dependencies [53bbd4958] +- Updated dependencies [53bbd4958] + - @celo/wallet-local@5.1.0-beta.0 + - @celo/utils@5.0.5-beta.0 diff --git a/packages/sdk/keystores/jest.config.js b/packages/sdk/keystores/jest.config.js index 1b955b56292..2681a75d5a6 100644 --- a/packages/sdk/keystores/jest.config.js +++ b/packages/sdk/keystores/jest.config.js @@ -1,8 +1,5 @@ -const { nodeFlakeTracking } = require('@celo/flake-tracker/src/jest/config.js') - module.exports = { preset: 'ts-jest', testEnvironment: 'node', testMatch: ['/src/**/?(*.)+(spec|test).ts?(x)'], - ...nodeFlakeTracking, } diff --git a/packages/sdk/keystores/package.json b/packages/sdk/keystores/package.json index 23c6230b3ea..40e685c9327 100644 --- a/packages/sdk/keystores/package.json +++ b/packages/sdk/keystores/package.json @@ -1,6 +1,6 @@ { "name": "@celo/keystores", - "version": "4.1.1-dev", + "version": "5.0.5", "description": "keystore implementation", "author": "Celo", "license": "Apache-2.0", @@ -17,13 +17,13 @@ "build": "tsc -b .", "clean": "tsc -b . --clean", "docs": "typedoc", - "test": "SKIP_KNOWN_FLAKES=false jest --runInBand", + "test": "jest --runInBand", "lint": "tslint -c tslint.json --project .", "prepublishOnly": "yarn build" }, "dependencies": { - "@celo/utils": "4.1.1-dev", - "@celo/wallet-local": "4.1.1-dev", + "@celo/utils": "^5.0.5", + "@celo/wallet-local": "^5.1.0", "ethereumjs-wallet": "^1.0.1" }, "devDependencies": { diff --git a/packages/sdk/network-utils/CHANGELOG.md b/packages/sdk/network-utils/CHANGELOG.md new file mode 100644 index 00000000000..413be434142 --- /dev/null +++ b/packages/sdk/network-utils/CHANGELOG.md @@ -0,0 +1,13 @@ +# @celo/network-utils + +## 5.0.5 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other + +## 5.0.5-beta.0 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other diff --git a/packages/sdk/network-utils/jest.config.js b/packages/sdk/network-utils/jest.config.js index ea4971b7402..31e6b5e7485 100644 --- a/packages/sdk/network-utils/jest.config.js +++ b/packages/sdk/network-utils/jest.config.js @@ -1,13 +1,6 @@ -const { nodeFlakeTracking } = require('@celo/flake-tracker/src/jest/config.js') - module.exports = { preset: 'ts-jest', - ...nodeFlakeTracking, testMatch: ['/src/**/?(*.)+(spec|test).ts?(x)'], - setupFilesAfterEnv: [ - '@celo/dev-utils/lib/matchers', - '/jestSetup.ts', - ...nodeFlakeTracking.setupFilesAfterEnv, - ], + setupFilesAfterEnv: ['@celo/dev-utils/lib/matchers', '/jestSetup.ts'], verbose: true, } diff --git a/packages/sdk/network-utils/package.json b/packages/sdk/network-utils/package.json index 677c853f601..e3a15d8b62d 100644 --- a/packages/sdk/network-utils/package.json +++ b/packages/sdk/network-utils/package.json @@ -1,6 +1,6 @@ { "name": "@celo/network-utils", - "version": "4.1.1-dev", + "version": "5.0.5", "description": "Utilities for fetching static information about the Celo network", "main": "./lib/index.js", "types": "./lib/index.d.ts", @@ -23,13 +23,12 @@ }, "dependencies": { "@types/debug": "^4.1.5", - "cross-fetch": "3.0.4", + "cross-fetch": "3.0.6", "debug": "^4.1.1" }, "devDependencies": { - "@celo/dev-utils": "0.0.1-dev", - "fetch-mock": "9.10.4", - "@celo/flake-tracker": "0.0.1-dev" + "@celo/dev-utils": "0.0.1", + "fetch-mock": "9.10.4" }, "engines": { "node": ">=8.13.0" diff --git a/packages/sdk/phone-utils/CHANGELOG.md b/packages/sdk/phone-utils/CHANGELOG.md new file mode 100644 index 00000000000..d2a8a374f74 --- /dev/null +++ b/packages/sdk/phone-utils/CHANGELOG.md @@ -0,0 +1,19 @@ +# @celo/phone-utils + +## 5.0.5 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- Updated dependencies [53bbd4958] + - @celo/utils@5.0.5 + - @celo/base@5.0.5 + +## 5.0.5-beta.0 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- Updated dependencies [53bbd4958] + - @celo/utils@5.0.5-beta.0 + - @celo/base@5.0.5-beta.0 diff --git a/packages/sdk/phone-utils/jest.config.js b/packages/sdk/phone-utils/jest.config.js index 86abcf4a671..53358d65426 100644 --- a/packages/sdk/phone-utils/jest.config.js +++ b/packages/sdk/phone-utils/jest.config.js @@ -1,7 +1,4 @@ -const { nodeFlakeTracking } = require('@celo/flake-tracker/src/jest/config.js') - module.exports = { preset: 'ts-jest', testMatch: ['/src/**/?(*.)+(spec|test).ts?(x)'], - ...nodeFlakeTracking, } diff --git a/packages/sdk/phone-utils/package.json b/packages/sdk/phone-utils/package.json index cf57f7006a6..139c4ac2eb4 100644 --- a/packages/sdk/phone-utils/package.json +++ b/packages/sdk/phone-utils/package.json @@ -1,6 +1,6 @@ { "name": "@celo/phone-utils", - "version": "4.1.1-dev", + "version": "5.0.5", "description": "Celo phone utils", "author": "Celo", "license": "Apache-2.0", @@ -22,18 +22,18 @@ "lib/**/*" ], "dependencies": { - "@celo/base": "4.1.1-dev", - "@celo/utils": "4.1.1-dev", + "@celo/base": "^5.0.5", + "@celo/utils": "^5.0.5", "@types/country-data": "^0.0.0", "@types/google-libphonenumber": "^7.4.23", - "@types/node": "^10.12.18", + "@types/node": "^18.7.16", "country-data": "^0.0.31", "fp-ts": "2.1.1", "google-libphonenumber": "^3.2.27", - "io-ts": "2.0.1" + "io-ts": "2.0.1", + "web3-utils": "^1.10.0" }, "devDependencies": { - "@celo/flake-tracker": "0.0.1-dev", "@celo/typescript": "0.0.1" } } diff --git a/packages/sdk/transactions-uri/CHANGELOG.md b/packages/sdk/transactions-uri/CHANGELOG.md new file mode 100644 index 00000000000..6a0c3188675 --- /dev/null +++ b/packages/sdk/transactions-uri/CHANGELOG.md @@ -0,0 +1,23 @@ +# @celo/transactions-uri + +## 5.0.5 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [53bbd4958] + - @celo/connect@5.1.0 + - @celo/base@5.0.5 + +## 5.0.5-beta.0 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [53bbd4958] + - @celo/connect@5.1.0-beta.0 + - @celo/base@5.0.5-beta.0 diff --git a/packages/sdk/transactions-uri/jest.config.js b/packages/sdk/transactions-uri/jest.config.js index d68feae834d..6037bd8aa5d 100644 --- a/packages/sdk/transactions-uri/jest.config.js +++ b/packages/sdk/transactions-uri/jest.config.js @@ -1,14 +1,7 @@ -const { nodeFlakeTracking } = require('@celo/flake-tracker/src/jest/config.js') - module.exports = { preset: 'ts-jest', - ...nodeFlakeTracking, testMatch: ['/src/**/?(*.)+(spec|test).ts?(x)'], - setupFilesAfterEnv: [ - '@celo/dev-utils/lib/matchers', - '/jestSetup.ts', - ...nodeFlakeTracking.setupFilesAfterEnv, - ], + setupFilesAfterEnv: ['@celo/dev-utils/lib/matchers', '/jestSetup.ts'], globalSetup: '/src/test-utils/setup.global.ts', globalTeardown: '/src/test-utils/teardown.global.ts', verbose: true, diff --git a/packages/sdk/transactions-uri/package.json b/packages/sdk/transactions-uri/package.json index 62a6ea715a8..25aa6458c9b 100644 --- a/packages/sdk/transactions-uri/package.json +++ b/packages/sdk/transactions-uri/package.json @@ -1,6 +1,6 @@ { "name": "@celo/transactions-uri", - "version": "4.1.1-dev", + "version": "5.0.5", "description": "Celo's transactions uri generation", "main": "./lib/index.js", "types": "./lib/index.d.ts", @@ -26,17 +26,16 @@ "dependencies": { "@types/debug": "^4.1.5", "@types/qrcode": "^1.3.4", - "@celo/base": "4.1.1-dev", - "@celo/connect": "4.1.1-dev", + "@celo/base": "^5.0.5", + "@celo/connect": "^5.1.0", "bn.js": "4.11.9", "qrcode": "1.4.4", "web3-eth-abi": "1.10.0" }, "devDependencies": { - "@celo/dev-utils": "0.0.1-dev", - "@celo/contractkit": "4.1.1-dev", - "dotenv": "^8.2.0", - "@celo/flake-tracker": "0.0.1-dev" + "@celo/dev-utils": "0.0.1", + "@celo/contractkit": "^5.1.0", + "dotenv": "^8.2.0" }, "engines": { "node": ">=8.13.0" diff --git a/packages/sdk/transactions-uri/src/test-utils/setup.global.ts b/packages/sdk/transactions-uri/src/test-utils/setup.global.ts index 96fc3d25bdf..5d50245d7ff 100644 --- a/packages/sdk/transactions-uri/src/test-utils/setup.global.ts +++ b/packages/sdk/transactions-uri/src/test-utils/setup.global.ts @@ -2,15 +2,12 @@ import baseSetup from '@celo/dev-utils/lib/ganache-setup' // Has to import the matchers somewhere so that typescript knows the matchers have been made available import _unused from '@celo/dev-utils/lib/matchers' import { waitForPortOpen } from '@celo/dev-utils/lib/network' -// @ts-ignore -import flakeTrackerSetup from '@celo/flake-tracker/src/jest/setup.global' import * as path from 'path' // Warning: There should be an unused import of '@celo/dev-utils/lib/matchers' above. // If there is not, then your editor probably deleted it automatically. export default async function globalSetup() { - await flakeTrackerSetup() console.log('\nstarting ganache...') await baseSetup(path.resolve(path.join(__dirname, '../..')), '.tmp/devchain.tar.gz', { from_targz: true, diff --git a/packages/sdk/transactions-uri/src/test-utils/teardown.global.ts b/packages/sdk/transactions-uri/src/test-utils/teardown.global.ts index c85f17c2e77..aea64f79dbb 100644 --- a/packages/sdk/transactions-uri/src/test-utils/teardown.global.ts +++ b/packages/sdk/transactions-uri/src/test-utils/teardown.global.ts @@ -1,8 +1,5 @@ import teardown from '@celo/dev-utils/lib/ganache-teardown' -// @ts-ignore -import flakeTrackerTeardown from '@celo/flake-tracker/src/jest/teardown.global.js' export default async function globalTeardown() { - await flakeTrackerTeardown() await teardown() } diff --git a/packages/sdk/utils/CHANGELOG.md b/packages/sdk/utils/CHANGELOG.md new file mode 100644 index 00000000000..cabf079bc57 --- /dev/null +++ b/packages/sdk/utils/CHANGELOG.md @@ -0,0 +1,17 @@ +# @celo/utils + +## 5.0.5 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- Updated dependencies [53bbd4958] + - @celo/base@5.0.5 + +## 5.0.5-beta.0 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- Updated dependencies [53bbd4958] + - @celo/base@5.0.5-beta.0 diff --git a/packages/sdk/utils/jest.config.js b/packages/sdk/utils/jest.config.js index 86abcf4a671..53358d65426 100644 --- a/packages/sdk/utils/jest.config.js +++ b/packages/sdk/utils/jest.config.js @@ -1,7 +1,4 @@ -const { nodeFlakeTracking } = require('@celo/flake-tracker/src/jest/config.js') - module.exports = { preset: 'ts-jest', testMatch: ['/src/**/?(*.)+(spec|test).ts?(x)'], - ...nodeFlakeTracking, } diff --git a/packages/sdk/utils/package.json b/packages/sdk/utils/package.json index c24163b36e2..38623be12a3 100644 --- a/packages/sdk/utils/package.json +++ b/packages/sdk/utils/package.json @@ -1,6 +1,6 @@ { "name": "@celo/utils", - "version": "4.1.1-dev", + "version": "5.0.5", "description": "Celo common utils", "author": "Celo", "license": "Apache-2.0", @@ -22,20 +22,22 @@ "lib/**/*" ], "dependencies": { - "@celo/base": "4.1.1-dev", + "@celo/base": "^5.0.5", "@ethereumjs/util": "8.0.5", + "rlp": "^2.2.4", "@types/bn.js": "^5.1.0", "@types/elliptic": "^6.4.9", - "@types/node": "^10.12.18", + "@types/node": "^18.7.16", "bignumber.js": "^9.0.0", "elliptic": "^6.5.4", "ethereum-cryptography": "1.2.0", + "bn.js": "4.11.9", "io-ts": "2.0.1", + "fp-ts": "2.1.1", "web3-eth-abi": "1.10.0", "web3-utils": "1.10.0" }, "devDependencies": { - "@celo/flake-tracker": "0.0.1-dev", "@celo/typescript": "0.0.1" }, "resolutions": { diff --git a/packages/sdk/wallets/wallet-base/CHANGELOG.md b/packages/sdk/wallets/wallet-base/CHANGELOG.md new file mode 100644 index 00000000000..98961d60ab8 --- /dev/null +++ b/packages/sdk/wallets/wallet-base/CHANGELOG.md @@ -0,0 +1,33 @@ +# @celo/wallet-base + +## 5.1.0 + +### Minor Changes + +- 53bbd4958: Add cip64 support for feeCurrency Transactions. Note this is the replacement for the deprecated cip42 and legacy tx types https://github.com/celo-org/celo-proposals/blob/master/CIPs/cip/0064.md + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [53bbd4958] + - @celo/connect@5.1.0 + - @celo/utils@5.0.5 + - @celo/base@5.0.5 + +## 5.1.0-beta.0 + +### Minor Changes + +- 53bbd4958: Add cip64 support for feeCurrency Transactions. Note this is the replacement for the deprecated cip42 and legacy tx types https://github.com/celo-org/celo-proposals/blob/master/CIPs/cip/0064.md + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [53bbd4958] + - @celo/connect@5.1.0-beta.0 + - @celo/utils@5.0.5-beta.0 + - @celo/base@5.0.5-beta.0 diff --git a/packages/sdk/wallets/wallet-base/jest.config.js b/packages/sdk/wallets/wallet-base/jest.config.js new file mode 100644 index 00000000000..2681a75d5a6 --- /dev/null +++ b/packages/sdk/wallets/wallet-base/jest.config.js @@ -0,0 +1,5 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + testMatch: ['/src/**/?(*.)+(spec|test).ts?(x)'], +} diff --git a/packages/sdk/wallets/wallet-base/package.json b/packages/sdk/wallets/wallet-base/package.json index 5a71c68c8ac..818eeb6f958 100644 --- a/packages/sdk/wallets/wallet-base/package.json +++ b/packages/sdk/wallets/wallet-base/package.json @@ -1,6 +1,6 @@ { "name": "@celo/wallet-base", - "version": "4.1.1-dev", + "version": "5.1.0", "description": "Wallet base implementation", "author": "Celo", "license": "Apache-2.0", @@ -21,15 +21,22 @@ "lint": "tslint -c tslint.json --project .", "prepublishOnly": "yarn build" }, + "devDependencies": { + "viem": "~1.5.4" + }, "dependencies": { - "@celo/connect": "4.1.1-dev", - "@celo/base": "4.1.1-dev", - "@celo/utils": "4.1.1-dev", + "@celo/connect": "^5.1.0", + "@celo/base": "^5.0.5", + "@celo/utils": "^5.0.5", "@ethereumjs/util": "8.0.5", + "@ethereumjs/rlp": "^5.0.0", + "ethereum-cryptography": "^2.1.2", "@types/debug": "^4.1.5", "bignumber.js": "^9.0.0", + "web3-eth-accounts": "1.10.0", "debug": "^4.1.1", - "eth-lib": "^0.2.8" + "eth-lib": "^0.2.8", + "web3": "1.10.0" }, "engines": { "node": ">=8.14.2" diff --git a/packages/sdk/wallets/wallet-base/src/signing-utils.test.ts b/packages/sdk/wallets/wallet-base/src/signing-utils.test.ts new file mode 100644 index 00000000000..eb1882edce2 --- /dev/null +++ b/packages/sdk/wallets/wallet-base/src/signing-utils.test.ts @@ -0,0 +1,617 @@ +import { CeloTx } from '@celo/connect' +import { normalizeAddressWith0x, privateKeyToAddress } from '@celo/utils/lib/address' +import { parseTransaction, serializeTransaction } from 'viem' +import { privateKeyToAccount } from 'viem/accounts' +import { celo } from 'viem/chains' +import Web3 from 'web3' +import { + extractSignature, + getSignerFromTxEIP2718TX, + isPriceToLow, + recoverTransaction, + rlpEncodedTx, + stringNumberOrBNToHex, +} from './signing-utils' +const PRIVATE_KEY1 = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' +const ACCOUNT_ADDRESS1 = normalizeAddressWith0x(privateKeyToAddress(PRIVATE_KEY1)) as `0x${string}` + +describe('rlpEncodedTx', () => { + describe('legacy', () => { + const legacyTransaction = { + feeCurrency: '0x5409ED021D9299bf6814279A6A1411A7e866A631', + from: ACCOUNT_ADDRESS1, + to: ACCOUNT_ADDRESS1, + chainId: 2, + value: Web3.utils.toWei('1000', 'ether'), + nonce: 1, + gas: '1500000000', + gasPrice: '9900000000', + data: '0xabcdef', + } + it('convert CeloTx into RLP', () => { + const transaction = { + ...legacyTransaction, + } + const result = rlpEncodedTx(transaction) + expect(result).toMatchInlineSnapshot(` + { + "rlpEncode": "0xf8490185024e1603008459682f00945409ed021d9299bf6814279a6a1411a7e866a6318080941be31a94361a391bbafb2a4ccd704f57dc04d4bb893635c9adc5dea0000083abcdef028080", + "transaction": { + "chainId": 2, + "data": "0xabcdef", + "feeCurrency": "0x5409ed021d9299bf6814279a6a1411a7e866a631", + "from": "0x1be31a94361a391bbafb2a4ccd704f57dc04d4bb", + "gas": "0x59682f00", + "gasPrice": "0x024e160300", + "gatewayFee": "0x", + "gatewayFeeRecipient": "0x", + "maxFeePerGas": "0x", + "maxPriorityFeePerGas": "0x", + "nonce": 1, + "to": "0x1be31a94361a391bbafb2a4ccd704f57dc04d4bb", + "value": "0x3635c9adc5dea00000", + }, + "type": "celo-legacy", + } + `) + }) + + describe('when chainId / gasPrice / nonce is invalid', () => { + it('chainId is not a positive number it throws error', () => { + const transaction = { + ...legacyTransaction, + chainId: -1, + } + expect(() => rlpEncodedTx(transaction)).toThrowErrorMatchingInlineSnapshot( + `"Gas, nonce or chainId is less than than 0"` + ) + }) + it('gasPrice is not a positive number it throws error', () => { + const transaction = { + ...legacyTransaction, + gasPrice: -1, + } + expect(() => rlpEncodedTx(transaction)).toThrowErrorMatchingInlineSnapshot( + `"GasPrice or maxFeePerGas or maxPriorityFeePerGas is less than than 0"` + ) + }) + it('nonce is not a positive number it throws error', () => { + const transaction = { + ...legacyTransaction, + nonce: -1, + } + expect(() => rlpEncodedTx(transaction)).toThrowErrorMatchingInlineSnapshot( + `"Gas, nonce or chainId is less than than 0"` + ) + }) + it('gas is not a positive number it throws error', () => { + const transaction = { + ...legacyTransaction, + gas: -1, + } + expect(() => rlpEncodedTx(transaction)).toThrowErrorMatchingInlineSnapshot( + `"Gas, nonce or chainId is less than than 0"` + ) + }) + }) + }) + + describe('when no gas fields are provided', () => { + it('throws an error', () => { + expect(() => rlpEncodedTx({})).toThrowErrorMatchingInlineSnapshot(`""gas" is missing"`) + }) + }) + + describe('EIP1559 / CIP42', () => { + const eip1559Transaction: CeloTx = { + from: ACCOUNT_ADDRESS1, + to: ACCOUNT_ADDRESS1, + chainId: 2, + value: Web3.utils.toWei('1000', 'ether'), + nonce: 0, + maxFeePerGas: '10', + maxPriorityFeePerGas: '99', + gas: '99', + data: '0xabcdef', + } + + describe('when maxFeePerGas is to low', () => { + it('throws an error', () => { + const transaction = { + ...eip1559Transaction, + maxFeePerGas: Web3.utils.toBN('-5'), + } + expect(() => rlpEncodedTx(transaction)).toThrowErrorMatchingInlineSnapshot( + `"GasPrice or maxFeePerGas or maxPriorityFeePerGas is less than than 0"` + ) + }) + }) + describe('when maxPriorityFeePerGas is to low', () => { + it('throws an error', () => { + const transaction = { + ...eip1559Transaction, + maxPriorityFeePerGas: Web3.utils.toBN('-5'), + } + expect(() => rlpEncodedTx(transaction)).toThrowErrorMatchingInlineSnapshot( + `"GasPrice or maxFeePerGas or maxPriorityFeePerGas is less than than 0"` + ) + }) + }) + + describe('when maxFeePerGas and maxPriorityFeePerGas and feeCurrency are provided', () => { + it('orders fields in RLP as specified by CIP64', () => { + const CIP64Transaction = { + ...eip1559Transaction, + feeCurrency: '0x5409ED021D9299bf6814279A6A1411A7e866A631', + } + const result = rlpEncodedTx(CIP64Transaction) + expect(result).toMatchInlineSnapshot(` + { + "rlpEncode": "0x7bf83e0280630a63941be31a94361a391bbafb2a4ccd704f57dc04d4bb893635c9adc5dea0000083abcdefc0945409ed021d9299bf6814279a6a1411a7e866a631", + "transaction": { + "chainId": 2, + "data": "0xabcdef", + "feeCurrency": "0x5409ed021d9299bf6814279a6a1411a7e866a631", + "from": "0x1be31a94361a391bbafb2a4ccd704f57dc04d4bb", + "gas": "0x63", + "maxFeePerGas": "0x0a", + "maxPriorityFeePerGas": "0x63", + "nonce": 0, + "to": "0x1be31a94361a391bbafb2a4ccd704f57dc04d4bb", + "value": "0x3635c9adc5dea00000", + }, + "type": "cip64", + } + `) + }) + }) + + describe('when maxFeePerGas and maxPriorityFeePerGas are provided', () => { + it('orders fields in RLP as specified by EIP1559', () => { + const result = rlpEncodedTx(eip1559Transaction) + expect(result).toMatchInlineSnapshot(` + { + "rlpEncode": "0x02e90280630a63941be31a94361a391bbafb2a4ccd704f57dc04d4bb893635c9adc5dea0000083abcdefc0", + "transaction": { + "chainId": 2, + "data": "0xabcdef", + "from": "0x1be31a94361a391bbafb2a4ccd704f57dc04d4bb", + "gas": "0x63", + "maxFeePerGas": "0x0a", + "maxPriorityFeePerGas": "0x63", + "nonce": 0, + "to": "0x1be31a94361a391bbafb2a4ccd704f57dc04d4bb", + "value": "0x3635c9adc5dea00000", + }, + "type": "eip1559", + } + `) + }) + }) + }) + describe('compared to viem', () => { + it('matches output of viem serializeTransaction with accessList', () => { + const tx = { + type: 'eip1559' as const, + from: ACCOUNT_ADDRESS1, + to: ACCOUNT_ADDRESS1, + chainId: 2, + value: Web3.utils.toWei('1000', 'ether'), + nonce: 0, + maxFeePerGas: '1000', + maxPriorityFeePerGas: '99', + gas: '9900', + data: '0xabcdef' as const, + accessList: [ + { + address: '0x0000000000000000000000000000000000000000' as const, + storageKeys: [ + '0x0000000000000000000000000000000000000000000000000000000000000001' as const, + '0x60fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe' as const, + ], + }, + ], + } + const txViem = { + ...tx, + gas: BigInt(tx.gas), + maxFeePerGas: BigInt(tx.maxFeePerGas), + maxPriorityFeePerGas: BigInt(tx.maxPriorityFeePerGas), + value: BigInt(tx.value), + } + const viemSerialized = serializeTransaction(txViem) + const serialized = rlpEncodedTx(tx) + + const parsedCK = parseTransaction(serialized.rlpEncode) + const parsedViem = parseTransaction(viemSerialized) + expect(parsedCK).toEqual(parsedViem) + expect(serialized.rlpEncode).toEqual(viemSerialized) + }) + it('matches output of viem serializeTransaction without accessList', () => { + const tx = { + type: 'eip1559' as const, + from: ACCOUNT_ADDRESS1, + to: ACCOUNT_ADDRESS1, + chainId: 2, + value: Web3.utils.toWei('1000', 'ether'), + nonce: 0, + maxFeePerGas: '1000', + maxPriorityFeePerGas: '99', + gas: '9900', + data: '0xabcdef' as const, + } + const txViem = { + ...tx, + gas: BigInt(tx.gas), + maxFeePerGas: BigInt(tx.maxFeePerGas), + maxPriorityFeePerGas: BigInt(tx.maxPriorityFeePerGas), + value: BigInt(tx.value), + } + const viemSerialized = serializeTransaction(txViem) + const serialized = rlpEncodedTx(tx) + + const parsedCK = parseTransaction(serialized.rlpEncode) + const parsedViem = parseTransaction(viemSerialized) + expect(parsedCK).toEqual(parsedViem) + expect(serialized.rlpEncode).toEqual(viemSerialized) + }) + }) +}) + +function ckToViem(tx: CeloTx & { v?: number }) { + return { + ...tx, + gas: BigInt(tx.gas!), + maxFeePerGas: BigInt(tx.maxFeePerGas?.toString()!), + maxPriorityFeePerGas: BigInt(tx.maxPriorityFeePerGas?.toString()!), + value: BigInt(tx.value?.toString()!), + v: BigInt(tx.v?.toString()! === '0x' ? 0 : tx.v?.toString()!), + } +} + +describe('recoverTransaction', () => { + const ACCOUNT_ADDRESS1 = privateKeyToAddress(PRIVATE_KEY1) + describe('with EIP1559 transactions', () => { + test('ok', async () => { + const account = privateKeyToAccount(PRIVATE_KEY1) + const hash = await account.signTransaction({ + type: 'eip1559' as const, + from: ACCOUNT_ADDRESS1, + to: ACCOUNT_ADDRESS1 as `0x${string}`, + chainId: 2, + value: BigInt(1000), + nonce: 0, + maxFeePerGas: BigInt('1000'), + maxPriorityFeePerGas: BigInt('99'), + gas: BigInt('9900'), + data: '0xabcdef' as const, + }) + + const [transaction, signer] = recoverTransaction(hash) + expect(signer).toEqual(ACCOUNT_ADDRESS1) + expect(transaction).toMatchInlineSnapshot(` + { + "accessList": [], + "chainId": 2, + "data": "0xabcdef", + "gas": 9900, + "maxFeePerGas": 1000, + "maxPriorityFeePerGas": 99, + "nonce": 0, + "r": "0x04ddb2c87a6e0f77aa25da7439c72f978541f74fa1bd20becf2e109301d2f85c", + "s": "0x2d91eec5c0abca75d4df8322677bf43306e90172b77914494bbb7641b6dfc7e9", + "to": "0x1be31a94361a391bbafb2a4ccd704f57dc04d4bb", + "type": "eip1559", + "v": 28, + "value": 1000, + "yParity": 1, + } + `) + }) + + it('matches output of viem parseTransaction', () => { + const encodedByCK1559TX = + // from packages/sdk/wallets/wallet-local/src/local-wallet.test.ts:211 -- when calling signTransaction succeeds with eip1559 + '0x02f86d82ad5a8063630a94588e4b68193001e4d10928660ab4165b813717c0880de0b6b3a764000083abcdefc080a02c61b97c545c0a59732adbc497e944818da323a508930996383751d17e0b932ea015666dce65f074f12335ab78e1912f8b83fda75f05a002943459598712e6b17c' + const [transaction, signer] = recoverTransaction(encodedByCK1559TX) + const parsed = parseTransaction(encodedByCK1559TX) + + expect(ckToViem(transaction)).toMatchObject(parsed) + expect(signer).toMatchInlineSnapshot(`"0x1Be31A94361a391bBaFB2a4CCd704F57dc04d4bb"`) + }) + it('can recover (parse) transactions signed by viem', () => { + // stolen from viems's default eip1559 test result viem/src/accounts/utils/signTransaction.test.ts + const encodedByViem1559TX = + '0x02f850018203118080825208808080c080a04012522854168b27e5dc3d5839bab5e6b39e1a0ffd343901ce1622e3d64b48f1a04e00902ae0502c4728cbf12156290df99c3ed7de85b1dbfe20b5c36931733a33' + const recovered = recoverTransaction(encodedByViem1559TX) + expect(recovered).toMatchInlineSnapshot(` + [ + { + "accessList": [], + "chainId": 1, + "data": "0x", + "gas": 21000, + "maxFeePerGas": 0, + "maxPriorityFeePerGas": 0, + "nonce": 785, + "r": "0x4012522854168b27e5dc3d5839bab5e6b39e1a0ffd343901ce1622e3d64b48f1", + "s": "0x4e00902ae0502c4728cbf12156290df99c3ed7de85b1dbfe20b5c36931733a33", + "to": "0x", + "type": "eip1559", + "v": 27, + "value": 0, + "yParity": 0, + }, + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + ] + `) + }) + }) + it('handles celo-legacy transactions', () => { + const celoLegacyTx = + '0xf88480630a80941be31a94361a391bbafb2a4ccd704f57dc04d4bb82567894588e4b68193001e4d10928660ab4165b813717c0880de0b6b3a764000083abcdef83015ad8a09e121a99dc0832a9f4d1d71500b3c8a69a3c064d437c225d6292577ffcc45a71a02c5efa3c4b58953c35968e42d11d3882dacacf45402ee802824268b7cd60daff' + expect(recoverTransaction(celoLegacyTx)).toMatchInlineSnapshot(` + [ + { + "chainId": "0xad5a", + "data": "0xabcdef", + "feeCurrency": "0x", + "gas": 10, + "gasPrice": 99, + "gatewayFee": "0x5678", + "gatewayFeeRecipient": "0x1be31a94361a391bbafb2a4ccd704f57dc04d4bb", + "nonce": 0, + "to": "0x588e4b68193001e4d10928660ab4165b813717c0", + "type": "celo-legacy", + "value": "0x0de0b6b3a7640000", + }, + "0x1Be31A94361a391bBaFB2a4CCd704F57dc04d4bb", + ] + `) + }) + it('handles cip64 transactions', () => { + const cip64TX = + '0x7bf88282ad5a8063630a94588e4b68193001e4d10928660ab4165b813717c0880de0b6b3a764000083abcdefc094cd2a3d9f938e13cd947ec05abc7fe734df8dd82680a091b5504a59e529e7efa42dbb97fbc3311a91d035c873a94ab0789441fc989f84a02e8254d6b3101b63417e5d496833bc84f4832d4a8bf8a2b83e291d8f38c0f62d' + expect(recoverTransaction(cip64TX)).toMatchInlineSnapshot(` + [ + { + "accessList": [], + "chainId": 44378, + "data": "0xabcdef", + "feeCurrency": "0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826", + "gas": 10, + "maxFeePerGas": 99, + "maxPriorityFeePerGas": 99, + "nonce": 0, + "r": "0x91b5504a59e529e7efa42dbb97fbc3311a91d035c873a94ab0789441fc989f84", + "s": "0x2e8254d6b3101b63417e5d496833bc84f4832d4a8bf8a2b83e291d8f38c0f62d", + "to": "0x588e4b68193001e4d10928660ab4165b813717c0", + "type": "cip64", + "v": 27, + "value": 1000000000000000000, + "yParity": 0, + }, + "0x1Be31A94361a391bBaFB2a4CCd704F57dc04d4bb", + ] + `) + }) + it('handles cip42 transactions', () => { + const cip42TX = + '0x7cf89a82ad5a8063630a94cd2a3d9f938e13cd947ec05abc7fe734df8dd826941be31a94361a391bbafb2a4ccd704f57dc04d4bb82567894588e4b68193001e4d10928660ab4165b813717c0880de0b6b3a764000083abcdefc01ba0c610507b2ac3cff80dd7017419021196807d605efce0970c18cde48db33c27d1a01799477e0f601f554f0ee6f7ac21490602124801e9f7a99d9605249b90f03112' + expect(recoverTransaction(cip42TX)).toMatchInlineSnapshot(` + [ + { + "accessList": [], + "chainId": 44378, + "data": "0xabcdef", + "feeCurrency": "0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826", + "gas": 10, + "gatewayFee": "0x5678", + "gatewayFeeRecipient": "0x1be31a94361a391bbafb2a4ccd704f57dc04d4bb", + "maxFeePerGas": 99, + "maxPriorityFeePerGas": 99, + "nonce": 0, + "r": "0xc610507b2ac3cff80dd7017419021196807d605efce0970c18cde48db33c27d1", + "s": "0x1799477e0f601f554f0ee6f7ac21490602124801e9f7a99d9605249b90f03112", + "to": "0x588e4b68193001e4d10928660ab4165b813717c0", + "type": "cip42", + "v": 28, + "value": 1000000000000000000, + "yParity": 1, + }, + "0x90AB065B949165c47Acac34cA9A43171bBeBb1E1", + ] + `) + }) + test('cip42 serialized by viem', async () => { + const account = privateKeyToAccount(PRIVATE_KEY1) + const signed = await account.signTransaction( + { + // @ts-ignore -- types on viem dont quite work for setting the tx type but the actual js execution works fine + type: 'cip42', + accessList: [], + chainId: 44378, + data: '0xabcdef', + feeCurrency: '0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826', + gas: BigInt(10), + gatewayFee: BigInt('0x5678'), + gatewayFeeRecipient: '0x1be31a94361a391bbafb2a4ccd704f57dc04d4bb', + maxFeePerGas: BigInt(99), + maxPriorityFeePerGas: BigInt(99), + nonce: 0, + to: '0x588e4b68193001e4d10928660ab4165b813717c0', + value: BigInt(1000000000000000000), + }, + { serializer: celo.serializers?.transaction } + ) + + expect(recoverTransaction(signed)).toMatchInlineSnapshot(` + [ + { + "accessList": [], + "chainId": 44378, + "data": "0xabcdef", + "feeCurrency": "0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826", + "gas": 10, + "gatewayFee": "0x5678", + "gatewayFeeRecipient": "0x1be31a94361a391bbafb2a4ccd704f57dc04d4bb", + "maxFeePerGas": 99, + "maxPriorityFeePerGas": 99, + "nonce": 0, + "r": "0xc610507b2ac3cff80dd7017419021196807d605efce0970c18cde48db33c27d1", + "s": "0x1799477e0f601f554f0ee6f7ac21490602124801e9f7a99d9605249b90f03112", + "to": "0x588e4b68193001e4d10928660ab4165b813717c0", + "type": "cip42", + "v": 27, + "value": 1000000000000000000, + "yParity": 0, + }, + "0x1Be31A94361a391bBaFB2a4CCd704F57dc04d4bb", + ] + `) + expect(recoverTransaction(signed)[1]).toEqual(account.address) + }) +}) + +describe('isPriceToLow', () => { + test('maxFee and maxPriorityFee are positive', () => { + expect( + isPriceToLow({ + maxFeePerGas: 1_000_000_000, + maxPriorityFeePerGas: Web3.utils.toBN('50000000000000'), + gasPrice: undefined, + }) + ).toBe(false) + }) + test('gasPrice is positive', () => { + expect( + isPriceToLow({ + gasPrice: Web3.utils.toBN('50000000000000'), + }) + ).toBe(false) + }) + test('maxFeePerGas is less than 0 but maxPriorityFeePerGas is positive ', () => { + expect(() => + isPriceToLow({ + maxFeePerGas: -1, + maxPriorityFeePerGas: 1_000_000_000, + gasPrice: undefined, + }) + ).toThrowErrorMatchingInlineSnapshot( + `"GasPrice or maxFeePerGas or maxPriorityFeePerGas is less than than 0"` + ) + }) + test('maxPriorityFeePerGas is less than 0 but maxFeePerGas is positive ', () => { + expect(() => + isPriceToLow({ + maxFeePerGas: 1_000_000_000, + maxPriorityFeePerGas: -1_000_000_000, + gasPrice: undefined, + }) + ).toThrowErrorMatchingInlineSnapshot( + `"GasPrice or maxFeePerGas or maxPriorityFeePerGas is less than than 0"` + ) + }) + test('gasPrice is less than 0', () => { + expect(() => + isPriceToLow({ + maxFeePerGas: '0x', + maxPriorityFeePerGas: '0x', + gasPrice: -1, + }) + ).toThrowErrorMatchingInlineSnapshot( + `"GasPrice or maxFeePerGas or maxPriorityFeePerGas is less than than 0"` + ) + }) +}) + +describe('extractSignature', () => { + it('extracts from celo legacy txs', () => { + // packages/sdk/wallets/wallet-local/src/local-wallet.test.ts:176 (succeeds with legacy) + const extracted = extractSignature( + '0xf88480630a80941be31a94361a391bbafb2a4ccd704f57dc04d4bb82567894588e4b68193001e4d10928660ab4165b813717c0880de0b6b3a764000083abcdef83015ad8a09e121a99dc0832a9f4d1d71500b3c8a69a3c064d437c225d6292577ffcc45a71a02c5efa3c4b58953c35968e42d11d3882dacacf45402ee802824268b7cd60daff' + ) + expect(extracted).toMatchInlineSnapshot(` + { + "r": "0x9e121a99dc0832a9f4d1d71500b3c8a69a3c064d437c225d6292577ffcc45a71", + "s": "0x2c5efa3c4b58953c35968e42d11d3882dacacf45402ee802824268b7cd60daff", + "v": "0x015ad8", + } + `) + }) + it('extracts from cip42 txs', () => { + // packages/sdk/wallets/wallet-local/src/local-wallet.test.ts:274 (succeeds with cip42) + const extracted = extractSignature( + '0x7cf89a82ad5a8063630a94cd2a3d9f938e13cd947ec05abc7fe734df8dd826941be31a94361a391bbafb2a4ccd704f57dc04d4bb82567894588e4b68193001e4d10928660ab4165b813717c0880de0b6b3a764000083abcdefc01ba0c610507b2ac3cff80dd7017419021196807d605efce0970c18cde48db33c27d1a01799477e0f601f554f0ee6f7ac21490602124801e9f7a99d9605249b90f03112' + ) + expect(extracted).toMatchInlineSnapshot(` + { + "r": "0xc610507b2ac3cff80dd7017419021196807d605efce0970c18cde48db33c27d1", + "s": "0x1799477e0f601f554f0ee6f7ac21490602124801e9f7a99d9605249b90f03112", + "v": "0x1b", + } + `) + }) + it('extracts from eip1559 txs', () => { + // packages/sdk/wallets/wallet-local/src/local-wallet.test.ts:209 ( succeeds with eip1559) + const extracted = extractSignature( + '0x02f87082ad5a8063630a94588e4b68193001e4d10928660ab4165b813717c0880de0b6b3a764000083abcdef8083015ad7a00fd2d0c579a971ebc04207d8c5ff5bb3449052f0c9e946a7146e5ae4d4ec6289a0737423de64cc81a62e700b5ca7970212aaed3d28de4dbce8b054483d361f6ff8' + ) + expect(extracted).toMatchInlineSnapshot(` + { + "r": "0x0fd2d0c579a971ebc04207d8c5ff5bb3449052f0c9e946a7146e5ae4d4ec6289", + "s": "0x737423de64cc81a62e700b5ca7970212aaed3d28de4dbce8b054483d361f6ff8", + "v": "0x015ad7", + } + `) + }) + it('fails when length is wrong', () => { + expect(() => extractSignature('0x')).toThrowErrorMatchingInlineSnapshot( + `"@extractSignature: provided transaction has 0 elements but celo-legacy txs with a signature have 12 []"` + ) + }) +}) + +describe('getSignerFromTx', () => { + const account = privateKeyToAccount(PRIVATE_KEY1) + test('extracts signer address from cip42 tx signed by viem', async () => { + const signed = await account.signTransaction( + { + // @ts-ignore + type: 'cip42', + accessList: [], + chainId: 44378, + data: '0xabcdef', + feeCurrency: '0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826', + gas: BigInt(10), + gatewayFee: BigInt('0x5678'), + gatewayFeeRecipient: '0x1be31a94361a391bbafb2a4ccd704f57dc04d4bb', + maxFeePerGas: BigInt(99), + maxPriorityFeePerGas: BigInt(99), + nonce: 0, + to: '0x588e4b68193001e4d10928660ab4165b813717c0', + value: BigInt(1000000000000000000), + }, + { serializer: celo.serializers?.transaction } + ) + expect(getSignerFromTxEIP2718TX(signed)).toEqual(account.address) + }) +}) + +describe('stringNumberOrBNToHex', () => { + test('string as base 10 number', () => { + expect(stringNumberOrBNToHex('1230000000000020')).toEqual('0x045eadb112e014') + expect(stringNumberOrBNToHex('123')).toEqual('0x7b') + }) + test('string as base 16 number', () => { + expect(stringNumberOrBNToHex('0x7b')).toEqual('0x7b') + }) + test('number', () => { + expect(stringNumberOrBNToHex(1230000000000020)).toEqual('0x045eadb112e014') + expect(stringNumberOrBNToHex(123)).toEqual('0x7b') + }) + test('BN', () => { + const biggie = Web3.utils.toBN('123') + expect(stringNumberOrBNToHex(biggie)).toEqual('0x7b') + }) +}) diff --git a/packages/sdk/wallets/wallet-base/src/signing-utils.ts b/packages/sdk/wallets/wallet-base/src/signing-utils.ts index c711de1195b..3b2d2dac052 100644 --- a/packages/sdk/wallets/wallet-base/src/signing-utils.ts +++ b/packages/sdk/wallets/wallet-base/src/signing-utils.ts @@ -1,13 +1,40 @@ +// tslint:disable: ordered-imports import { ensureLeading0x, trimLeading0x } from '@celo/base/lib/address' -import { CeloTx, EncodedTransaction, RLPEncodedTx } from '@celo/connect' -import { inputCeloTxFormatter } from '@celo/connect/lib/utils/formatter' +import { + CeloTx, + CeloTxWithSig, + EncodedTransaction, + Hex, + RLPEncodedTx, + TransactionTypes, + isPresent, +} from '@celo/connect' +import { + hexToNumber, + inputCeloTxFormatter, + parseAccessList, +} from '@celo/connect/lib/utils/formatter' import { EIP712TypedData, generateTypedDataHash } from '@celo/utils/lib/sign-typed-data-utils' import { parseSignatureWithoutPrefix } from '@celo/utils/lib/signatureUtils' +// @ts-ignore-next-line import * as ethUtil from '@ethereumjs/util' import debugFactory from 'debug' // @ts-ignore-next-line eth-lib types not found import { account as Account, bytes as Bytes, hash as Hash, RLP } from 'eth-lib' +import { keccak256 } from 'ethereum-cryptography/keccak' +import { hexToBytes } from 'ethereum-cryptography/utils.js' +import Web3 from 'web3' // TODO try to do this without web3 direct +import Accounts from 'web3-eth-accounts' +const { + Address, + ecrecover, + fromRpcSig, + hashPersonalMessage, + pubToAddress, + toBuffer, + toChecksumAddress, +} = ethUtil const debug = debugFactory('wallet-base:tx:sign') // Original code taken from @@ -19,6 +46,8 @@ export const publicKeyPrefix: number = 0x04 export const sixtyFour: number = 64 export const thirtyTwo: number = 32 +const Y_PARITY_EIP_2098 = 27 + function isNullOrUndefined(value: any): boolean { return value === null || value === undefined } @@ -47,122 +76,360 @@ function makeEven(hex: string) { return hex } -function signatureFormatter(signature: { v: number; r: Buffer; s: Buffer }): { +function signatureFormatter( + signature: { v: number; r: Buffer; s: Buffer }, + type: TransactionTypes +): { v: string r: string s: string } { + let v = signature.v + if (type !== 'celo-legacy') { + v = signature.v === Y_PARITY_EIP_2098 ? 0 : 1 + } return { - v: stringNumberToHex(signature.v), + v: stringNumberToHex(v), r: makeEven(trimLeadingZero(ensureLeading0x(signature.r.toString('hex')))), s: makeEven(trimLeadingZero(ensureLeading0x(signature.s.toString('hex')))), } } -function stringNumberToHex(num?: number | string): string { +export function stringNumberOrBNToHex( + num?: number | string | ReturnType +): Hex { + if (typeof num === 'string' || typeof num === 'number' || num === undefined) { + return stringNumberToHex(num) + } else { + return makeEven(`0x` + num.toString(16)) as Hex + } +} +function stringNumberToHex(num?: number | string): Hex { const auxNumber = Number(num) if (num === '0x' || num === undefined || auxNumber === 0) { return '0x' } - return Bytes.fromNumber(auxNumber) + return makeEven(Web3.utils.numberToHex(num)) as Hex } - export function rlpEncodedTx(tx: CeloTx): RLPEncodedTx { + assertSerializableTX(tx) + const transaction = inputCeloTxFormatter(tx) + transaction.to = Bytes.fromNat(tx.to || '0x').toLowerCase() + transaction.nonce = Number(((tx.nonce as any) !== '0x' ? tx.nonce : 0) || 0) + transaction.data = Bytes.fromNat(tx.data || '0x').toLowerCase() + transaction.value = stringNumberOrBNToHex(tx.value) + transaction.gas = stringNumberOrBNToHex(tx.gas) + transaction.chainId = tx.chainId || 1 + // Celo Specific + transaction.feeCurrency = Bytes.fromNat(tx.feeCurrency || '0x').toLowerCase() + transaction.gatewayFeeRecipient = Bytes.fromNat(tx.gatewayFeeRecipient || '0x').toLowerCase() + transaction.gatewayFee = stringNumberOrBNToHex(tx.gatewayFee) + + // Legacy + transaction.gasPrice = stringNumberOrBNToHex(tx.gasPrice) + // EIP1559 / CIP42 + transaction.maxFeePerGas = stringNumberOrBNToHex(tx.maxFeePerGas) + transaction.maxPriorityFeePerGas = stringNumberOrBNToHex(tx.maxPriorityFeePerGas) + + let rlpEncode: Hex + if (isCIP64(tx)) { + // https://github.com/celo-org/celo-proposals/blob/master/CIPs/cip-0064.md + // 0x7b || rlp([chainId, nonce, maxPriorityFeePerGas, maxFeePerGas, gasLimit, to, value, data, accessList, feeCurrency, signatureYParity, signatureR, signatureS]). + rlpEncode = RLP.encode([ + stringNumberToHex(transaction.chainId), + stringNumberToHex(transaction.nonce), + transaction.maxPriorityFeePerGas || '0x', + transaction.maxFeePerGas || '0x', + transaction.gas || '0x', + transaction.to || '0x', + transaction.value || '0x', + transaction.data || '0x', + transaction.accessList || [], + transaction.feeCurrency || '0x', + ]) + delete transaction.gatewayFee + delete transaction.gatewayFeeRecipient + delete transaction.gasPrice + return { transaction, rlpEncode: concatHex([TxTypeToPrefix.cip64, rlpEncode]), type: 'cip64' } + } else if (isCIP42(tx)) { + // There shall be a typed transaction with the code 0x7c that has the following format: + // 0x7c || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, feecurrency, gatewayFeeRecipient, gatewayfee, destination, amount, data, access_list, signature_y_parity, signature_r, signature_s]). + // This will be in addition to the type 0x02 transaction as specified in EIP-1559. + rlpEncode = RLP.encode([ + stringNumberToHex(transaction.chainId), + stringNumberToHex(transaction.nonce), + transaction.maxPriorityFeePerGas || '0x', + transaction.maxFeePerGas || '0x', + transaction.gas || '0x', + transaction.feeCurrency || '0x', + transaction.gatewayFeeRecipient || '0x', + transaction.gatewayFee || '0x', + transaction.to || '0x', + transaction.value || '0x', + transaction.data || '0x', + transaction.accessList || [], + ]) + delete transaction.gasPrice + return { transaction, rlpEncode: concatHex([TxTypeToPrefix.cip42, rlpEncode]), type: 'cip42' } + } else if (isEIP1559(tx)) { + // https://eips.ethereum.org/EIPS/eip-1559 + // 0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list, signature_y_parity, signature_r, signature_s]). + rlpEncode = RLP.encode([ + stringNumberToHex(transaction.chainId), + stringNumberToHex(transaction.nonce), + transaction.maxPriorityFeePerGas || '0x', + transaction.maxFeePerGas || '0x', + transaction.gas || '0x', + transaction.to || '0x', + transaction.value || '0x', + transaction.data || '0x', + transaction.accessList || [], + ]) + delete transaction.feeCurrency + delete transaction.gatewayFee + delete transaction.gatewayFeeRecipient + delete transaction.gasPrice + return { + transaction, + rlpEncode: concatHex([TxTypeToPrefix.eip1559, rlpEncode]), + type: 'eip1559', + } + } else { + // This order should match the order in Geth. + // https://github.com/celo-org/celo-blockchain/blob/027dba2e4584936cc5a8e8993e4e27d28d5247b8/core/types/transaction.go#L65 + rlpEncode = RLP.encode([ + stringNumberToHex(transaction.nonce), + transaction.gasPrice, + transaction.gas, + transaction.feeCurrency, + transaction.gatewayFeeRecipient, + transaction.gatewayFee, + transaction.to, + transaction.value, + transaction.data, + stringNumberToHex(transaction.chainId), + '0x', + '0x', + ]) + return { transaction, rlpEncode, type: 'celo-legacy' } + } +} + +enum TxTypeToPrefix { + 'celo-legacy' = '', + cip42 = '0x7c', + cip64 = '0x7b', + eip1559 = '0x02', +} + +function concatTypePrefixHex( + rawTransaction: string, + txType: EncodedTransaction['tx']['type'] +): Hex { + const prefix = TxTypeToPrefix[txType] + if (prefix) { + return concatHex([prefix, rawTransaction]) + } + return rawTransaction as Hex +} + +function assertSerializableTX(tx: CeloTx) { if (!tx.gas) { throw new Error('"gas" is missing') } + // ensure at least gasPrice or maxFeePerGas and maxPriorityFeePerGas are set if ( - isNullOrUndefined(tx.chainId) || - isNullOrUndefined(tx.gasPrice) || - isNullOrUndefined(tx.nonce) + !isPresent(tx.gasPrice) && + (!isPresent(tx.maxFeePerGas) || !isPresent(tx.maxPriorityFeePerGas)) + ) { + throw new Error('"gasPrice" or "maxFeePerGas" and "maxPriorityFeePerGas" are missing') + } + + // ensure that gasPrice and maxFeePerGas are not set at the same time + if ( + isPresent(tx.gasPrice) && + (isPresent(tx.maxFeePerGas) || isPresent(tx.maxPriorityFeePerGas)) ) { throw new Error( - 'One of the values "chainId", "gasPrice", or "nonce" couldn\'t be fetched: ' + - JSON.stringify({ chainId: tx.chainId, gasPrice: tx.gasPrice, nonce: tx.nonce }) + 'when "maxFeePerGas" or "maxPriorityFeePerGas" are set, "gasPrice" must not be set' ) } - if (tx.nonce! < 0 || tx.gas! < 0 || tx.gasPrice! < 0 || tx.chainId! < 0) { - throw new Error('Gas, gasPrice, nonce or chainId is lower than 0') + if (isNullOrUndefined(tx.nonce) || isNullOrUndefined(tx.chainId)) { + throw new Error( + 'One of the values "chainId" or "nonce" couldn\'t be fetched: ' + + JSON.stringify({ chainId: tx.chainId, nonce: tx.nonce }) + ) } - const transaction: CeloTx = inputCeloTxFormatter(tx) - transaction.to = Bytes.fromNat(tx.to || '0x').toLowerCase() - transaction.nonce = Number(((tx.nonce as any) !== '0x' ? tx.nonce : 0) || 0) - transaction.data = Bytes.fromNat(tx.data || '0x').toLowerCase() - transaction.value = stringNumberToHex(tx.value?.toString()) - transaction.feeCurrency = Bytes.fromNat(tx.feeCurrency || '0x').toLowerCase() - transaction.gatewayFeeRecipient = Bytes.fromNat(tx.gatewayFeeRecipient || '0x').toLowerCase() - transaction.gatewayFee = stringNumberToHex(tx.gatewayFee) - transaction.gasPrice = stringNumberToHex(tx.gasPrice?.toString()) - transaction.gas = stringNumberToHex(tx.gas) - transaction.chainId = tx.chainId || 1 - // This order should match the order in Geth. - // https://github.com/celo-org/celo-blockchain/blob/027dba2e4584936cc5a8e8993e4e27d28d5247b8/core/types/transaction.go#L65 - const rlpEncode = RLP.encode([ - stringNumberToHex(transaction.nonce), - transaction.gasPrice, - transaction.gas, - transaction.feeCurrency, - transaction.gatewayFeeRecipient, - transaction.gatewayFee, - transaction.to, - transaction.value, - transaction.data, - stringNumberToHex(transaction.chainId), - '0x', - '0x', - ]) + if (isLessThanZero(tx.nonce) || isLessThanZero(tx.gas) || isLessThanZero(tx.chainId)) { + throw new Error('Gas, nonce or chainId is less than than 0') + } + isPriceToLow(tx) +} + +export function isPriceToLow(tx: CeloTx) { + const prices = [tx.gasPrice, tx.maxFeePerGas, tx.maxPriorityFeePerGas].filter( + (price) => price !== undefined + ) + const isLow = false + for (const price of prices) { + if (isLessThanZero(price)) { + throw new Error('GasPrice or maxFeePerGas or maxPriorityFeePerGas is less than than 0') + } + } + + return isLow +} + +function isEIP1559(tx: CeloTx): boolean { + return isPresent(tx.maxFeePerGas) && isPresent(tx.maxPriorityFeePerGas) +} + +function isCIP64(tx: CeloTx) { + return ( + isEIP1559(tx) && + isPresent(tx.feeCurrency) && + !isPresent(tx.gatewayFeeRecipient) && + !isPresent(tx.gatewayFeeRecipient) + ) +} + +function isCIP42(tx: CeloTx): boolean { + return ( + isEIP1559(tx) && + (isPresent(tx.feeCurrency) || isPresent(tx.gatewayFeeRecipient) || isPresent(tx.gatewayFee)) + ) +} - return { transaction, rlpEncode } +function concatHex(values: string[]): Hex { + return `0x${values.reduce((acc, x) => acc + x.replace('0x', ''), '')}` +} + +function isLessThanZero(value: CeloTx['gasPrice']) { + if (isNullOrUndefined(value)) { + return true + } + switch (typeof value) { + case 'string': + case 'number': + return Number(value) < 0 + default: + return value?.lt(Web3.utils.toBN(0)) || false + } } export async function encodeTransaction( rlpEncoded: RLPEncodedTx, signature: { v: number; r: Buffer; s: Buffer } ): Promise { - const sanitizedSignature = signatureFormatter(signature) + const sanitizedSignature = signatureFormatter(signature, rlpEncoded.type) const v = sanitizedSignature.v const r = sanitizedSignature.r const s = sanitizedSignature.s - const rawTx = RLP.decode(rlpEncoded.rlpEncode).slice(0, 9).concat([v, r, s]) + const decodedTX = prefixAwareRLPDecode(rlpEncoded.rlpEncode, rlpEncoded.type) + // for legacy tx we need to slice but for new ones we do not want to do that + const rawTx = (rlpEncoded.type === 'celo-legacy' ? decodedTX.slice(0, 9) : decodedTX).concat([ + v, + r, + s, + ]) - const rawTransaction = RLP.encode(rawTx) + // After signing, the transaction is encoded again and type prefix added + const rawTransaction = concatTypePrefixHex(RLP.encode(rawTx), rlpEncoded.type) const hash = getHashFromEncoded(rawTransaction) - const result: EncodedTransaction = { - tx: { - nonce: rlpEncoded.transaction.nonce!.toString(), - gasPrice: rlpEncoded.transaction.gasPrice!.toString(), - gas: rlpEncoded.transaction.gas!.toString(), - to: rlpEncoded.transaction.to!.toString(), - value: rlpEncoded.transaction.value!.toString(), - input: rlpEncoded.transaction.data!, + const baseTX = { + nonce: rlpEncoded.transaction.nonce!.toString(), + gas: rlpEncoded.transaction.gas!.toString(), + to: rlpEncoded.transaction.to!.toString(), + value: rlpEncoded.transaction.value!.toString(), + input: rlpEncoded.transaction.data!, + v, + r, + s, + hash, + } + let tx: Partial = baseTX + if (rlpEncoded.type === 'eip1559' || rlpEncoded.type === 'cip42') { + tx = { + ...tx, + // @ts-expect-error -- just a matter of how this tx is built + maxFeePerGas: rlpEncoded.transaction.maxFeePerGas!.toString(), + maxPriorityFeePerGas: rlpEncoded.transaction.maxPriorityFeePerGas!.toString(), + accessList: parseAccessList(rlpEncoded.transaction.accessList || []), + } + } + if (rlpEncoded.type === 'cip42' || rlpEncoded.type === 'celo-legacy') { + tx = { + ...tx, + // @ts-expect-error -- just a matter of how this tx is built feeCurrency: rlpEncoded.transaction.feeCurrency!.toString(), gatewayFeeRecipient: rlpEncoded.transaction.gatewayFeeRecipient!.toString(), gatewayFee: rlpEncoded.transaction.gatewayFee!.toString(), - v, - r, - s, - hash, - }, + } + } + if (rlpEncoded.type === 'celo-legacy') { + tx = { + ...tx, + // @ts-expect-error -- just a matter of how this tx is built + gasPrice: rlpEncoded.transaction.gasPrice!.toString(), + } + } + + const result: EncodedTransaction & { type: TransactionTypes } = { + tx: tx as EncodedTransaction['tx'], raw: rawTransaction, + type: rlpEncoded.type, } return result } +// new types have prefix but legacy does not +function prefixAwareRLPDecode(rlpEncode: string, type: TransactionTypes): string[] { + return type === 'celo-legacy' ? RLP.decode(rlpEncode) : RLP.decode(`0x${rlpEncode.slice(4)}`) +} + +function correctLengthOf(type: TransactionTypes, includeSig: boolean = true) { + switch (type) { + case 'cip64': { + return includeSig ? 13 : 10 + } + case 'cip42': + return includeSig ? 15 : 12 + case 'celo-legacy': + case 'eip1559': + return 12 + } +} +// Based on the return type of ensureLeading0x this was not a Buffer +export function extractSignature(rawTx: string) { + const type = determineTXType(rawTx) + const rawValues = prefixAwareRLPDecode(rawTx, type) + const length = rawValues.length + if (correctLengthOf(type) !== length) { + throw new Error( + `@extractSignature: provided transaction has ${length} elements but ${type} txs with a signature have ${correctLengthOf( + type + )} ${JSON.stringify(rawValues)}` + ) + } + return extractSignatureFromDecoded(rawValues) +} -export function extractSignature(rawTx: string): { v: number; r: Buffer; s: Buffer } { - const rawValues = RLP.decode(rawTx) - let r = rawValues[10] - let s = rawValues[11] +function extractSignatureFromDecoded(rawValues: string[]) { + // signature is always (for the tx we support so far) the last three elements of the array in order v, r, s, + const v = rawValues.at(-3) + let r = rawValues.at(-2) + let s = rawValues.at(-1) + // https://github.com/wagmi-dev/viem/blob/993321689b3e2220976504e7e170fe47731297ce/src/utils/transaction/parseTransaction.ts#L281 // Account.recover cannot handle canonicalized signatures // A canonicalized signature may have the first byte removed if its value is 0 - r = ensureLeading0x(trimLeading0x(r).padStart(64, '0')) - s = ensureLeading0x(trimLeading0x(s).padStart(64, '0')) + r = ensureLeading0x(trimLeading0x(r as string).padStart(64, '0')) + s = ensureLeading0x(trimLeading0x(s as string).padStart(64, '0')) return { - v: rawValues[9], + v, r, s, } @@ -171,44 +438,238 @@ export function extractSignature(rawTx: string): { v: number; r: Buffer; s: Buff // Recover transaction and sender address from a raw transaction. // This is used for testing. export function recoverTransaction(rawTx: string): [CeloTx, string] { - const rawValues = RLP.decode(rawTx) - debug('signing-utils@recoverTransaction: values are %s', rawValues) - const recovery = Bytes.toNumber(rawValues[9]) - // tslint:disable-next-line:no-bitwise - const chainId = Bytes.fromNumber((recovery - 35) >> 1) - const celoTx: CeloTx = { - nonce: rawValues[0].toLowerCase() === '0x' ? 0 : parseInt(rawValues[0], 16), - gasPrice: rawValues[1].toLowerCase() === '0x' ? 0 : parseInt(rawValues[1], 16), - gas: rawValues[2].toLowerCase() === '0x' ? 0 : parseInt(rawValues[2], 16), - feeCurrency: rawValues[3], - gatewayFeeRecipient: rawValues[4], - gatewayFee: rawValues[5], - to: rawValues[6], - value: rawValues[7], - data: rawValues[8], + if (!rawTx.startsWith('0x')) { + throw new Error('rawTx must start with 0x') + } + + switch (determineTXType(rawTx)) { + case 'cip64': + return recoverTransactionCIP64(rawTx as Hex) + case 'cip42': + return recoverTransactionCIP42(rawTx as Hex) + case 'eip1559': + return recoverTransactionEIP1559(rawTx as Hex) + default: + const rawValues = RLP.decode(rawTx) + debug('signing-utils@recoverTransaction: values are %s', rawValues) + const recovery = Bytes.toNumber(rawValues[9]) + // tslint:disable-next-line:no-bitwise + const chainId = Bytes.fromNumber((recovery - 35) >> 1) + const celoTx: CeloTx = { + type: 'celo-legacy', + nonce: rawValues[0].toLowerCase() === '0x' ? 0 : parseInt(rawValues[0], 16), + gasPrice: rawValues[1].toLowerCase() === '0x' ? 0 : parseInt(rawValues[1], 16), + gas: rawValues[2].toLowerCase() === '0x' ? 0 : parseInt(rawValues[2], 16), + feeCurrency: rawValues[3], + gatewayFeeRecipient: rawValues[4], + gatewayFee: rawValues[5], + to: rawValues[6], + value: rawValues[7], + data: rawValues[8], + chainId, + } + const { r, v, s } = extractSignatureFromDecoded(rawValues) + const signature = Account.encodeSignature([v, r, s]) + const extraData = recovery < 35 ? [] : [chainId, '0x', '0x'] + const signingData = rawValues.slice(0, 9).concat(extraData) + const signingDataHex = RLP.encode(signingData) + const signer = Account.recover(getHashFromEncoded(signingDataHex), signature) + return [celoTx, signer] + } +} + +// inspired by @ethereumjs/tx +function getPublicKeyofSignerFromTx(transactionArray: string[], type: TransactionTypes) { + // this needs to be 10 for cip64, 12 for cip42 and eip1559 + const base = transactionArray.slice(0, correctLengthOf(type, false)) + const message = concatHex([TxTypeToPrefix[type], RLP.encode(base).slice(2)]) + const msgHash = keccak256(hexToBytes(message)) + + const { v, r, s } = extractSignatureFromDecoded(transactionArray) + try { + return ecrecover( + toBuffer(msgHash), + v === '0x' || v === undefined ? BigInt(0) : BigInt(1), + toBuffer(r), + toBuffer(s) + ) + } catch (e: any) { + throw new Error(e) + } +} + +export function getSignerFromTxEIP2718TX(serializedTransaction: string): string { + const transactionArray: any[] = RLP.decode(`0x${serializedTransaction.slice(4)}`) + const signer = getPublicKeyofSignerFromTx( + transactionArray, + determineTXType(serializedTransaction) + ) + return toChecksumAddress(Address.fromPublicKey(signer).toString()) +} + +function determineTXType(serializedTransaction: string): TransactionTypes { + const prefix = serializedTransaction.slice(0, 4) + + if (prefix === TxTypeToPrefix.eip1559) { + return 'eip1559' + } else if (prefix === TxTypeToPrefix.cip42) { + return 'cip42' + } else if (prefix === TxTypeToPrefix.cip64) { + return 'cip64' + } + return 'celo-legacy' +} + +function vrsForRecovery(vRaw: string, r: string, s: string) { + const v = vRaw === '0x' || hexToNumber(vRaw) === 0 ? Y_PARITY_EIP_2098 : Y_PARITY_EIP_2098 + 1 + return { + v, + r, + s, + yParity: v === Y_PARITY_EIP_2098 ? 0 : 1, + } as const +} + +function recoverTransactionCIP42(serializedTransaction: Hex): [CeloTxWithSig, string] { + const transactionArray: any[] = prefixAwareRLPDecode(serializedTransaction, 'cip42') + debug('signing-utils@recoverTransactionCIP42: values are %s', transactionArray) + if (transactionArray.length !== 15 && transactionArray.length !== 12) { + throw new Error( + `Invalid transaction length for type CIP42: ${transactionArray.length} instead of 15 or 12. array: ${transactionArray}` + ) + } + const [ chainId, + nonce, + maxPriorityFeePerGas, + maxFeePerGas, + gas, + feeCurrency, + gatewayFeeRecipient, + gatewayFee, + to, + value, + data, + accessList, + vRaw, + r, + s, + ] = transactionArray + + const celoTX: CeloTxWithSig = { + type: 'cip42', + nonce: nonce.toLowerCase() === '0x' ? 0 : parseInt(nonce, 16), + maxPriorityFeePerGas: + maxPriorityFeePerGas.toLowerCase() === '0x' ? 0 : parseInt(maxPriorityFeePerGas, 16), + maxFeePerGas: maxFeePerGas.toLowerCase() === '0x' ? 0 : parseInt(maxFeePerGas, 16), + gas: gas.toLowerCase() === '0x' ? 0 : parseInt(gas, 16), + feeCurrency, + gatewayFeeRecipient, + gatewayFee, + to, + value: value.toLowerCase() === '0x' ? 0 : parseInt(value, 16), + data, + chainId: chainId.toLowerCase() === '0x' ? 0 : parseInt(chainId, 16), + accessList: parseAccessList(accessList), + ...vrsForRecovery(vRaw, r, s), } - let r = rawValues[10] - let s = rawValues[11] - // Account.recover cannot handle canonicalized signatures - // A canonicalized signature may have the first byte removed if its value is 0 - r = ensureLeading0x(trimLeading0x(r).padStart(64, '0')) - s = ensureLeading0x(trimLeading0x(s).padStart(64, '0')) - const signature = Account.encodeSignature([rawValues[9], r, s]) - const extraData = recovery < 35 ? [] : [chainId, '0x', '0x'] - const signingData = rawValues.slice(0, 9).concat(extraData) - const signingDataHex = RLP.encode(signingData) - const signer = Account.recover(getHashFromEncoded(signingDataHex), signature) + + const signer = + transactionArray.length === 15 ? getSignerFromTxEIP2718TX(serializedTransaction) : 'unsigned' + return [celoTX, signer] +} + +function recoverTransactionCIP64(serializedTransaction: Hex): [CeloTxWithSig, string] { + const transactionArray: any[] = prefixAwareRLPDecode(serializedTransaction, 'cip64') + debug('signing-utils@recoverTransactionCIP64: values are %s', transactionArray) + if (transactionArray.length !== 13 && transactionArray.length !== 10) { + throw new Error( + `Invalid transaction length for type CIP64: ${transactionArray.length} instead of 13 or 10. array: ${transactionArray}` + ) + } + const [ + chainId, + nonce, + maxPriorityFeePerGas, + maxFeePerGas, + gas, + to, + value, + data, + accessList, + feeCurrency, + vRaw, + r, + s, + ] = transactionArray + + const celoTX: CeloTxWithSig = { + type: 'cip64', + nonce: nonce.toLowerCase() === '0x' ? 0 : parseInt(nonce, 16), + maxPriorityFeePerGas: + maxPriorityFeePerGas.toLowerCase() === '0x' ? 0 : parseInt(maxPriorityFeePerGas, 16), + maxFeePerGas: maxFeePerGas.toLowerCase() === '0x' ? 0 : parseInt(maxFeePerGas, 16), + gas: gas.toLowerCase() === '0x' ? 0 : parseInt(gas, 16), + feeCurrency, + to, + value: value.toLowerCase() === '0x' ? 0 : parseInt(value, 16), + data, + chainId: chainId.toLowerCase() === '0x' ? 0 : parseInt(chainId, 16), + accessList: parseAccessList(accessList), + ...vrsForRecovery(vRaw, r, s), + } + + const signer = + transactionArray.length === 13 ? getSignerFromTxEIP2718TX(serializedTransaction) : 'unsigned' + return [celoTX, signer] +} + +function recoverTransactionEIP1559(serializedTransaction: Hex): [CeloTxWithSig, string] { + const transactionArray: any[] = prefixAwareRLPDecode(serializedTransaction, 'eip1559') + debug('signing-utils@recoverTransactionEIP1559: values are %s', transactionArray) + + const [ + chainId, + nonce, + maxPriorityFeePerGas, + maxFeePerGas, + gas, + to, + value, + data, + accessList, + vRaw, + r, + s, + ] = transactionArray + + const celoTx: CeloTxWithSig = { + type: 'eip1559', + nonce: nonce.toLowerCase() === '0x' ? 0 : parseInt(nonce, 16), + gas: gas.toLowerCase() === '0x' ? 0 : parseInt(gas, 16), + maxPriorityFeePerGas: + maxPriorityFeePerGas.toLowerCase() === '0x' ? 0 : parseInt(maxPriorityFeePerGas, 16), + maxFeePerGas: maxFeePerGas.toLowerCase() === '0x' ? 0 : parseInt(maxFeePerGas, 16), + to, + value: value.toLowerCase() === '0x' ? 0 : parseInt(value, 16), + data, + chainId: chainId.toLowerCase() === '0x' ? 0 : parseInt(chainId, 16), + accessList: parseAccessList(accessList), + ...vrsForRecovery(vRaw, r, s), + } + const web3Account = new Accounts() + const signer = web3Account.recoverTransaction(serializedTransaction) + return [celoTx, signer] } export function recoverMessageSigner(signingDataHex: string, signedData: string): string { - const dataBuff = ethUtil.toBuffer(signingDataHex) - const msgHashBuff = ethUtil.hashPersonalMessage(dataBuff) - const signature = ethUtil.fromRpcSig(signedData) + const dataBuff = toBuffer(signingDataHex) + const msgHashBuff = hashPersonalMessage(dataBuff) + const signature = fromRpcSig(signedData) - const publicKey = ethUtil.ecrecover(msgHashBuff, signature.v, signature.r, signature.s) - const address = ethUtil.pubToAddress(publicKey, true) + const publicKey = ecrecover(msgHashBuff, signature.v, signature.r, signature.s) + const address = pubToAddress(publicKey, true) return ensureLeading0x(address.toString('hex')) } @@ -236,9 +697,10 @@ export function verifySignatureWithoutPrefix( export function decodeSig(sig: any) { const [v, r, s] = Account.decodeSignature(sig) + return { v: parseInt(v, 16), - r: ethUtil.toBuffer(r) as Buffer, - s: ethUtil.toBuffer(s) as Buffer, + r: toBuffer(r) as Buffer, + s: toBuffer(s) as Buffer, } } diff --git a/packages/sdk/wallets/wallet-base/src/wallet-base.ts b/packages/sdk/wallets/wallet-base/src/wallet-base.ts index 9d0cd2575aa..acab8d7fab4 100644 --- a/packages/sdk/wallets/wallet-base/src/wallet-base.ts +++ b/packages/sdk/wallets/wallet-base/src/wallet-base.ts @@ -77,7 +77,8 @@ export abstract class WalletBase implements ReadOnlyWall throw new Error('No transaction object given!') } const rlpEncoded = rlpEncodedTx(txParams) - const addToV = chainIdTransformationForSigning(txParams.chainId!) + const addToV = + rlpEncoded.type === 'celo-legacy' ? chainIdTransformationForSigning(txParams.chainId!) : 27 // Get the signer from the 'from' field const fromAddress = txParams.from!.toString() diff --git a/packages/sdk/wallets/wallet-base/tsconfig.json b/packages/sdk/wallets/wallet-base/tsconfig.json index e6a754088ba..51debe8913d 100644 --- a/packages/sdk/wallets/wallet-base/tsconfig.json +++ b/packages/sdk/wallets/wallet-base/tsconfig.json @@ -5,5 +5,6 @@ "outDir": "lib" }, "include": ["src", "types"], + "exclude": ["**/*.test.ts"], "references": [{ "path": "../../utils" }] } diff --git a/packages/sdk/wallets/wallet-hsm-aws/CHANGELOG.md b/packages/sdk/wallets/wallet-hsm-aws/CHANGELOG.md new file mode 100644 index 00000000000..345cc2b805c --- /dev/null +++ b/packages/sdk/wallets/wallet-hsm-aws/CHANGELOG.md @@ -0,0 +1,25 @@ +# @celo/wallet-hsm-aws + +## 5.1.0 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- Updated dependencies [53bbd4958] +- Updated dependencies [53bbd4958] + - @celo/wallet-remote@5.1.0 + - @celo/wallet-base@5.1.0 + - @celo/wallet-hsm@5.1.0 + - @celo/utils@5.0.5 + +## 5.1.0-beta.0 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- Updated dependencies [53bbd4958] +- Updated dependencies [53bbd4958] + - @celo/wallet-remote@5.1.0-beta.0 + - @celo/wallet-base@5.1.0-beta.0 + - @celo/wallet-hsm@5.1.0-beta.0 + - @celo/utils@5.0.5-beta.0 diff --git a/packages/sdk/wallets/wallet-hsm-aws/package.json b/packages/sdk/wallets/wallet-hsm-aws/package.json index 954e2a90860..310d6ae9c5b 100644 --- a/packages/sdk/wallets/wallet-hsm-aws/package.json +++ b/packages/sdk/wallets/wallet-hsm-aws/package.json @@ -1,6 +1,6 @@ { "name": "@celo/wallet-hsm-aws", - "version": "4.1.1-dev", + "version": "5.1.0", "description": "AWS HSM wallet implementation", "author": "Celo", "license": "Apache-2.0", @@ -22,10 +22,10 @@ "prepublishOnly": "yarn build" }, "dependencies": { - "@celo/utils": "4.1.1-dev", - "@celo/wallet-base": "4.1.1-dev", - "@celo/wallet-remote": "4.1.1-dev", - "@celo/wallet-hsm": "4.1.1-dev", + "@celo/utils": "^5.0.5", + "@celo/wallet-base": "^5.1.0", + "@celo/wallet-remote": "^5.1.0", + "@celo/wallet-hsm": "^5.1.0", "@types/debug": "^4.1.5", "@types/secp256k1": "^4.0.0", "aws-sdk": "^2.705.0", @@ -36,8 +36,9 @@ "secp256k1": "^4.0.0" }, "devDependencies": { - "@celo/connect": "4.1.1-dev", + "@celo/connect": "^5.1.0", "elliptic": "^6.5.4", + "dotenv": "^8.2.0", "web3": "1.10.0" }, "engines": { diff --git a/packages/sdk/wallets/wallet-hsm-azure/CHANGELOG.md b/packages/sdk/wallets/wallet-hsm-azure/CHANGELOG.md new file mode 100644 index 00000000000..9773ee71e87 --- /dev/null +++ b/packages/sdk/wallets/wallet-hsm-azure/CHANGELOG.md @@ -0,0 +1,31 @@ +# @celo/wallet-hsm-azure + +## 5.1.0 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [53bbd4958] + - @celo/connect@5.1.0 + - @celo/wallet-remote@5.1.0 + - @celo/wallet-base@5.1.0 + - @celo/wallet-hsm@5.1.0 + - @celo/utils@5.0.5 + - @celo/base@5.0.5 + +## 5.1.0-beta.0 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [53bbd4958] + - @celo/connect@5.1.0-beta.0 + - @celo/wallet-remote@5.1.0-beta.0 + - @celo/wallet-base@5.1.0-beta.0 + - @celo/wallet-hsm@5.1.0-beta.0 + - @celo/utils@5.0.5-beta.0 + - @celo/base@5.0.5-beta.0 diff --git a/packages/sdk/wallets/wallet-hsm-azure/package.json b/packages/sdk/wallets/wallet-hsm-azure/package.json index a8d6a8d2bee..2c874e21711 100644 --- a/packages/sdk/wallets/wallet-hsm-azure/package.json +++ b/packages/sdk/wallets/wallet-hsm-azure/package.json @@ -1,6 +1,6 @@ { "name": "@celo/wallet-hsm-azure", - "version": "4.1.1-dev", + "version": "5.1.0", "description": "Azure HSM wallet implementation", "author": "Celo", "license": "Apache-2.0", @@ -25,11 +25,12 @@ "@azure/identity": "^1.1.0", "@azure/keyvault-keys": "^4.1.0", "@azure/keyvault-secrets": "^4.1.0", - "@celo/utils": "4.1.1-dev", - "@celo/wallet-base": "4.1.1-dev", - "@celo/wallet-remote": "4.1.1-dev", - "@celo/wallet-hsm": "4.1.1-dev", - "@celo/connect": "4.1.1-dev", + "@celo/base": "^5.0.5", + "@celo/utils": "^5.0.5", + "@celo/wallet-base": "^5.1.0", + "@celo/wallet-remote": "^5.1.0", + "@celo/wallet-hsm": "^5.1.0", + "@celo/connect": "^5.1.0", "@types/secp256k1": "^4.0.0", "eth-lib": "^0.2.8", "@ethereumjs/util": "8.0.5", diff --git a/packages/sdk/wallets/wallet-hsm-gcp/CHANGELOG.md b/packages/sdk/wallets/wallet-hsm-gcp/CHANGELOG.md new file mode 100644 index 00000000000..e948ed0bb13 --- /dev/null +++ b/packages/sdk/wallets/wallet-hsm-gcp/CHANGELOG.md @@ -0,0 +1,25 @@ +# @celo/wallet-hsm-gcp + +## 5.1.0 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- Updated dependencies [53bbd4958] +- Updated dependencies [53bbd4958] + - @celo/wallet-remote@5.1.0 + - @celo/wallet-base@5.1.0 + - @celo/wallet-hsm@5.1.0 + - @celo/utils@5.0.5 + +## 5.1.0-beta.0 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- Updated dependencies [53bbd4958] +- Updated dependencies [53bbd4958] + - @celo/wallet-remote@5.1.0-beta.0 + - @celo/wallet-base@5.1.0-beta.0 + - @celo/wallet-hsm@5.1.0-beta.0 + - @celo/utils@5.0.5-beta.0 diff --git a/packages/sdk/wallets/wallet-hsm-gcp/package.json b/packages/sdk/wallets/wallet-hsm-gcp/package.json index d7d5b15b37a..048a2158105 100644 --- a/packages/sdk/wallets/wallet-hsm-gcp/package.json +++ b/packages/sdk/wallets/wallet-hsm-gcp/package.json @@ -1,6 +1,6 @@ { "name": "@celo/wallet-hsm-gcp", - "version": "4.1.1-dev", + "version": "5.1.0", "description": "GCP HSM wallet implementation", "author": "Celo", "license": "Apache-2.0", @@ -20,10 +20,10 @@ "prepublishOnly": "yarn build" }, "dependencies": { - "@celo/utils": "4.1.1-dev", - "@celo/wallet-base": "4.1.1-dev", - "@celo/wallet-remote": "4.1.1-dev", - "@celo/wallet-hsm": "4.1.1-dev", + "@celo/utils": "^5.0.5", + "@celo/wallet-base": "^5.1.0", + "@celo/wallet-remote": "^5.1.0", + "@celo/wallet-hsm": "^5.1.0", "@google-cloud/kms": "~2.9.0", "@types/debug": "^4.1.5", "@types/secp256k1": "^4.0.0", @@ -34,8 +34,9 @@ "secp256k1": "^4.0.0" }, "devDependencies": { - "@celo/connect": "4.1.1-dev", + "@celo/connect": "^5.1.0", "elliptic": "^6.5.4", + "dotenv": "^8.2.0", "web3": "1.10.0" }, "engines": { diff --git a/packages/sdk/wallets/wallet-hsm/CHANGELOG.md b/packages/sdk/wallets/wallet-hsm/CHANGELOG.md new file mode 100644 index 00000000000..779d42057a7 --- /dev/null +++ b/packages/sdk/wallets/wallet-hsm/CHANGELOG.md @@ -0,0 +1,17 @@ +# @celo/wallet-hsm + +## 5.1.0 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- Updated dependencies [53bbd4958] + - @celo/base@5.0.5 + +## 5.1.0-beta.0 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- Updated dependencies [53bbd4958] + - @celo/base@5.0.5-beta.0 diff --git a/packages/sdk/wallets/wallet-hsm/package.json b/packages/sdk/wallets/wallet-hsm/package.json index bf368e57191..1c7c483d7c3 100644 --- a/packages/sdk/wallets/wallet-hsm/package.json +++ b/packages/sdk/wallets/wallet-hsm/package.json @@ -1,6 +1,6 @@ { "name": "@celo/wallet-hsm", - "version": "4.1.1-dev", + "version": "5.1.0", "description": "HSM wallet implementation utils", "author": "Celo", "license": "Apache-2.0", @@ -22,16 +22,20 @@ "prepublishOnly": "yarn build" }, "dependencies": { - "@celo/base": "4.1.1-dev", + "@celo/base": "^5.0.5", "@types/asn1js": "^0.0.2", "@types/secp256k1": "^4.0.0", "@types/debug": "^4.1.5", "eth-lib": "^0.2.8", "@ethereumjs/util": "8.0.5", "asn1js": "^2.0.26", + "bignumber.js": "^9.0.0", "elliptic": "^6.5.4", "secp256k1": "^4.0.0" }, + "devDependencies": { + "dotenv": "^8.2.0" + }, "engines": { "node": ">=8.14.2" } diff --git a/packages/sdk/wallets/wallet-ledger/CHANGELOG.md b/packages/sdk/wallets/wallet-ledger/CHANGELOG.md new file mode 100644 index 00000000000..3e2fdfc3d34 --- /dev/null +++ b/packages/sdk/wallets/wallet-ledger/CHANGELOG.md @@ -0,0 +1,29 @@ +# @celo/wallet-ledger + +## 5.1.0 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [53bbd4958] + - @celo/connect@5.1.0 + - @celo/wallet-remote@5.1.0 + - @celo/wallet-base@5.1.0 + - @celo/utils@5.0.5 + - @celo/base@5.0.5 + +## 5.1.0-beta.0 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [53bbd4958] + - @celo/connect@5.1.0-beta.0 + - @celo/wallet-remote@5.1.0-beta.0 + - @celo/wallet-base@5.1.0-beta.0 + - @celo/utils@5.0.5-beta.0 + - @celo/base@5.0.5-beta.0 diff --git a/packages/sdk/wallets/wallet-ledger/package.json b/packages/sdk/wallets/wallet-ledger/package.json index cc7b3c30354..5741d63248e 100644 --- a/packages/sdk/wallets/wallet-ledger/package.json +++ b/packages/sdk/wallets/wallet-ledger/package.json @@ -1,6 +1,6 @@ { "name": "@celo/wallet-ledger", - "version": "4.1.1-dev", + "version": "5.1.0", "description": "Ledger wallet implementation", "author": "Celo", "license": "Apache-2.0", @@ -22,17 +22,23 @@ "prepublishOnly": "yarn build" }, "dependencies": { - "@celo/utils": "4.1.1-dev", - "@celo/wallet-base": "4.1.1-dev", - "@celo/wallet-remote": "4.1.1-dev", - "@celo/connect": "4.1.1-dev", + "@celo/base": "^5.0.5", + "@celo/utils": "^5.0.5", + "@celo/wallet-base": "^5.1.0", + "@celo/wallet-remote": "^5.1.0", + "@celo/connect": "^5.1.0", "@ethereumjs/util": "8.0.5", "@ledgerhq/hw-app-eth": "~5.11.0", + "@ledgerhq/errors": "^5.50.0", "@ledgerhq/hw-transport": "~5.11.0", "debug": "^4.1.1", "eth-lib": "^0.2.8", "ethereum-cryptography": "1.2.0" }, + "devDependencies": { + "@ledgerhq/hw-transport-node-hid": "^6.27.4", + "web3": "1.10.0" + }, "engines": { "node": ">=8.14.2" } diff --git a/packages/sdk/wallets/wallet-ledger/src/ledger-wallet.ts b/packages/sdk/wallets/wallet-ledger/src/ledger-wallet.ts index 6c1d113c20c..0ac4d7aa5d4 100644 --- a/packages/sdk/wallets/wallet-ledger/src/ledger-wallet.ts +++ b/packages/sdk/wallets/wallet-ledger/src/ledger-wallet.ts @@ -1,6 +1,6 @@ import { CELO_DERIVATION_PATH_BASE } from '@celo/base/lib/account' import { zeroRange } from '@celo/base/lib/collections' -import { Address, ReadOnlyWallet } from '@celo/connect' +import { Address, CeloTx, EncodedTransaction, ReadOnlyWallet } from '@celo/connect' import { RemoteWallet } from '@celo/wallet-remote' import { TransportError, TransportStatusError } from '@ledgerhq/errors' import Ledger from '@ledgerhq/hw-app-eth' @@ -67,6 +67,19 @@ export class LedgerWallet extends RemoteWallet implements ReadOnly } } + signTransaction(txParams: CeloTx): Promise { + // CeloLedger does not support maxFeePerGas and maxPriorityFeePerGas yet + txParams.gasPrice = txParams.gasPrice ?? txParams.maxFeePerGas + if (txParams.maxFeePerGas || txParams.maxPriorityFeePerGas) { + console.info( + 'maxFeePerGas and maxPriorityFeePerGas are not supported on Ledger yet. Automatically using gasPrice instead.' + ) + delete txParams.maxFeePerGas + delete txParams.maxPriorityFeePerGas + } + return super.signTransaction(txParams) + } + protected async loadAccountSigners(): Promise> { if (!this.ledger) { this.ledger = this.generateNewLedger(this.transport) diff --git a/packages/sdk/wallets/wallet-local/CHANGELOG.md b/packages/sdk/wallets/wallet-local/CHANGELOG.md new file mode 100644 index 00000000000..86f4c2e57ed --- /dev/null +++ b/packages/sdk/wallets/wallet-local/CHANGELOG.md @@ -0,0 +1,33 @@ +# @celo/wallet-local + +## 5.1.0 + +### Minor Changes + +- 53bbd4958: Add cip64 support for feeCurrency Transactions. Note this is the replacement for the deprecated cip42 and legacy tx types https://github.com/celo-org/celo-proposals/blob/master/CIPs/cip/0064.md + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [53bbd4958] + - @celo/connect@5.1.0 + - @celo/wallet-base@5.1.0 + - @celo/utils@5.0.5 + +## 5.1.0-beta.0 + +### Minor Changes + +- 53bbd4958: Add cip64 support for feeCurrency Transactions. Note this is the replacement for the deprecated cip42 and legacy tx types https://github.com/celo-org/celo-proposals/blob/master/CIPs/cip/0064.md + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [53bbd4958] + - @celo/connect@5.1.0-beta.0 + - @celo/wallet-base@5.1.0-beta.0 + - @celo/utils@5.0.5-beta.0 diff --git a/packages/sdk/wallets/wallet-local/package.json b/packages/sdk/wallets/wallet-local/package.json index 146ae911bed..512c4d008cc 100644 --- a/packages/sdk/wallets/wallet-local/package.json +++ b/packages/sdk/wallets/wallet-local/package.json @@ -1,6 +1,6 @@ { "name": "@celo/wallet-local", - "version": "4.1.1-dev", + "version": "5.1.0", "description": "Local wallet implementation", "author": "Celo", "license": "Apache-2.0", @@ -17,19 +17,20 @@ "build": "tsc -b .", "clean": "tsc -b . --clean", "docs": "typedoc", - "test": "jest --runInBand", + "test": "jest", "lint": "tslint -c tslint.json --project .", "prepublishOnly": "yarn build" }, "dependencies": { - "@celo/utils": "4.1.1-dev", - "@celo/connect": "4.1.1-dev", - "@celo/wallet-base": "4.1.1-dev", + "@celo/utils": "^5.0.5", + "@celo/connect": "^5.1.0", + "@celo/wallet-base": "^5.1.0", "eth-lib": "^0.2.8", "@ethereumjs/util": "8.0.5" }, "devDependencies": { - "web3": "1.10.0" + "web3": "1.10.0", + "viem": "~1.5.4" }, "engines": { "node": ">=8.14.2" diff --git a/packages/sdk/wallets/wallet-local/src/local-wallet.test.ts b/packages/sdk/wallets/wallet-local/src/local-wallet.test.ts index 4f34b5e927a..692c7c9a277 100644 --- a/packages/sdk/wallets/wallet-local/src/local-wallet.test.ts +++ b/packages/sdk/wallets/wallet-local/src/local-wallet.test.ts @@ -1,4 +1,5 @@ -import { CeloTx, EncodedTransaction } from '@celo/connect' +import { StrongAddress } from '@celo/base/lib/address' +import { CeloTx, EncodedTransaction, Hex } from '@celo/connect' import { normalizeAddressWith0x, privateKeyToAddress, @@ -8,6 +9,8 @@ import { import { Encrypt } from '@celo/utils/lib/ecies' import { verifySignature } from '@celo/utils/lib/signatureUtils' import { recoverTransaction, verifyEIP712TypedDataSigner } from '@celo/wallet-base' +import { TransactionSerializableEIP1559, parseTransaction } from 'viem' +import { privateKeyToAccount } from 'viem/accounts' import Web3 from 'web3' import { LocalWallet } from './local-wallet' @@ -126,25 +129,37 @@ describe('Local wallet class', () => { }) test('fails calling signTransaction', async () => { - await expect(wallet.signTransaction(celoTransaction)).rejects.toThrowError() + await expect( + wallet.signTransaction(celoTransaction) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Could not find address 0x588e4b68193001e4d10928660ab4165b813717c0"` + ) }) test('fails calling signPersonalMessage', async () => { const hexStr: string = '0xa1' - await expect(wallet.signPersonalMessage(unknownAddress, hexStr)).rejects.toThrowError() + await expect( + wallet.signPersonalMessage(unknownAddress, hexStr) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Could not find address 0x588e4b68193001e4d10928660ab4165b813717c0"` + ) }) test('fails calling signTypedData', async () => { - await expect(wallet.signTypedData(unknownAddress, TYPED_DATA)).rejects.toThrowError() + await expect( + wallet.signTypedData(unknownAddress, TYPED_DATA) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Could not find address 0x588e4b68193001e4d10928660ab4165b813717c0"` + ) }) }) describe('using a known address', () => { describe('when calling signTransaction', () => { - let celoTransaction: CeloTx + let celoTransactionWithGasPrice: CeloTx beforeEach(() => { - celoTransaction = { + celoTransactionWithGasPrice = { from: knownAddress, to: otherAddress, chainId: CHAIN_ID, @@ -155,16 +170,176 @@ describe('Local wallet class', () => { feeCurrency: '0x', gatewayFeeRecipient: FEE_ADDRESS, gatewayFee: '0x5678', - data: '0xabcdef', + data: '0xabcdef' as const, } }) - test('succeeds', async () => { - await expect(wallet.signTransaction(celoTransaction)).resolves.not.toBeUndefined() + test('succeeds with legacy', async () => { + await expect(wallet.signTransaction(celoTransactionWithGasPrice)).resolves + .toMatchInlineSnapshot(` + { + "raw": "0xf88480630a80941be31a94361a391bbafb2a4ccd704f57dc04d4bb82567894588e4b68193001e4d10928660ab4165b813717c0880de0b6b3a764000083abcdef83015ad8a09e121a99dc0832a9f4d1d71500b3c8a69a3c064d437c225d6292577ffcc45a71a02c5efa3c4b58953c35968e42d11d3882dacacf45402ee802824268b7cd60daff", + "tx": { + "feeCurrency": "0x", + "gas": "0x0a", + "gasPrice": "0x63", + "gatewayFee": "0x5678", + "gatewayFeeRecipient": "0x1be31a94361a391bbafb2a4ccd704f57dc04d4bb", + "hash": "0xd24898ee3f68caa01fe065784453db7360bf783060fcbd18033f9d254ab8b082", + "input": "0xabcdef", + "nonce": "0", + "r": "0x9e121a99dc0832a9f4d1d71500b3c8a69a3c064d437c225d6292577ffcc45a71", + "s": "0x2c5efa3c4b58953c35968e42d11d3882dacacf45402ee802824268b7cd60daff", + "to": "0x588e4b68193001e4d10928660ab4165b813717c0", + "v": "0x015ad8", + "value": "0x0de0b6b3a7640000", + }, + "type": "celo-legacy", + } + `) + }) + + test('succeeds with eip1559', async () => { + const transaction1559 = { + ...celoTransactionWithGasPrice, + gasPrice: undefined, + feeCurrency: undefined, + maxFeePerGas: '99', + maxPriorityFeePerGas: '99', + } + await expect(wallet.signTransaction(transaction1559)).resolves.toMatchInlineSnapshot(` + { + "raw": "0x7cf88682ad5a8063630a80941be31a94361a391bbafb2a4ccd704f57dc04d4bb82567894588e4b68193001e4d10928660ab4165b813717c0880de0b6b3a764000083abcdefc001a0cfa1e1b30d1e4617ce80922d853c5e8b54b21f5ed6604438f90280ef2f0b7fd0a06fd8eee02fbdd421136fb45e6851ce72b5d87a2c06b2e136ef1a062df9256f4e", + "tx": { + "accessList": [], + "feeCurrency": "0x", + "gas": "0x0a", + "gatewayFee": "0x5678", + "gatewayFeeRecipient": "0x1be31a94361a391bbafb2a4ccd704f57dc04d4bb", + "hash": "0x29327536ba9901fde64b1b86882fd173517b41cd8bc8245e3761847d9b231c6d", + "input": "0xabcdef", + "maxFeePerGas": "0x63", + "maxPriorityFeePerGas": "0x63", + "nonce": "0", + "r": "0xcfa1e1b30d1e4617ce80922d853c5e8b54b21f5ed6604438f90280ef2f0b7fd0", + "s": "0x6fd8eee02fbdd421136fb45e6851ce72b5d87a2c06b2e136ef1a062df9256f4e", + "to": "0x588e4b68193001e4d10928660ab4165b813717c0", + "v": "0x01", + "value": "0x0de0b6b3a7640000", + }, + "type": "cip42", + } + `) + }) + + test('matches behavior of viem 1559', async () => { + const account = privateKeyToAccount(PRIVATE_KEY2) + const wallet2 = new LocalWallet() + // wallet 1 uses a private key that does not start with 0x which doesnt work for viem + wallet2.addAccount(PRIVATE_KEY2) + + const transaction1559 = { + ...celoTransactionWithGasPrice, + from: ACCOUNT_ADDRESS2, + to: otherAddress, + gasPrice: undefined, + feeCurrency: undefined, + gatewayFeeRecipient: undefined, + gatewayFee: undefined, + maxFeePerGas: '99', + maxPriorityFeePerGas: '99', + data: celoTransactionWithGasPrice.data as Hex, + } + const transaction1559Viem: TransactionSerializableEIP1559 = { + ...transaction1559, + type: 'eip1559', + gas: BigInt(transaction1559.gas as string), + to: transaction1559.to as StrongAddress, + value: BigInt(transaction1559.value as string), + maxFeePerGas: BigInt(transaction1559.maxFeePerGas as string), + maxPriorityFeePerGas: BigInt(transaction1559.maxPriorityFeePerGas as string), + accessList: undefined, + chainId: celoTransactionWithGasPrice.chainId as number, + } + const signedTransaction = await wallet2.signTransaction(transaction1559) + const viemSignedTransaction = await account.signTransaction(transaction1559Viem) + + expect(parseTransaction(signedTransaction.raw)).toEqual( + parseTransaction(viemSignedTransaction) + ) + expect(recoverTransaction(signedTransaction.raw)).toEqual( + recoverTransaction(viemSignedTransaction) + ) + expect(signedTransaction.raw).toEqual(viemSignedTransaction) + }) + test('succeeds with cip64', async () => { + const recoverTransactionCIP64 = { + ...celoTransactionWithGasPrice, + gasPrice: undefined, + gatewayFee: undefined, + gatewayFeeRecipient: undefined, + maxFeePerGas: '99', + maxPriorityFeePerGas: '99', + feeCurrency: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826', + } + await expect(wallet.signTransaction(recoverTransactionCIP64)).resolves + .toMatchInlineSnapshot(` + { + "raw": "0x7bf88282ad5a8063630a94588e4b68193001e4d10928660ab4165b813717c0880de0b6b3a764000083abcdefc094cd2a3d9f938e13cd947ec05abc7fe734df8dd82680a091b5504a59e529e7efa42dbb97fbc3311a91d035c873a94ab0789441fc989f84a02e8254d6b3101b63417e5d496833bc84f4832d4a8bf8a2b83e291d8f38c0f62d", + "tx": { + "gas": "0x0a", + "hash": "0x645afc1d19fe805c0c0956e70d5415487bf073741d7b297ccb7e7040c6ce5df6", + "input": "0xabcdef", + "nonce": "0", + "r": "0x91b5504a59e529e7efa42dbb97fbc3311a91d035c873a94ab0789441fc989f84", + "s": "0x2e8254d6b3101b63417e5d496833bc84f4832d4a8bf8a2b83e291d8f38c0f62d", + "to": "0x588e4b68193001e4d10928660ab4165b813717c0", + "v": "0x", + "value": "0x0de0b6b3a7640000", + }, + "type": "cip64", + } + `) + }) + + test('succeeds with cip42', async () => { + const transaction42 = { + ...celoTransactionWithGasPrice, + gasPrice: undefined, + maxFeePerGas: '99', + maxPriorityFeePerGas: '99', + gatewayFee: '0x5678', + feeCurrency: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826', + } + await expect(wallet.signTransaction(transaction42)).resolves.toMatchInlineSnapshot(` + { + "raw": "0x7cf89a82ad5a8063630a94cd2a3d9f938e13cd947ec05abc7fe734df8dd826941be31a94361a391bbafb2a4ccd704f57dc04d4bb82567894588e4b68193001e4d10928660ab4165b813717c0880de0b6b3a764000083abcdefc080a0c610507b2ac3cff80dd7017419021196807d605efce0970c18cde48db33c27d1a01799477e0f601f554f0ee6f7ac21490602124801e9f7a99d9605249b90f03112", + "tx": { + "accessList": [], + "feeCurrency": "0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826", + "gas": "0x0a", + "gatewayFee": "0x5678", + "gatewayFeeRecipient": "0x1be31a94361a391bbafb2a4ccd704f57dc04d4bb", + "hash": "0x7afcef8db391ff574b7f9c9205399b8ab094fc9fc8afbfb881204cbaaf093365", + "input": "0xabcdef", + "maxFeePerGas": "0x63", + "maxPriorityFeePerGas": "0x63", + "nonce": "0", + "r": "0xc610507b2ac3cff80dd7017419021196807d605efce0970c18cde48db33c27d1", + "s": "0x1799477e0f601f554f0ee6f7ac21490602124801e9f7a99d9605249b90f03112", + "to": "0x588e4b68193001e4d10928660ab4165b813717c0", + "v": "0x", + "value": "0x0de0b6b3a7640000", + }, + "type": "cip42", + } + `) }) test('with same signer', async () => { - const signedTx: EncodedTransaction = await wallet.signTransaction(celoTransaction) + const signedTx: EncodedTransaction = await wallet.signTransaction( + celoTransactionWithGasPrice + ) const [, recoveredSigner] = recoverTransaction(signedTx.raw) expect(normalizeAddressWith0x(recoveredSigner)).toBe( normalizeAddressWith0x(knownAddress) @@ -198,6 +373,109 @@ describe('Local wallet class', () => { ) }) }) + describe('when using signTransaction with type CIP42/64', () => { + let celoTransactionBase: CeloTx + let feeCurrency = '0x10c892a6ec43a53e45d0b916b4b7d383b1b78c0f' + let maxFeePerGas = '0x100000000' + let maxPriorityFeePerGas = '0x100000000' + + beforeEach(() => { + celoTransactionBase = { + gas: '1000000000', + from: knownAddress, + to: otherAddress, + chainId: CHAIN_ID, + value: Web3.utils.toWei('1', 'ether'), + nonce: 0, + data: '0xabcdef', + } + }) + describe('when feeCurrency and maxPriorityFeePerGas and maxFeePerGas are set but no gatewayfees', () => { + it('signs as a CIP64 tx', async () => { + const transaction: CeloTx = { + ...celoTransactionBase, + gatewayFee: undefined, + gatewayFeeRecipient: undefined, + feeCurrency, + maxFeePerGas, + maxPriorityFeePerGas, + } + const signedTx: EncodedTransaction = await wallet.signTransaction(transaction) + expect(signedTx.raw).toMatch(/^0x7b/) + }) + }) + describe('when feeCurrency and gatewayFee and maxPriorityFeePerGas and maxFeePerGas are set', () => { + it('signs as a CIP42 tx', async () => { + const transaction: CeloTx = { + ...celoTransactionBase, + gatewayFee: '0x1331', + gatewayFeeRecipient: FEE_ADDRESS, + feeCurrency, + maxFeePerGas, + maxPriorityFeePerGas, + } + const signedTx: EncodedTransaction = await wallet.signTransaction(transaction) + expect(signedTx.raw).toMatch(/^0x7c/) + }) + }) + describe('when feeCurrency and maxFeePerGas but not maxPriorityFeePerGas are set', () => { + it('throws error', async () => { + const transaction: CeloTx = { + ...celoTransactionBase, + feeCurrency, + maxFeePerGas, + maxPriorityFeePerGas: undefined, + } + expect(() => + wallet.signTransaction(transaction) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `""gasPrice" or "maxFeePerGas" and "maxPriorityFeePerGas" are missing"` + ) + }) + }) + + describe('when feeCurrency and maxPriorityFeePerGas but not maxFeePerGas are set', () => { + it('throws error', async () => { + const transaction: CeloTx = { + ...celoTransactionBase, + feeCurrency, + maxFeePerGas: undefined, + maxPriorityFeePerGas, + } + expect(() => + wallet.signTransaction(transaction) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `""gasPrice" or "maxFeePerGas" and "maxPriorityFeePerGas" are missing"` + ) + }) + }) + + describe('when gas and one of maxPriorityFeePerGas or maxFeePerGas are set', () => { + it('throws explaining only one kind of gas fee can be set', async () => { + const transaction: CeloTx = { + ...celoTransactionBase, + maxFeePerGas, + maxPriorityFeePerGas, + gasPrice: '0x100000000', + } + expect(async () => await wallet.signTransaction(transaction)).rejects.toThrowError( + 'when "maxFeePerGas" or "maxPriorityFeePerGas" are set, "gasPrice" must not be set' + ) + }) + }) + + describe('when maxPriorityFeePerGas / maxFeePerGas are set but not feeCurrency', () => { + it('signs as a EIP1559 tx', async () => { + const transaction: CeloTx = { + ...celoTransactionBase, + maxFeePerGas, + maxPriorityFeePerGas, + } + const signedTx: EncodedTransaction = await wallet.signTransaction(transaction) + expect(signedTx.raw).toMatch(/^0x02/) + }) + }) + }) describe('when calling signPersonalMessage', () => { test('succeeds', async () => { diff --git a/packages/sdk/wallets/wallet-local/src/signing.test.ts b/packages/sdk/wallets/wallet-local/src/signing.test.ts index ac867abb21e..ccd53ceb861 100644 --- a/packages/sdk/wallets/wallet-local/src/signing.test.ts +++ b/packages/sdk/wallets/wallet-local/src/signing.test.ts @@ -25,123 +25,11 @@ debug(`Account Address 1: ${ACCOUNT_ADDRESS1}`) debug(`Private key 2: ${PRIVATE_KEY2}`) debug(`Account Address 2: ${ACCOUNT_ADDRESS2}`) -async function verifyLocalSigning(web3: Web3, celoTransaction: CeloTx): Promise { - debug('Signer Testing using Account: %s', celoTransaction.from) - const signedTransaction = await web3.eth.signTransaction(celoTransaction) - debug('Singer Testing: Signed transaction %o', signedTransaction) - const rawTransaction: string = signedTransaction.raw - const [signedCeloTransaction, recoveredSigner] = recoverTransaction(rawTransaction) - debug( - 'Transaction was signed by "%s", recovered signer is "%s"', - celoTransaction.from, - recoveredSigner - ) - expect(recoveredSigner.toLowerCase()).toEqual(celoTransaction.from!.toString().toLowerCase()) - - if (celoTransaction.nonce != null) { - debug( - 'Checking nonce actual: %o expected: %o', - signedCeloTransaction.nonce, - parseInt(celoTransaction.nonce.toString(), 16) - ) - expect(signedCeloTransaction.nonce).toEqual(parseInt(celoTransaction.nonce.toString(), 16)) - } - if (celoTransaction.gas != null) { - debug( - 'Checking gas actual %o expected %o', - signedCeloTransaction.gas, - parseInt(celoTransaction.gas.toString(), 16) - ) - expect(signedCeloTransaction.gas).toEqual(parseInt(celoTransaction.gas.toString(), 16)) - } - if (celoTransaction.gasPrice != null) { - debug( - 'Checking gas price actual %o expected %o', - signedCeloTransaction.gasPrice, - parseInt(celoTransaction.gasPrice.toString(), 16) - ) - expect(signedCeloTransaction.gasPrice).toEqual( - parseInt(celoTransaction.gasPrice.toString(), 16) - ) - } - if (celoTransaction.feeCurrency != null) { - debug( - 'Checking fee currency actual %o expected %o', - signedCeloTransaction.feeCurrency, - celoTransaction.feeCurrency - ) - expect(signedCeloTransaction.feeCurrency!.toLowerCase()).toEqual( - celoTransaction.feeCurrency.toLowerCase() - ) - } - if (celoTransaction.gatewayFeeRecipient != null) { - debug( - 'Checking gateway fee recipient actual ' + - `${signedCeloTransaction.gatewayFeeRecipient} expected ${celoTransaction.gatewayFeeRecipient}` - ) - expect(signedCeloTransaction.gatewayFeeRecipient!.toLowerCase()).toEqual( - celoTransaction.gatewayFeeRecipient.toLowerCase() - ) - } - if (celoTransaction.gatewayFee != null) { - debug( - 'Checking gateway fee value actual %o expected %o', - signedCeloTransaction.gatewayFee, - celoTransaction.gatewayFee.toString() - ) - expect(signedCeloTransaction.gatewayFee).toEqual(celoTransaction.gatewayFee.toString()) - } - if (celoTransaction.data != null) { - debug(`Checking data actual ${signedCeloTransaction.data} expected ${celoTransaction.data}`) - expect(signedCeloTransaction.data!.toLowerCase()).toEqual(celoTransaction.data.toLowerCase()) - } -} - -async function verifyLocalSigningInAllPermutations( - web3: Web3, - from: string, - to: string -): Promise { - const amountInWei: string = Web3.utils.toWei('1', 'ether') - const nonce = 0 - const badNonce = 100 - const gas = 10 - const gasPrice = 99 - const feeCurrency = ACCOUNT_ADDRESS1 - const gatewayFeeRecipient = ACCOUNT_ADDRESS2 - const gatewayFee = '0x5678' - const data = '0xabcdef' - const chainId = 1 - - // tslint:disable:no-bitwise - // Test all possible combinations for rigor. - for (let i = 0; i < 16; i++) { - const celoTransaction: CeloTx = { - from, - to, - value: amountInWei, - nonce, - gasPrice, - chainId, - gas, - feeCurrency: i & 1 ? feeCurrency : undefined, - gatewayFeeRecipient: i & 2 ? gatewayFeeRecipient : undefined, - gatewayFee: i & 4 ? gatewayFee : undefined, - data: i & 8 ? data : undefined, - } - await verifyLocalSigning(web3, celoTransaction) - } - // tslint:enable:no-bitwise - - // A special case. - // An incorrect nonce will only work, if no implict calls to estimate gas are required. - await verifyLocalSigning(web3, { from, to, nonce: badNonce, gas, gasPrice, chainId }) -} - // These tests verify the signTransaction WITHOUT the ParamsPopulator describe('Transaction Utils', () => { // only needed for the eth_coinbase rcp call let connection: Connection + let web3: Web3 const mockProvider: Provider = { send: (payload: JsonRpcPayload, callback: Callback): void => { if (payload.method === 'eth_coinbase') { @@ -151,44 +39,213 @@ describe('Transaction Utils', () => { result: '0xc94770007dda54cF92009BFF0dE90c06F603a09f', } callback(null, response) + } else if (payload.method === 'eth_gasPrice') { + const response: JsonRpcResponse = { + jsonrpc: payload.jsonrpc, + id: Number(payload.id), + result: '0x09184e72a000', + } + callback(null, response) } else { callback(new Error(payload.method)) } }, } - const web3: Web3 = new Web3() - beforeEach(() => { + const setupConnection = async () => { + web3 = new Web3() web3.setProvider(mockProvider as any) connection = new Connection(web3) connection.wallet = new LocalWallet() - }) + } + async function verifyLocalSigning(celoTransaction: CeloTx): Promise { + let recoveredSigner: string | undefined + let recoveredTransaction: CeloTx | undefined + let signedTransaction: { raw: string; tx: any } | undefined + beforeAll(async () => { + signedTransaction = await web3.eth.signTransaction(celoTransaction) + const recovery = recoverTransaction(signedTransaction.raw) + recoveredTransaction = recovery[0] + recoveredSigner = recovery[1] + }) - afterEach(() => { - connection.stop() - }) + afterAll(async () => { + signedTransaction = undefined + recoveredTransaction = undefined + recoveredSigner = undefined + }) + + test('Signer matches recovered signer', async () => { + expect(recoveredSigner?.toLowerCase()).toEqual(celoTransaction.from!.toString().toLowerCase()) + }) + + test('Checking nonce', async () => { + if (celoTransaction.nonce != null) { + expect(recoveredTransaction?.nonce).toEqual(parseInt(celoTransaction.nonce.toString(), 16)) + } + }) + + test('Checking gas', async () => { + if (celoTransaction.gas != null) { + expect(recoveredTransaction?.gas).toEqual(parseInt(celoTransaction.gas.toString(), 16)) + } + }) + test('Checking gas price', async () => { + if (celoTransaction.gasPrice != null) { + expect(recoveredTransaction?.gasPrice).toEqual( + parseInt(celoTransaction.gasPrice.toString(), 16) + ) + } + }) + test('Checking maxFeePerGas', async () => { + if (celoTransaction.maxFeePerGas != null) { + expect(recoveredTransaction?.maxFeePerGas).toEqual( + parseInt(celoTransaction.maxFeePerGas.toString(), 16) + ) + } + }) + test('Checking maxPriorityFeePerGas', async () => { + if (celoTransaction.maxPriorityFeePerGas != null) { + expect(recoveredTransaction?.maxPriorityFeePerGas).toEqual( + parseInt(celoTransaction.maxPriorityFeePerGas.toString(), 16) + ) + } + }) + test('Checking feeCurrency', async () => { + if (celoTransaction.feeCurrency != null) { + expect(recoveredTransaction?.feeCurrency!.toLowerCase()).toEqual( + celoTransaction.feeCurrency.toLowerCase() + ) + } + }) + test('gatewayFeeRecipient', async () => { + if ( + celoTransaction.gatewayFeeRecipient !== undefined && + celoTransaction.gatewayFeeRecipient !== null + ) { + expect(recoveredTransaction?.gatewayFeeRecipient?.toLowerCase()).toEqual( + celoTransaction.gatewayFeeRecipient.toLowerCase() + ) + } + }) + test('Checking gateway fee value', async () => { + if (celoTransaction.gatewayFee !== undefined && celoTransaction.gatewayFee !== null) { + expect(recoveredTransaction?.gatewayFee).toEqual(celoTransaction.gatewayFee.toString()) + } + }) + test('Checking data', async () => { + if (celoTransaction.data != null) { + expect(recoveredTransaction?.data!.toLowerCase()).toEqual( + celoTransaction.data.toLowerCase() + ) + } + }) + } + + async function verifyLocalSigningInAllPermutations(from: string, to: string): Promise { + const amountInWei: string = Web3.utils.toWei('1', 'ether') + const nonce = 0 + const badNonce = 100 + const gas = 10000 + const gasPrice = 99000000000 + const feeCurrency = ACCOUNT_ADDRESS1 + const gatewayFeeRecipient = ACCOUNT_ADDRESS2 + const gatewayFee = '0x5678' + const data = '0xabcdef' + const chainId = 1 + + // tslint:disable:no-bitwise + // Test all possible combinations for rigor. + for (let i = 0; i < 16; i++) { + const celoTransaction: CeloTx = { + from, + to, + value: amountInWei, + nonce, + gasPrice: i % 2 === 0 ? gasPrice : undefined, + maxFeePerGas: i % 2 === 1 ? gasPrice : undefined, + maxPriorityFeePerGas: i % 2 === 1 ? gasPrice : undefined, + chainId, + gas, + feeCurrency: i % 3 === 0 ? feeCurrency : undefined, + gatewayFeeRecipient: i % 7 === 0 ? gatewayFeeRecipient : undefined, + gatewayFee: i % 7 === 0 ? gatewayFee : undefined, + data: i & 8 ? data : undefined, + } + describe(transactionDescription(celoTransaction), () => { + verifyLocalSigning(celoTransaction) + }) + } + + function transactionDescription(celoTransaction: CeloTx) { + const description: string[] = [] + if (celoTransaction.gasPrice != undefined) { + description.push(`Testing Legacy with gas price ${celoTransaction.gasPrice}`) + } else if ( + celoTransaction.gatewayFeeRecipient !== undefined || + celoTransaction.gatewayFee !== undefined + ) { + description.push('Testing CIP42 with') + } else if (celoTransaction.feeCurrency != undefined) { + description.push('Testing CIP64 with') + } else { + description.push(`Testing EIP1559 with maxFeePerGas ${celoTransaction.maxFeePerGas}`) + } + if (celoTransaction.data != undefined) { + description.push(`data: ${celoTransaction.data}`) + } + + if (celoTransaction.feeCurrency != undefined) { + description.push(`fee currency: ${celoTransaction.feeCurrency}`) + } + + if (celoTransaction.gatewayFeeRecipient != undefined) { + description.push(`gateway fee recipient: ${celoTransaction.gatewayFeeRecipient}`) + } + if (celoTransaction.gatewayFee != undefined) { + description.push(`gateway fee: ${celoTransaction.gatewayFee}`) + } + + return description.join(' ') + } + + // A special case. + // An incorrect nonce will only work, if no implicit calls to estimate gas are required. + describe('Testing with bad nonce', () => { + verifyLocalSigning({ from, to, nonce: badNonce, gas, gasPrice, chainId }) + }) + } describe('Signer Testing with single local account and pay gas in CELO', () => { - it('Test1 should be able to sign and get the signer back with single local account', async () => { - jest.setTimeout(60 * 1000) - connection.addAccount(PRIVATE_KEY1) - await verifyLocalSigningInAllPermutations(web3, ACCOUNT_ADDRESS1, ACCOUNT_ADDRESS2) + describe('Test1 should be able to sign and get the signer back with single local account', () => { + beforeAll(async () => { + await setupConnection() + connection.addAccount(PRIVATE_KEY1) + }) + verifyLocalSigningInAllPermutations(ACCOUNT_ADDRESS1, ACCOUNT_ADDRESS2) + afterAll(() => connection.stop()) }) }) describe('Signer Testing with multiple local accounts', () => { - it('Test2 should be able to sign with first account and get the signer back with multiple local accounts', async () => { - jest.setTimeout(60 * 1000) - connection.addAccount(PRIVATE_KEY1) - connection.addAccount(PRIVATE_KEY2) - await verifyLocalSigningInAllPermutations(web3, ACCOUNT_ADDRESS1, ACCOUNT_ADDRESS2) - }) - - it('Test3 should be able to sign with second account and get the signer back with multiple local accounts', async () => { - jest.setTimeout(60 * 1000) - connection.addAccount(PRIVATE_KEY1) - connection.addAccount(PRIVATE_KEY2) - await verifyLocalSigningInAllPermutations(web3, ACCOUNT_ADDRESS2, ACCOUNT_ADDRESS1) + describe('Test2 should be able to sign with first account and get the signer back with multiple local accounts', () => { + beforeAll(async () => { + await setupConnection() + connection.addAccount(PRIVATE_KEY1) + connection.addAccount(PRIVATE_KEY2) + }) + verifyLocalSigningInAllPermutations(ACCOUNT_ADDRESS1, ACCOUNT_ADDRESS2) + afterAll(() => connection.stop()) + }) + + describe('Test3 should be able to sign with second account and get the signer back with multiple local accounts', () => { + beforeAll(async () => { + await setupConnection() + connection.addAccount(PRIVATE_KEY1) + connection.addAccount(PRIVATE_KEY2) + }) + verifyLocalSigningInAllPermutations(ACCOUNT_ADDRESS2, ACCOUNT_ADDRESS1) + afterAll(() => connection.stop()) }) }) }) diff --git a/packages/sdk/wallets/wallet-local/tsconfig.json b/packages/sdk/wallets/wallet-local/tsconfig.json index e267672350c..b37d430f5cc 100644 --- a/packages/sdk/wallets/wallet-local/tsconfig.json +++ b/packages/sdk/wallets/wallet-local/tsconfig.json @@ -5,5 +5,6 @@ "outDir": "lib" }, "include": ["src"], + "exclude": ["**/*.test.ts"], "references": [{ "path": "../../utils" }] } diff --git a/packages/sdk/wallets/wallet-remote/CHANGELOG.md b/packages/sdk/wallets/wallet-remote/CHANGELOG.md new file mode 100644 index 00000000000..fb3931c85f7 --- /dev/null +++ b/packages/sdk/wallets/wallet-remote/CHANGELOG.md @@ -0,0 +1,25 @@ +# @celo/wallet-remote + +## 5.1.0 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [53bbd4958] + - @celo/connect@5.1.0 + - @celo/wallet-base@5.1.0 + - @celo/utils@5.0.5 + +## 5.1.0-beta.0 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [53bbd4958] + - @celo/connect@5.1.0-beta.0 + - @celo/wallet-base@5.1.0-beta.0 + - @celo/utils@5.0.5-beta.0 diff --git a/packages/sdk/wallets/wallet-remote/package.json b/packages/sdk/wallets/wallet-remote/package.json index aaed4419f06..259be783025 100644 --- a/packages/sdk/wallets/wallet-remote/package.json +++ b/packages/sdk/wallets/wallet-remote/package.json @@ -1,6 +1,6 @@ { "name": "@celo/wallet-remote", - "version": "4.1.1-dev", + "version": "5.1.0", "description": "Remote wallet implementation", "author": "Celo", "license": "Apache-2.0", @@ -22,14 +22,16 @@ "prepublishOnly": "yarn build" }, "dependencies": { - "@celo/connect": "4.1.1-dev", - "@celo/utils": "4.1.1-dev", - "@celo/wallet-base": "4.1.1-dev", + "@celo/connect": "^5.1.0", + "@celo/utils": "^5.0.5", + "@celo/wallet-base": "^5.1.0", "@ethereumjs/util": "8.0.5", "@types/debug": "^4.1.5", "eth-lib": "^0.2.8" }, - "devDependencies": {}, + "devDependencies": { + "web3": "1.10.0" + }, "engines": { "node": ">=8.14.2" } diff --git a/packages/sdk/wallets/wallet-rpc/CHANGELOG.md b/packages/sdk/wallets/wallet-rpc/CHANGELOG.md new file mode 100644 index 00000000000..9a978a5ef52 --- /dev/null +++ b/packages/sdk/wallets/wallet-rpc/CHANGELOG.md @@ -0,0 +1,29 @@ +# @celo/wallet-rpc + +## 5.1.0 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [53bbd4958] + - @celo/connect@5.1.0 + - @celo/wallet-remote@5.1.0 + - @celo/wallet-base@5.1.0 + - @celo/utils@5.0.5 + - @celo/base@5.0.5 + +## 5.1.0-beta.0 + +### Patch Changes + +- 53bbd4958: Note celo sdk packages will no longer be fix bumped (ie will not share the same version always) and will now use ^range when depending on each other +- Updated dependencies [d48c68afc] +- Updated dependencies [53bbd4958] +- Updated dependencies [53bbd4958] + - @celo/connect@5.1.0-beta.0 + - @celo/wallet-remote@5.1.0-beta.0 + - @celo/wallet-base@5.1.0-beta.0 + - @celo/utils@5.0.5-beta.0 + - @celo/base@5.0.5-beta.0 diff --git a/packages/sdk/wallets/wallet-rpc/package.json b/packages/sdk/wallets/wallet-rpc/package.json index 8858aad8877..127ccbd6921 100644 --- a/packages/sdk/wallets/wallet-rpc/package.json +++ b/packages/sdk/wallets/wallet-rpc/package.json @@ -1,6 +1,6 @@ { "name": "@celo/wallet-rpc", - "version": "4.1.1-dev", + "version": "5.1.0", "description": "Geth RPC wallet implementation", "author": "Celo", "license": "Apache-2.0", @@ -22,16 +22,20 @@ "prepublishOnly": "yarn build" }, "dependencies": { - "@celo/connect": "4.1.1-dev", - "@celo/utils": "4.1.1-dev", - "@celo/wallet-base": "4.1.1-dev", - "@celo/wallet-remote": "4.1.1-dev", + "@types/bn.js": "^5.1.0", + "@celo/base": "^5.0.5", + "@celo/connect": "^5.1.0", + "@celo/utils": "^5.0.5", + "@celo/wallet-base": "^5.1.0", + "@celo/wallet-remote": "^5.1.0", "bignumber.js": "^9.0.0", "debug": "^4.1.1" }, "devDependencies": { - "@celo/dev-utils": "0.0.1-dev", - "@celo/contractkit": "4.1.1-dev" + "@celo/dev-utils": "0.0.1", + "@celo/contractkit": "^5.1.0", + "bn.js": "^5.1.0", + "web3": "1.10.0" }, "engines": { "node": ">=8.14.2" diff --git a/packages/sdk/wallets/wallet-rpc/src/rpc-signer.ts b/packages/sdk/wallets/wallet-rpc/src/rpc-signer.ts index 2e6cca39f0f..d59a7cb965a 100644 --- a/packages/sdk/wallets/wallet-rpc/src/rpc-signer.ts +++ b/packages/sdk/wallets/wallet-rpc/src/rpc-signer.ts @@ -3,7 +3,7 @@ import { CeloTx, EncodedTransaction, RpcCaller, Signer } from '@celo/connect' import { EIP712TypedData } from '@celo/utils/lib/sign-typed-data-utils' import { decodeSig } from '@celo/wallet-base' import BigNumber from 'bignumber.js' -import BN from 'bn.js' +import type BN from 'bn.js' const INCORRECT_PASSWORD_ERROR = 'could not decrypt key with given password' const currentTimeInSeconds = () => Math.floor(Date.now() / 1000) @@ -82,14 +82,21 @@ export class RpcSigner implements Signer { throw new Error(`RpcSigner cannot sign tx with 'from' ${tx.from}`) } // see geth SendTxArgs type - // https://github.com/celo-org/celo-blockchain/blob/bf2ba25426f9956384220b8b2ce302337e7fa8a4/internal/ethapi/api.go#L1363 + // https://github.com/celo-org/celo-blockchain/blob/fc20d6921478cda68fc88797078f20053bae8866/internal/ethapi/api.go#L1241C6-L1241C20 const rpcTx = { ...tx, nonce: toRpcHex(tx.nonce), value: toRpcHex(tx.value), gas: toRpcHex(tx.gas), - gasPrice: toRpcHex(tx.gasPrice), gatewayFee: toRpcHex(tx.gatewayFee), + ...(tx.gasPrice + ? { + gasPrice: toRpcHex(tx.gasPrice), + } + : { + maxPriorityFeePerGas: toRpcHex(tx.maxPriorityFeePerGas), + maxFeePerGas: toRpcHex(tx.maxFeePerGas), + }), } return this.callAndCheckResponse(RpcSignerEndpoint.SignTransaction, [rpcTx]) } diff --git a/packages/sdk/wallets/wallet-rpc/src/rpc-wallet.test.ts b/packages/sdk/wallets/wallet-rpc/src/rpc-wallet.test.ts index cb23c4942ee..8f25fe84d25 100644 --- a/packages/sdk/wallets/wallet-rpc/src/rpc-wallet.test.ts +++ b/packages/sdk/wallets/wallet-rpc/src/rpc-wallet.test.ts @@ -178,8 +178,26 @@ testWithGanache('rpc-wallet', (web3) => { } }) - test('succeeds', async () => { - await expect(rpcWallet.signTransaction(celoTransaction)).resolves.not.toBeUndefined() + test('succeeds with old school pricing', async () => { + await expect( + rpcWallet.signTransaction(celoTransaction) + ).resolves.toMatchInlineSnapshot( + `"0xf86b8081991094588e4b68193001e4d10928660ab4165b813717c08a0100000000000000000083abcdef25a073bb7eaa60c810af1fad0f68fa15d4714f9990d0202b62797f6134493ec9f6fba046c13e92017228c2c8f0fae74ddd735021817f2f9757cd66debed078daf4070e"` + ) + }) + + test('succeeds with with FeeMarketFields', async () => { + const feeMarketTransaction = { + ...celoTransaction, + gasPrice: undefined, + maxFeePerGas: '1500000000', + maxPriorityFeePerGas: '1500000000', + } + await expect( + rpcWallet.signTransaction(feeMarketTransaction) + ).resolves.toMatchInlineSnapshot( + `"0xf86a80801094588e4b68193001e4d10928660ab4165b813717c08a0100000000000000000083abcdef26a05e9c1e7690d05f3e1433c824fbd948643ff6c618e347ea8c23a6363f3b17cdffa072dc1c22d6147be7b4b7b3cf51eb73b8bedd7940d7b668dcd7ef688a2354a631"` + ) }) // TODO(yorke): enable once fixed: https://github.com/celo-org/celo-monorepo/issues/4077 diff --git a/packages/terraform-modules-public/.gitignore b/packages/terraform-modules-public/.gitignore deleted file mode 100644 index a38f5ea139c..00000000000 --- a/packages/terraform-modules-public/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -plan/ -*.tfstate* -*secret*.tfvars diff --git a/packages/terraform-modules-public/aws/README.md b/packages/terraform-modules-public/aws/README.md deleted file mode 100644 index 14dc64d6316..00000000000 --- a/packages/terraform-modules-public/aws/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# Terraform Celo Validator Stack - -## Overview - -[Terraform](https://www.terraform.io) is a tool by Hashicorp that allows developers to treat _"infrastructure as code"_, easying the management and repeatibility of the -infrastructure. -Infrastructure and all kind of cloud resources are defined in modules, and Terraform creates/changes/destroys when changes are applied. - -Inside the [testnet](./testnet) folder you will find a module (and submodules) to create the setup for running a Celo Validator on AWS. The next logic resources can be created: - -- `vpc` module for setting up a VPC with a public and private subnet on multiple availability zones. Validator nodes go in the private subnet while bastion, proxy, and attestation nodes go in the public subnet. -- `bastion` module for an SSH bastion node. For security purposes, this is the only node that accepts external SSH traffic. All other nodes only accept SSH from the bastion. -- `proxy` module for creating a Proxy connected to a validator -- `validator` module for deploying a Validator -- `attestation-service` for deploying the Attestation Service (https://docs.celo.org/getting-started/baklava-testnet/running-a-validator#running-the-attestation-service). Each attestation service will connect to the same multi-AZ RDS DB instance. Only Twilio is supported at this time. - -## Operating System - -All nodes run on the Ubuntu LTS 18.04 AMI. Running `terraform apply` will select the latest available AMI. - -## Hardening & Security - -Celo nodes will get provisioned with some recommended security settings from cLab's Security Audit team. Most of this hardening is done in `install-base.sh` and `final-hardening.sh`. - -## Requirements - -Inside the [example](./example) folder you can find an example tf to use the module. Use that tf as base file for your deployment, modifying the account variables used for your convenience. -Alternatively you can take that tf files as base for customizing your deployment. Please take care specially about the VPC network configuration. The validators nodes deployed have not a public IP so the access to them is restricted. In order to provide outbound connection of these nodes the VPC network has to be configured with a NAT service allowing external traffic. diff --git a/packages/terraform-modules-public/aws/examples/mainnet/main.tf b/packages/terraform-modules-public/aws/examples/mainnet/main.tf deleted file mode 100644 index 3ebd6d3442a..00000000000 --- a/packages/terraform-modules-public/aws/examples/mainnet/main.tf +++ /dev/null @@ -1,41 +0,0 @@ -provider "aws" { - region = var.region -} - -terraform { - required_version = ">= 0.12.0" - # We recommend using remote state for production configs. - # Uncomment and update the config block below to use remote state. - # - # backend "s3" { - # bucket = "mybucket" - # key = "mytfs/celo/terraform.tfstate" - # region = "myregion" - - # dynamodb_table = "mydynamodb_table" - # encrypt = true - # } -} - -module "celo_cluster" { - source = "../../testnet" - - region = var.region - cidr_blocks = var.cidr_blocks - key_pair_name = var.key_pair_name - celo_image = var.celo_image - celo_network_id = var.celo_network_id - celo_image_attestation = var.celo_image_attestation - ethstats_host = var.ethstats_host - twilio_messaging_service_sid = var.twilio_messaging_service_sid - twilio_verify_service_sid = var.twilio_verify_service_sid - twilio_account_sid = var.twilio_account_sid - twilio_unsupported_regions = var.twilio_unsupported_regions - twilio_auth_token = var.twilio_auth_token - nexmo_api_key = var.nexmo_api_key - nexmo_api_secret = var.nexmo_api_secret - nexmo_unsupported_regions = var.nexmo_unsupported_regions - proxies = var.proxies - validators = var.validators - attestation_services = var.attestation_services -} \ No newline at end of file diff --git a/packages/terraform-modules-public/aws/examples/mainnet/outputs.tf b/packages/terraform-modules-public/aws/examples/mainnet/outputs.tf deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/packages/terraform-modules-public/aws/examples/mainnet/variables.tf b/packages/terraform-modules-public/aws/examples/mainnet/variables.tf deleted file mode 100644 index 4c83e420ca0..00000000000 --- a/packages/terraform-modules-public/aws/examples/mainnet/variables.tf +++ /dev/null @@ -1,249 +0,0 @@ -variable region { - type = string - description = "AWS Region to provision this cluster" - default = "us-west-1" -} - -variable cidr_blocks { - type = object({ - vpc = string - subnet_az1_public = string - subnet_az1_private = string - subnet_az2_public = string - subnet_az2_private = string - allowed_ssh_clients = string - }) - description = "The cidr_blocks for the different subnets in a redundant Celo network" - default = { - vpc = "10.20.0.0/16" - subnet_az1_public = "10.20.0.0/24" - subnet_az1_private = "10.20.1.0/24" - subnet_az2_public = "10.20.10.0/24" - subnet_az2_private = "10.20.11.0/24" - allowed_ssh_clients = "0.0.0.0/0" - } -} - -variable key_pair_name { - type = string - description = "SSH key pair name" -} - -variable celo_image { - type = string - description = "Docker image for Celo nodes" - default = "us.gcr.io/celo-org/celo-node:mainnet" -} - -variable celo_network_id { - type = string - description = "ID of the Celo network to join" - default = "42220" -} - -variable ethstats_host { - type = string - description = "Hostname for ethstats" - default = "stats-server.celo.org" -} - -variable proxies { - description = "Configuration for zero or more proxies in each availability zone." - type = object({ - az1 = map(object({ - validator_name = string - validator_signer_address = string - proxy_address = string - proxy_private_key_filename = string - proxy_private_key_file_contents = string - proxy_private_key_password = string - proxy_node_private_key = string - proxy_enode = string - })) - az2 = map(object({ - validator_name = string - validator_signer_address = string - proxy_address = string - proxy_private_key_filename = string - proxy_private_key_file_contents = string - proxy_private_key_password = string - proxy_node_private_key = string - proxy_enode = string - })) - }) - default = { - az1 = {} - az2 = {} - } - # Here is an example configuration. We recommend putting this into a secret.auto.tfvars file. - # default = { - # az1 = { - # myvalidator_az1_01 = { - # validator_name = "myvalidator_az1_01" - # validator_signer_address = "0000000011111111222222223333333344444444" - # proxy_address = "1234567812345678123456781234567812345678" - # proxy_private_key_filename = "UTC--2020-04-12T06-49-54.736290200Z--1234567812345678123456781234567812345678" # Use the name of the private key file generated when you created your signer address key. This will be in the keystore folder on the node where you created the signer account. - # proxy_private_key_file_contents = "" - # proxy_private_key_password = "" - # proxy_node_private_key = "" - # proxy_enode = "" - # } - # myvalidator_az1_02 = { - # validator_name = "myvalidator_az1_02" - # validator_signer_address = "5555555566666666777777778888888899999999" - # ... - # } - # } - # az2 = { - # myvalidator_az2_01 = { - # validator_name = "myvalidator_az2_01" - # validator_signer_address = "4444444433333333222222221111111100000000" - # ... - # } - # myvalidator_az2_02 = { - # validator_name = "myvalidator_az2_02" - # validator_signer_address = "9999999988888888777777776666666655555555" - # ... - # } - # } - # - # } -} - -variable validators { - description = "Configuration for zero or more validators in each availability zone" - type = object({ - az1 = map(object({ - name = string - signer_address = string - signer_private_key_filename = string - signer_private_key_file_contents = string - signer_private_key_password = string - })) - az2 = map(object({ - name = string - signer_address = string - signer_private_key_filename = string - signer_private_key_file_contents = string - signer_private_key_password = string - })) - }) - default = { - az1 = {} - az2 = {} - } - # Here is an example configuration. We recommend putting this into a secret.auto.tfvars file. - # default = { - # az1 = { - # myvalidator_az1_01 = { - # name = "myvalidator_az1_01" - # signer_address = "0000000011111111222222223333333344444444" - # signer_private_key_filename = "UTC--2020-02-06T06-49-54.736290200Z--0000000011111111222222223333333344444444" # Use the name of the private key file generated when you created your signer address key. This will be in the keystore folder on the node where you created the signer account. - # signer_private_key_file_contents = "" - # signer_private_key_password = "" - # } - # myvalidator_az1_02 = { - # ... - # } - # } - # az2 = { - # myvalidator_az2_01 = { - # ... - # } - # myvalidator_az2_02 = { - # ... - # } - # } - # - # } -} - - -variable celo_image_attestation { - type = string - description = "Docker image for Celo attestation service" - default = "us.gcr.io/celo-testnet/celo-monorepo:attestation-service-1-0-3" -} - -variable twilio_messaging_service_sid { - type = string -} - -variable twilio_verify_service_sid { - type = string -} - -variable twilio_account_sid { - type = string -} - -variable twilio_unsupported_regions { - type = string -} - -variable twilio_auth_token { - type = string -} - -variable nexmo_api_key { - type = string -} - -variable nexmo_api_secret { - type = string -} - -variable nexmo_unsupported_regions { - type = string -} - -variable attestation_services { - description = "Configuration for zero or more attestation nodes in each availability zone" - type = object({ - az1 = map(object({ - validator_name = string - validator_address = string - attestation_signer_address = string - attestation_signer_private_key_filename = string - attestation_signer_private_key_file_contents = string - attestation_signer_private_key_password = string - })) - az2 = map(object({ - validator_name = string - validator_address = string - attestation_signer_address = string - attestation_signer_private_key_filename = string - attestation_signer_private_key_file_contents = string - attestation_signer_private_key_password = string - })) - }) - default = { - az1 = {} - az2 = {} - } - # Here is an example configuration. We recommend putting this into a secret.auto.tfvars file. - # default = { - # az1 = { - # myvalidator_az1_01 = { - # validator_name = "myvalidator_az1_01" - # validator_address = "1234567812345678123456781234567812345678" - # attestation_signer_address = "2323232345454545676767678989898910101010" - # attestation_signer_private_key_filename = "UTC--2020-02-06T06-49-54.736290200Z--2323232345454545676767678989898910101010" # Use the name of the private key file generated when you created your signer address key. This will be in the keystore folder on the node where you created the signer account. - # attestation_signer_private_key_file_contents = "" - # attestation_signer_private_key_password = "" - # } - # myvalidator_az1_02 = { - # ... - # } - # } - # az2 = { - # myvalidator_az2_01 = { - # ... - # } - # myvalidator_az2_02 = { - # ... - # } - # } - # - # } -} \ No newline at end of file diff --git a/packages/terraform-modules-public/aws/examples/testnet/main.tf b/packages/terraform-modules-public/aws/examples/testnet/main.tf deleted file mode 100644 index 3ebd6d3442a..00000000000 --- a/packages/terraform-modules-public/aws/examples/testnet/main.tf +++ /dev/null @@ -1,41 +0,0 @@ -provider "aws" { - region = var.region -} - -terraform { - required_version = ">= 0.12.0" - # We recommend using remote state for production configs. - # Uncomment and update the config block below to use remote state. - # - # backend "s3" { - # bucket = "mybucket" - # key = "mytfs/celo/terraform.tfstate" - # region = "myregion" - - # dynamodb_table = "mydynamodb_table" - # encrypt = true - # } -} - -module "celo_cluster" { - source = "../../testnet" - - region = var.region - cidr_blocks = var.cidr_blocks - key_pair_name = var.key_pair_name - celo_image = var.celo_image - celo_network_id = var.celo_network_id - celo_image_attestation = var.celo_image_attestation - ethstats_host = var.ethstats_host - twilio_messaging_service_sid = var.twilio_messaging_service_sid - twilio_verify_service_sid = var.twilio_verify_service_sid - twilio_account_sid = var.twilio_account_sid - twilio_unsupported_regions = var.twilio_unsupported_regions - twilio_auth_token = var.twilio_auth_token - nexmo_api_key = var.nexmo_api_key - nexmo_api_secret = var.nexmo_api_secret - nexmo_unsupported_regions = var.nexmo_unsupported_regions - proxies = var.proxies - validators = var.validators - attestation_services = var.attestation_services -} \ No newline at end of file diff --git a/packages/terraform-modules-public/aws/examples/testnet/outputs.tf b/packages/terraform-modules-public/aws/examples/testnet/outputs.tf deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/packages/terraform-modules-public/aws/examples/testnet/variables.tf b/packages/terraform-modules-public/aws/examples/testnet/variables.tf deleted file mode 100644 index c40a387b605..00000000000 --- a/packages/terraform-modules-public/aws/examples/testnet/variables.tf +++ /dev/null @@ -1,249 +0,0 @@ -variable region { - type = string - description = "AWS Region to provision this cluster" - default = "us-west-1" -} - -variable cidr_blocks { - type = object({ - vpc = string - subnet_az1_public = string - subnet_az1_private = string - subnet_az2_public = string - subnet_az2_private = string - allowed_ssh_clients = string - }) - description = "The cidr_blocks for the different subnets in a redundant Celo network" - default = { - vpc = "10.10.0.0/16" - subnet_az1_public = "10.10.0.0/24" - subnet_az1_private = "10.10.1.0/24" - subnet_az2_public = "10.10.10.0/24" - subnet_az2_private = "10.10.11.0/24" - allowed_ssh_clients = "0.0.0.0/0" - } -} - -variable key_pair_name { - type = string - description = "SSH key pair name" -} - -variable celo_image { - type = string - description = "Docker image for Celo nodes" - default = "us.gcr.io/celo-testnet/celo-node:baklava" -} - -variable celo_network_id { - type = string - description = "ID of the Celo network to join" - default = "62320" -} - -variable ethstats_host { - type = string - description = "Hostname for ethstats" - default = "baklava-celostats-server.celo-testnet.org" -} - -variable proxies { - description = "Configuration for zero or more proxies in each availability zone." - type = object({ - az1 = map(object({ - validator_name = string - validator_signer_address = string - proxy_address = string - proxy_private_key_filename = string - proxy_private_key_file_contents = string - proxy_private_key_password = string - proxy_node_private_key = string - proxy_enode = string - })) - az2 = map(object({ - validator_name = string - validator_signer_address = string - proxy_address = string - proxy_private_key_filename = string - proxy_private_key_file_contents = string - proxy_private_key_password = string - proxy_node_private_key = string - proxy_enode = string - })) - }) - default = { - az1 = {} - az2 = {} - } - # Here is an example configuration. We recommend putting this into a secret.auto.tfvars file. - # default = { - # az1 = { - # myvalidator_az1_01 = { - # validator_name = "myvalidator_az1_01" - # validator_signer_address = "0000000011111111222222223333333344444444" - # proxy_address = "1234567812345678123456781234567812345678" - # proxy_private_key_filename = "UTC--2020-04-12T06-49-54.736290200Z--1234567812345678123456781234567812345678" # Use the name of the private key file generated when you created your signer address key. This will be in the keystore folder on the node where you created the signer account. - # proxy_private_key_file_contents = "" - # proxy_private_key_password = "" - # proxy_node_private_key = "" - # proxy_enode = "" - # } - # myvalidator_az1_02 = { - # validator_name = "myvalidator_az1_02" - # validator_signer_address = "5555555566666666777777778888888899999999" - # ... - # } - # } - # az2 = { - # myvalidator_az2_01 = { - # validator_name = "myvalidator_az2_01" - # validator_signer_address = "4444444433333333222222221111111100000000" - # ... - # } - # myvalidator_az2_02 = { - # validator_name = "myvalidator_az2_02" - # validator_signer_address = "9999999988888888777777776666666655555555" - # ... - # } - # } - # - # } -} - -variable validators { - description = "Configuration for zero or more validators in each availability zone" - type = object({ - az1 = map(object({ - name = string - signer_address = string - signer_private_key_filename = string - signer_private_key_file_contents = string - signer_private_key_password = string - })) - az2 = map(object({ - name = string - signer_address = string - signer_private_key_filename = string - signer_private_key_file_contents = string - signer_private_key_password = string - })) - }) - default = { - az1 = {} - az2 = {} - } - # Here is an example configuration. We recommend putting this into a secret.auto.tfvars file. - # default = { - # az1 = { - # myvalidator_az1_01 = { - # name = "myvalidator_az1_01" - # signer_address = "0000000011111111222222223333333344444444" - # signer_private_key_filename = "UTC--2020-02-06T06-49-54.736290200Z--0000000011111111222222223333333344444444" # Use the name of the private key file generated when you created your signer address key. This will be in the keystore folder on the node where you created the signer account. - # signer_private_key_file_contents = "" - # signer_private_key_password = "" - # } - # myvalidator_az1_02 = { - # ... - # } - # } - # az2 = { - # myvalidator_az2_01 = { - # ... - # } - # myvalidator_az2_02 = { - # ... - # } - # } - # - # } -} - - -variable celo_image_attestation { - type = string - description = "Docker image for Celo attestation service" - default = "us.gcr.io/celo-testnet/celo-monorepo:attestation-service-1-0-3" -} - -variable twilio_messaging_service_sid { - type = string -} - -variable twilio_verify_service_sid { - type = string -} - -variable twilio_account_sid { - type = string -} - -variable twilio_unsupported_regions { - type = string -} - -variable twilio_auth_token { - type = string -} - -variable nexmo_api_key { - type = string -} - -variable nexmo_api_secret { - type = string -} - -variable nexmo_unsupported_regions { - type = string -} - -variable attestation_services { - description = "Configuration for zero or more attestation nodes in each availability zone" - type = object({ - az1 = map(object({ - validator_name = string - validator_address = string - attestation_signer_address = string - attestation_signer_private_key_filename = string - attestation_signer_private_key_file_contents = string - attestation_signer_private_key_password = string - })) - az2 = map(object({ - validator_name = string - validator_address = string - attestation_signer_address = string - attestation_signer_private_key_filename = string - attestation_signer_private_key_file_contents = string - attestation_signer_private_key_password = string - })) - }) - default = { - az1 = {} - az2 = {} - } - # Here is an example configuration. We recommend putting this into a secret.auto.tfvars file. - # default = { - # az1 = { - # myvalidator_az1_01 = { - # validator_name = "myvalidator_az1_01" - # validator_address = "1234567812345678123456781234567812345678" - # attestation_signer_address = "2323232345454545676767678989898910101010" - # attestation_signer_private_key_filename = "UTC--2020-02-06T06-49-54.736290200Z--2323232345454545676767678989898910101010" # Use the name of the private key file generated when you created your signer address key. This will be in the keystore folder on the node where you created the signer account. - # attestation_signer_private_key_file_contents = "" - # attestation_signer_private_key_password = "" - # } - # myvalidator_az1_02 = { - # ... - # } - # } - # az2 = { - # myvalidator_az2_01 = { - # ... - # } - # myvalidator_az2_02 = { - # ... - # } - # } - # - # } -} \ No newline at end of file diff --git a/packages/terraform-modules-public/aws/testnet/main.tf b/packages/terraform-modules-public/aws/testnet/main.tf deleted file mode 100644 index 1784231e90f..00000000000 --- a/packages/terraform-modules-public/aws/testnet/main.tf +++ /dev/null @@ -1,227 +0,0 @@ -provider "aws" { - region = var.region -} - -module "celo_vpc" { - source = "./modules/vpc" - - name = "celo-vpc" - cidr_blocks = var.cidr_blocks -} - -module "celo_bastion_az1" { - source = "./modules/bastion" - - subnet_id = module.celo_vpc.subnet_ids.az1.public - security_group_id = module.celo_vpc.security_group_ids.bastion - key_pair_name = var.key_pair_name - name = "celo-bastion-az1" - instance_type = var.instance_types.bastion - iam_instance_profile = var.iam_instance_profiles.bastion -} - -module "celo_bastion_az2" { - source = "./modules/bastion" - - subnet_id = module.celo_vpc.subnet_ids.az2.public - security_group_id = module.celo_vpc.security_group_ids.bastion - key_pair_name = var.key_pair_name - name = "celo-bastion-az2" - instance_type = var.instance_types.bastion - iam_instance_profile = var.iam_instance_profiles.bastion -} - -module "celo_proxy_az1" { - source = "./modules/proxy" - - subnet_id = module.celo_vpc.subnet_ids.az1.public - security_group_id = module.celo_vpc.security_group_ids.proxy - key_pair_name = var.key_pair_name - instance_type = var.instance_types.proxy - celo_image = var.celo_image - celo_network_id = var.celo_network_id - ethstats_host = var.ethstats_host - iam_instance_profile = var.iam_instance_profiles.proxy - cloudwatch_log_group_name = var.cloudwatch_log_group_names.proxy - cloudwatch_collect_disk_and_memory_usage = var.cloudwatch_collect_disk_and_memory_usage - - proxies = var.proxies.az1 -} - -module "celo_proxy_az2" { - source = "./modules/proxy" - - subnet_id = module.celo_vpc.subnet_ids.az2.public - security_group_id = module.celo_vpc.security_group_ids.proxy - key_pair_name = var.key_pair_name - instance_type = var.instance_types.proxy - celo_image = var.celo_image - celo_network_id = var.celo_network_id - ethstats_host = var.ethstats_host - iam_instance_profile = var.iam_instance_profiles.proxy - cloudwatch_log_group_name = var.cloudwatch_log_group_names.proxy - cloudwatch_collect_disk_and_memory_usage = var.cloudwatch_collect_disk_and_memory_usage - - proxies = var.proxies.az2 -} - -locals { - validator_proxy_settings = { - az1 = zipmap( - keys(var.proxies.az1), - [for k, v in var.proxies.az1 : { - proxy_enode = var.proxies.az1[k].proxy_enode - proxy_private_ip = lookup(module.celo_proxy_az1.instances, k, { private_ip = "" }).private_ip - proxy_public_ip = lookup(module.celo_proxy_az1.eips, k, { public_ip = "" }).public_ip - } - ] - ) - az2 = zipmap( - keys(var.proxies.az2), - [for k, v in var.proxies.az2 : { - proxy_enode = var.proxies.az2[k].proxy_enode - proxy_private_ip = lookup(module.celo_proxy_az2.instances, k, { private_ip = "" }).private_ip - proxy_public_ip = lookup(module.celo_proxy_az2.eips, k, { public_ip = "" }).public_ip - } - ] - ) - } - validator_params = { - az1 = zipmap( - keys(var.validators.az1), - [for k, v in var.validators.az1 : merge(var.validators.az1[k], lookup(local.validator_proxy_settings.az1, k, {}))] - ) - az2 = zipmap( - keys(var.validators.az2), - [for k, v in var.validators.az2 : merge(var.validators.az2[k], lookup(local.validator_proxy_settings.az2, k, {}))] - ) - } -} - -module "celo_validator_az1" { - source = "./modules/validator" - - subnet_id = module.celo_vpc.subnet_ids.az1.private - security_group_id = module.celo_vpc.security_group_ids.validator - key_pair_name = var.key_pair_name - instance_type = var.instance_types.validator - celo_image = var.celo_image - celo_network_id = var.celo_network_id - ethstats_host = var.ethstats_host - iam_instance_profile = var.iam_instance_profiles.validator - cloudwatch_log_group_name = var.cloudwatch_log_group_names.validator - cloudwatch_collect_disk_and_memory_usage = var.cloudwatch_collect_disk_and_memory_usage - - validators = local.validator_params.az1 -} - -module "celo_validator_az2" { - source = "./modules/validator" - - subnet_id = module.celo_vpc.subnet_ids.az2.private - security_group_id = module.celo_vpc.security_group_ids.validator - key_pair_name = var.key_pair_name - instance_type = var.instance_types.validator - celo_image = var.celo_image - celo_network_id = var.celo_network_id - ethstats_host = var.ethstats_host - iam_instance_profile = var.iam_instance_profiles.validator - cloudwatch_log_group_name = var.cloudwatch_log_group_names.validator - cloudwatch_collect_disk_and_memory_usage = var.cloudwatch_collect_disk_and_memory_usage - - validators = local.validator_params.az2 -} - -resource "random_password" "password" { - length = 50 - special = false - min_lower = 1 - min_upper = 1 - min_numeric = 1 -} - -resource "aws_db_subnet_group" "attestation" { - count = (length(var.attestation_services.az1) > 0 || length(var.attestation_services.az2) > 0) ? 1 : 0 - name = "celo-db-subnet-group" - subnet_ids = [module.celo_vpc.subnet_ids.az1.private, module.celo_vpc.subnet_ids.az2.private] -} - -resource "aws_db_instance" "attestation" { - count = (length(var.attestation_services.az1) > 0 || length(var.attestation_services.az2) > 0) ? 1 : 0 - identifier = "celo-attestation-db" - allocated_storage = 32 - storage_type = "gp2" - engine = "postgres" - engine_version = "9.6" - instance_class = "db.t3.small" - name = "attestation" - username = "attestation" - password = random_password.password.result - multi_az = true - db_subnet_group_name = aws_db_subnet_group.attestation[0].name - vpc_security_group_ids = [module.celo_vpc.security_group_ids.attestation_db] - skip_final_snapshot = true -} - -locals { - attestation_db_url = length(aws_db_instance.attestation) > 0 ? format("postgresql://%s:%s@%s/%s", - aws_db_instance.attestation[0].username, - aws_db_instance.attestation[0].password, - aws_db_instance.attestation[0].endpoint, - aws_db_instance.attestation[0].name - ) : "" -} - -module "celo_attestation_service_az1" { - source = "./modules/attestation-service" - - subnet_id = module.celo_vpc.subnet_ids.az1.public - security_group_id = module.celo_vpc.security_group_ids.attestation_service - key_pair_name = var.key_pair_name - instance_type = var.instance_types.attestation_service - celo_image = var.celo_image - celo_network_id = var.celo_network_id - celo_image_attestation = var.celo_image_attestation - database_url = local.attestation_db_url - twilio_messaging_service_sid = var.twilio_messaging_service_sid - twilio_verify_service_sid = var.twilio_verify_service_sid - twilio_account_sid = var.twilio_account_sid - twilio_unsupported_regions = var.twilio_unsupported_regions - twilio_auth_token = var.twilio_auth_token - nexmo_api_key = var.nexmo_api_key - nexmo_api_secret = var.nexmo_api_secret - nexmo_unsupported_regions = var.nexmo_unsupported_regions - iam_instance_profile = var.iam_instance_profiles.attestation_service - cloudwatch_attestation_node_log_group_name = var.cloudwatch_log_group_names.attestation_node - cloudwatch_attestation_service_log_group_name = var.cloudwatch_log_group_names.attestation_service - cloudwatch_collect_disk_and_memory_usage = var.cloudwatch_collect_disk_and_memory_usage - - attestation_services = var.attestation_services.az1 -} - -module "celo_attestation_service_az2" { - source = "./modules/attestation-service" - - subnet_id = module.celo_vpc.subnet_ids.az2.public - security_group_id = module.celo_vpc.security_group_ids.attestation_service - key_pair_name = var.key_pair_name - instance_type = var.instance_types.attestation_service - celo_image = var.celo_image - celo_network_id = var.celo_network_id - celo_image_attestation = var.celo_image_attestation - database_url = local.attestation_db_url - twilio_messaging_service_sid = var.twilio_messaging_service_sid - twilio_verify_service_sid = var.twilio_verify_service_sid - twilio_account_sid = var.twilio_account_sid - twilio_unsupported_regions = var.twilio_unsupported_regions - twilio_auth_token = var.twilio_auth_token - nexmo_api_key = var.nexmo_api_key - nexmo_api_secret = var.nexmo_api_secret - nexmo_unsupported_regions = var.nexmo_unsupported_regions - iam_instance_profile = var.iam_instance_profiles.attestation_service - cloudwatch_attestation_node_log_group_name = var.cloudwatch_log_group_names.attestation_node - cloudwatch_attestation_service_log_group_name = var.cloudwatch_log_group_names.attestation_service - cloudwatch_collect_disk_and_memory_usage = var.cloudwatch_collect_disk_and_memory_usage - - attestation_services = var.attestation_services.az2 -} \ No newline at end of file diff --git a/packages/terraform-modules-public/aws/testnet/modules/ami/main.tf b/packages/terraform-modules-public/aws/testnet/modules/ami/main.tf deleted file mode 100644 index 71dd0a5cec0..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/ami/main.tf +++ /dev/null @@ -1,15 +0,0 @@ -data "aws_ami" "ubuntu" { - most_recent = true - - filter { - name = "name" - values = ["ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-*"] - } - - filter { - name = "virtualization-type" - values = ["hvm"] - } - - owners = ["099720109477"] -} \ No newline at end of file diff --git a/packages/terraform-modules-public/aws/testnet/modules/ami/outputs.tf b/packages/terraform-modules-public/aws/testnet/modules/ami/outputs.tf deleted file mode 100644 index f5635b33910..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/ami/outputs.tf +++ /dev/null @@ -1,5 +0,0 @@ -output ami_ids { - value = { - ubuntu_18_04 = data.aws_ami.ubuntu.id - } -} \ No newline at end of file diff --git a/packages/terraform-modules-public/aws/testnet/modules/ami/variables.tf b/packages/terraform-modules-public/aws/testnet/modules/ami/variables.tf deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/packages/terraform-modules-public/aws/testnet/modules/attestation-service/main.tf b/packages/terraform-modules-public/aws/testnet/modules/attestation-service/main.tf deleted file mode 100644 index 7d765c1e966..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/attestation-service/main.tf +++ /dev/null @@ -1,68 +0,0 @@ -module "ami" { - source = "../ami" -} - -resource "aws_instance" "attestation_service" { - for_each = var.attestation_services - - ami = module.ami.ami_ids.ubuntu_18_04 - instance_type = var.instance_type - subnet_id = var.subnet_id - vpc_security_group_ids = [var.security_group_id] - key_name = var.key_pair_name - iam_instance_profile = var.iam_instance_profile - - root_block_device { - volume_size = var.volume_size - } - - user_data = join("\n", [ - file("${path.module}/../startup-scripts/install-base.sh"), - var.cloudwatch_collect_disk_and_memory_usage ? file("${path.module}/../startup-scripts/install-cloudwatch-agent.sh") : "", - file("${path.module}/../startup-scripts/install-docker.sh"), - file("${path.module}/../startup-scripts/install-chrony.sh"), - file("${path.module}/../startup-scripts/install-postgres-client.sh"), - templatefile("${path.module}/../startup-scripts/run-attestation-service.sh", { - validator_address = each.value.validator_address - attestation_signer_address = each.value.attestation_signer_address - attestation_signer_private_key_filename = each.value.attestation_signer_private_key_filename - attestation_signer_private_key_file_contents = each.value.attestation_signer_private_key_file_contents - attestation_signer_private_key_password = each.value.attestation_signer_private_key_password - database_url = var.database_url - twilio_messaging_service_sid = var.twilio_messaging_service_sid - twilio_verify_service_sid = var.twilio_verify_service_sid - twilio_account_sid = var.twilio_account_sid - twilio_unsupported_regions = var.twilio_unsupported_regions - twilio_auth_token = var.twilio_auth_token - nexmo_api_key = var.nexmo_api_key - nexmo_api_secret = var.nexmo_api_secret - nexmo_unsupported_regions = var.nexmo_unsupported_regions - celo_image = var.celo_image - celo_network_id = var.celo_network_id - celo_image_attestation = var.celo_image_attestation - cloudwatch_attestation_node_log_group_name = var.cloudwatch_attestation_node_log_group_name - cloudwatch_attestation_node_log_stream_name = "celo_attestation_node_${each.key}" - cloudwatch_attestation_service_log_group_name = var.cloudwatch_attestation_service_log_group_name - cloudwatch_attestation_service_log_stream_name = "celo_attestation_service_${each.key}" - }), - file("${path.module}/../startup-scripts/final-hardening.sh") - ]) - - tags = { - Name = "celo-attestation-service-${each.value.validator_name}" - } - - lifecycle { - ignore_changes = [ - ami, - user_data - ] - } -} - -resource "aws_eip" "attestation_service" { - for_each = var.attestation_services - - instance = aws_instance.attestation_service[each.key].id - vpc = true -} diff --git a/packages/terraform-modules-public/aws/testnet/modules/attestation-service/outputs.tf b/packages/terraform-modules-public/aws/testnet/modules/attestation-service/outputs.tf deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/packages/terraform-modules-public/aws/testnet/modules/attestation-service/variables.tf b/packages/terraform-modules-public/aws/testnet/modules/attestation-service/variables.tf deleted file mode 100644 index 7b01b1badbd..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/attestation-service/variables.tf +++ /dev/null @@ -1,105 +0,0 @@ -variable instance_type { - type = string - description = "AWS instance type for this node" -} - -variable subnet_id { - type = string - description = "Subnet ID to place this proxy. This should be a public subnet from your Celo VPC." -} - -variable security_group_id { - type = string - description = "VPC Security group for this instance" -} - -variable key_pair_name { - type = string - description = "Name of the SSH key pair to access this node from the bastion" -} - -variable volume_size { - type = number - description = "GB size for the EBS volume" - default = 256 -} - -variable celo_network_id { - type = string -} - -variable celo_image { - type = string -} - -variable celo_image_attestation { - type = string -} - -variable database_url { - type = string -} - -variable twilio_messaging_service_sid { - type = string -} - -variable twilio_verify_service_sid { - type = string -} - -variable twilio_account_sid { - type = string -} - -variable twilio_unsupported_regions { - type = string -} - -variable twilio_auth_token { - type = string -} - -variable nexmo_api_key { - type = string -} - -variable nexmo_api_secret { - type = string -} - -variable nexmo_unsupported_regions { - type = string -} - -variable attestation_services { - description = "Configuration for attestation nodes." - type = map(object({ - validator_name = string - validator_address = string - attestation_signer_address = string - attestation_signer_private_key_filename = string - attestation_signer_private_key_file_contents = string - attestation_signer_private_key_password = string - })) -} - -variable iam_instance_profile { - type = string - default = null -} - -variable cloudwatch_attestation_node_log_group_name { - type = string - default = "" -} - -variable cloudwatch_attestation_service_log_group_name { - type = string - default = "" -} - -variable cloudwatch_collect_disk_and_memory_usage { - type = bool - default = false -} \ No newline at end of file diff --git a/packages/terraform-modules-public/aws/testnet/modules/bastion/main.tf b/packages/terraform-modules-public/aws/testnet/modules/bastion/main.tf deleted file mode 100644 index 9cc26a2ec01..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/bastion/main.tf +++ /dev/null @@ -1,26 +0,0 @@ -module "ami" { - source = "../ami" -} - -resource "aws_instance" "bastion" { - ami = module.ami.ami_ids.ubuntu_18_04 - instance_type = var.instance_type - subnet_id = var.subnet_id - associate_public_ip_address = true - vpc_security_group_ids = [ - var.security_group_id - ] - key_name = var.key_pair_name - iam_instance_profile = var.iam_instance_profile - - user_data = join("\n", [ - file("${path.module}/../startup-scripts/install-base.sh"), - file("${path.module}/../startup-scripts/install-chrony.sh"), - file("${path.module}/../startup-scripts/configure-bastion.sh"), - file("${path.module}/../startup-scripts/final-hardening.sh") - ]) - - tags = { - Name = var.name - } -} diff --git a/packages/terraform-modules-public/aws/testnet/modules/bastion/outputs.tf b/packages/terraform-modules-public/aws/testnet/modules/bastion/outputs.tf deleted file mode 100644 index f5c5027e5c9..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/bastion/outputs.tf +++ /dev/null @@ -1,3 +0,0 @@ -output public_ip { - value = aws_instance.bastion.public_ip -} \ No newline at end of file diff --git a/packages/terraform-modules-public/aws/testnet/modules/bastion/variables.tf b/packages/terraform-modules-public/aws/testnet/modules/bastion/variables.tf deleted file mode 100644 index 61c14d224c4..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/bastion/variables.tf +++ /dev/null @@ -1,28 +0,0 @@ -variable subnet_id { - type = string - description = "Subnet for the SSH Bastion" -} - -variable security_group_id { - type = string - description = "VPC Security group for this instance" -} - -variable key_pair_name { - type = string - description = "SSH Key Pair name" -} - -variable name { - type = string - description = "Name for this instance" -} - -variable instance_type { - type = string -} - -variable iam_instance_profile { - type = string - default = null -} \ No newline at end of file diff --git a/packages/terraform-modules-public/aws/testnet/modules/proxy/main.tf b/packages/terraform-modules-public/aws/testnet/modules/proxy/main.tf deleted file mode 100644 index ca450ec3545..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/proxy/main.tf +++ /dev/null @@ -1,58 +0,0 @@ -module "ami" { - source = "../ami" -} - -resource "aws_instance" "celo_proxy" { - for_each = var.proxies - - ami = module.ami.ami_ids.ubuntu_18_04 - instance_type = var.instance_type - subnet_id = var.subnet_id - vpc_security_group_ids = [var.security_group_id] - key_name = var.key_pair_name - iam_instance_profile = var.iam_instance_profile - - root_block_device { - volume_size = var.volume_size - } - - user_data = join("\n", [ - file("${path.module}/../startup-scripts/install-base.sh"), - var.cloudwatch_collect_disk_and_memory_usage ? file("${path.module}/../startup-scripts/install-cloudwatch-agent.sh") : "", - file("${path.module}/../startup-scripts/install-docker.sh"), - file("${path.module}/../startup-scripts/install-chrony.sh"), - templatefile("${path.module}/../startup-scripts/run-proxy-node.sh", { - celo_image = var.celo_image - celo_network_id = var.celo_network_id - ethstats_host = var.ethstats_host - validator_name = each.value.validator_name - validator_signer_address = each.value.validator_signer_address - proxy_address = each.value.proxy_address - proxy_private_key_filename = each.value.proxy_private_key_filename - proxy_private_key_file_contents = each.value.proxy_private_key_file_contents - proxy_private_key_password = each.value.proxy_private_key_password - proxy_node_private_key = each.value.proxy_node_private_key - cloudwatch_log_group_name = var.cloudwatch_log_group_name - cloudwatch_log_stream_name = "celo_proxy_${each.key}" - }), - file("${path.module}/../startup-scripts/final-hardening.sh") - ]) - - tags = { - Name = "celo-proxy-${each.value.validator_name}" - } - - lifecycle { - ignore_changes = [ - ami, - user_data - ] - } -} - -resource "aws_eip" "celo_proxy" { - for_each = var.proxies - - instance = aws_instance.celo_proxy[each.key].id - vpc = true -} \ No newline at end of file diff --git a/packages/terraform-modules-public/aws/testnet/modules/proxy/outputs.tf b/packages/terraform-modules-public/aws/testnet/modules/proxy/outputs.tf deleted file mode 100644 index a816593b835..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/proxy/outputs.tf +++ /dev/null @@ -1,7 +0,0 @@ -output instances { - value = aws_instance.celo_proxy -} - -output eips { - value = aws_eip.celo_proxy -} \ No newline at end of file diff --git a/packages/terraform-modules-public/aws/testnet/modules/proxy/variables.tf b/packages/terraform-modules-public/aws/testnet/modules/proxy/variables.tf deleted file mode 100644 index 29990297b79..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/proxy/variables.tf +++ /dev/null @@ -1,68 +0,0 @@ -variable instance_type { - type = string - description = "AWS instance type for this node" -} - -variable subnet_id { - type = string - description = "Subnet ID to place this proxy. This should be a public subnet from your Celo VPC." -} - -variable security_group_id { - type = string - description = "VPC Security group for this instance" -} - -variable key_pair_name { - type = string - description = "Name of the SSH key pair to access this node from the bastion" -} - -variable volume_size { - type = number - description = "GB size for the EBS volume" - default = 256 -} - -variable celo_image { - type = string - description = "Name of the docker image to run" -} - -variable celo_network_id { - type = string - description = "Celo network ID to join" -} - -variable ethstats_host { - type = string - description = "Hostname for ethstats" -} - -variable proxies { - type = map(object({ - validator_name = string - validator_signer_address = string - proxy_address = string - proxy_private_key_filename = string - proxy_private_key_file_contents = string - proxy_private_key_password = string - proxy_node_private_key = string - })) - description = "Map of proxy configurations." -} - -variable iam_instance_profile { - type = string - default = null -} - -variable cloudwatch_log_group_name { - type = string - default = "" -} - -variable cloudwatch_collect_disk_and_memory_usage { - type = bool - default = false -} diff --git a/packages/terraform-modules-public/aws/testnet/modules/startup-scripts/configure-bastion.sh b/packages/terraform-modules-public/aws/testnet/modules/startup-scripts/configure-bastion.sh deleted file mode 100644 index ba6dd2606a9..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/startup-scripts/configure-bastion.sh +++ /dev/null @@ -1,6 +0,0 @@ -#! /bin/bash - -SSH_CONFIG=/etc/ssh/sshd_config - -sed -i 's/^AllowAgentForwarding no/AllowAgentForwarding yes/' $SSH_CONFIG -systemctl restart ssh diff --git a/packages/terraform-modules-public/aws/testnet/modules/startup-scripts/final-hardening.sh b/packages/terraform-modules-public/aws/testnet/modules/startup-scripts/final-hardening.sh deleted file mode 100644 index b48888f5ea4..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/startup-scripts/final-hardening.sh +++ /dev/null @@ -1,60 +0,0 @@ -# Enable autologout -cat < /etc/profile.d/autologout.sh -TMOUT=300 -readonly TMOUT -export TMOUT -EOF -chmod +x /etc/profile.d/autologout.sh - -# Limit proc mount -mount -o remount,rw,hidepid=2 /proc - -# Harden login.defs -sed -i 's/^UMASK\t*022/UMASK\t\t027/' /etc/login.defs -sed -i 's/^PASS_MAX_DAYS\t*[0-9]*/PASS_MAX_DAYS\t90/' /etc/login.defs -sed -i 's/^PASS_MIN_DAYS\t*[0-9]*/PASS_MIN_DAYS\t1/' /etc/login.defs - -# Disable core dumps -echo "* hard core 0" >> /etc/security/limits.d/core.conf -echo "ulimit -c 0 > /dev/null 2>&1" >> /etc/profile.d/disablecoredumps.sh -chmod +x /etc/profile.d/disablecoredumps.sh -cat < /etc/systemd/coredump.conf -[Coredump] - -Storage=none -ProcessSizeMax=0 -EOF -systemctl daemon-reload - -# sysctl Hardening -echo "net.ipv4.conf.default.log_martians=1" >> /etc/sysctl.conf -echo "net.ipv4.conf.default.accept_source_route=0" >> /etc/sysctl.conf -echo "net.ipv4.conf.all.send_redirects=0" >> /etc/sysctl.conf -echo "net.ipv4.conf.all.log_martians=1" >> /etc/sysctl.conf -echo "kernel.sysrq=0" >> /etc/sysctl.conf -echo "kernel.kptr_restrict=2" >> /etc/sysctl.conf -echo "kernel.dmesg_restrict=1" >> /etc/sysctl.conf -echo "kernel.core_uses_pid=1" >> /etc/sysctl.conf -echo "fs.suid_dumpable=0" >> /etc/sysctl.conf -echo "net.ipv6.conf.default.accept_redirects=0" >> /etc/sysctl.conf -echo "net.ipv6.conf.all.accept_redirects=0" >> /etc/sysctl.conf -echo "net.ipv4.conf.default.accept_redirects=0" >> /etc/sysctl.conf -echo "net.ipv4.conf.all.accept_redirects=0" >> /etc/sysctl.conf - -sysctl --system - -# Upgrade packages -apt update -unattended-upgrade -d -apt upgrade -y - -# Harden file permissions -chmod 600 /boot/grub/grub.cfg -chmod 600 /etc/at.deny -chmod 600 /etc/crontab -chmod 600 /etc/ssh/sshd_config -chmod 700 /etc/cron.d -chmod 700 /etc/cron.daily -chmod 700 /etc/cron.hourly -chmod 700 /etc/cron.weekly -chmod 700 /etc/cron.monthly \ No newline at end of file diff --git a/packages/terraform-modules-public/aws/testnet/modules/startup-scripts/install-base.sh b/packages/terraform-modules-public/aws/testnet/modules/startup-scripts/install-base.sh deleted file mode 100644 index fe470df9679..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/startup-scripts/install-base.sh +++ /dev/null @@ -1,24 +0,0 @@ -#! /bin/bash - -apt-get update -apt-get --assume-yes install \ - python \ - apt-show-versions \ - libpam-cracklib \ - fail2ban \ - unzip - -SSH_CONFIG=/etc/ssh/sshd_config - -sed -i 's/^#AllowAgentForwarding yes/AllowAgentForwarding no/' $SSH_CONFIG -sed -i 's/^#AllowTcpForwarding yes/AllowTcpForwarding no/' $SSH_CONFIG -sed -i '/UsePAM yes/a AllowUsers ubuntu' $SSH_CONFIG -sed -i 's/^#ClientAliveCountMax [0-9]*/ClientAliveCountMax 2/' $SSH_CONFIG -sed -i 's/^#Compression [a-zA-Z]*/Compression no/' $SSH_CONFIG -sed -i 's/^#TCPKeepAlive [a-zA-Z]*/TCPKeepAlive no/' $SSH_CONFIG -sed -i 's/^X11Forwarding yes/X11Forwarding no/' $SSH_CONFIG -sed -i 's/^#MaxSessions [0-9]*/MaxSessions 2/' $SSH_CONFIG -sed -i 's/^#MaxAuthTries [0-9]*/MaxAuthTries 3/' $SSH_CONFIG -sed -i 's/^#LogLevel [a-zA-Z]*/LogLevel VERBOSE/' $SSH_CONFIG - -systemctl restart ssh diff --git a/packages/terraform-modules-public/aws/testnet/modules/startup-scripts/install-chrony.sh b/packages/terraform-modules-public/aws/testnet/modules/startup-scripts/install-chrony.sh deleted file mode 100644 index d042a4124f8..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/startup-scripts/install-chrony.sh +++ /dev/null @@ -1,11 +0,0 @@ -#! /bin/bash -apt-get update -apt-get --assume-yes remove ntp* -apt-get --assume-yes install chrony - -# This configures chrony on the instance to use the Amazon Time Sync Service -# See https://aws.amazon.com/blogs/aws/keeping-time-with-amazon-time-sync-service/ for more details - -mv /etc/chrony/chrony.conf /etc/chrony/chrony.conf.old -sed '/^pool ntp\.ubuntu\.com*/i server 169.254.169.123 prefer iburst minpoll 4 maxpoll 4' /etc/chrony/chrony.conf.old > /etc/chrony/chrony.conf -/etc/init.d/chrony restart diff --git a/packages/terraform-modules-public/aws/testnet/modules/startup-scripts/install-cloudwatch-agent.sh b/packages/terraform-modules-public/aws/testnet/modules/startup-scripts/install-cloudwatch-agent.sh deleted file mode 100644 index 1c9251f9aad..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/startup-scripts/install-cloudwatch-agent.sh +++ /dev/null @@ -1,45 +0,0 @@ -#! /bin/bash - -CLOUDWATCH_FOLDER=/tmp/cloudwatch_agent_install -CLOUDWATCH_CONFIG_FILE=/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json -mkdir $CLOUDWATCH_FOLDER -cd $CLOUDWATCH_FOLDER - -wget https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/amd64/latest/amazon-cloudwatch-agent.deb -dpkg -i -E ./amazon-cloudwatch-agent.deb - -cat < $CLOUDWATCH_CONFIG_FILE -{ - "agent": { - "metrics_collection_interval": 60, - "run_as_user": "root" - }, - "metrics": { - "append_dimensions": { - "AutoScalingGroupName": "\${aws:AutoScalingGroupName}", - "ImageId": "\${aws:ImageId}", - "InstanceId": "\${aws:InstanceId}", - "InstanceType": "\${aws:InstanceType}" - }, - "metrics_collected": { - "disk": { - "measurement": [ - "used_percent" - ], - "metrics_collection_interval": 60, - "resources": [ - "*" - ] - }, - "mem": { - "measurement": [ - "mem_used_percent" - ], - "metrics_collection_interval": 60 - } - } - } -} -EOF - -/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c file:$CLOUDWATCH_CONFIG_FILE -s diff --git a/packages/terraform-modules-public/aws/testnet/modules/startup-scripts/install-docker.sh b/packages/terraform-modules-public/aws/testnet/modules/startup-scripts/install-docker.sh deleted file mode 100644 index 8901161c49c..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/startup-scripts/install-docker.sh +++ /dev/null @@ -1,17 +0,0 @@ -#! /bin/bash - -apt-get update -apt-get --assume-yes install \ - apt-transport-https \ - ca-certificates \ - curl \ - gnupg-agent \ - software-properties-common -curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - -add-apt-repository \ - "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ - $(lsb_release -cs) \ - stable" - -apt-get update -apt-get --assume-yes install docker-ce docker-ce-cli containerd.io \ No newline at end of file diff --git a/packages/terraform-modules-public/aws/testnet/modules/startup-scripts/install-postgres-client.sh b/packages/terraform-modules-public/aws/testnet/modules/startup-scripts/install-postgres-client.sh deleted file mode 100644 index 8078d642db8..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/startup-scripts/install-postgres-client.sh +++ /dev/null @@ -1,3 +0,0 @@ -#! /bin/bash -apt-get update -apt-get --assume-yes install postgresql-client-common postgresql-client diff --git a/packages/terraform-modules-public/aws/testnet/modules/startup-scripts/run-attestation-service.sh b/packages/terraform-modules-public/aws/testnet/modules/startup-scripts/run-attestation-service.sh deleted file mode 100644 index b3a56b162e4..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/startup-scripts/run-attestation-service.sh +++ /dev/null @@ -1,68 +0,0 @@ -#! /bin/bash - -export CELO_IMAGE=${celo_image} -export NETWORK_ID=${celo_network_id} -export CELO_VALIDATOR_ADDRESS=${validator_address} -NODE_DIRECTORY=/home/ubuntu/celo-attestations-node - -mkdir $NODE_DIRECTORY -cd $NODE_DIRECTORY - -docker run -v $PWD:/root/.celo --rm $CELO_IMAGE init /celo/genesis.json -export BOOTNODE_ENODES="$(docker run --rm --entrypoint cat $CELO_IMAGE /celo/bootnodes)" - -export CELO_ATTESTATION_SIGNER_ADDRESS=${attestation_signer_address} -echo -n '${attestation_signer_private_key_password}' > .password -echo -n '${attestation_signer_private_key_file_contents}' > keystore/${attestation_signer_private_key_filename} - -ATTESTATION_NODE_CLOUDWATCH_LOG_GROUP_NAME=${cloudwatch_attestation_node_log_group_name} -ATTESTATION_NODE_CLOUDWATCH_LOG_STREAM_NAME=${cloudwatch_attestation_node_log_stream_name} - -if [[ -z $ATTESTATION_NODE_CLOUDWATCH_LOG_GROUP_NAME || -z $ATTESTATION_NODE_CLOUDWATCH_LOG_STREAM_NAME ]]; then - DOCKER_LOGGING_PARAMS='' -else - DOCKER_LOGGING_PARAMS="--log-driver=awslogs --log-opt awslogs-group=$ATTESTATION_NODE_CLOUDWATCH_LOG_GROUP_NAME --log-opt awslogs-stream=$ATTESTATION_NODE_CLOUDWATCH_LOG_STREAM_NAME" -fi - -docker run -d --name celo-attestations $DOCKER_LOGGING_PARAMS --restart always -p 127.0.0.1:8545:8545 -v $PWD:/root/.celo $CELO_IMAGE --verbosity 3 --networkid $NETWORK_ID --syncmode full --nousb --rpc --rpcaddr 0.0.0.0 --rpcapi eth,net,web3,debug,admin --unlock $CELO_ATTESTATION_SIGNER_ADDRESS --password /root/.celo/.password --bootnodes $BOOTNODE_ENODES --allow-insecure-unlock - -export CELO_IMAGE_ATTESTATION=${celo_image_attestation} -export CONFIG_FILE_PATH=.attestationconfig - -echo 'DATABASE_URL=${database_url}' >> $CONFIG_FILE_PATH -echo 'CELO_PROVIDER=http://localhost:8545' >> $CONFIG_FILE_PATH -echo 'CELO_VALIDATOR_ADDRESS=${validator_address}' >> $CONFIG_FILE_PATH -echo 'ATTESTATION_SIGNER_ADDRESS=${attestation_signer_address}' >> $CONFIG_FILE_PATH - -echo 'SMS_PROVIDERS=twilio,nexmo' >> $CONFIG_FILE_PATH - -echo 'PORT=80' >> $CONFIG_FILE_PATH - -echo 'NEXMO_KEY=${nexmo_api_key}' >> $CONFIG_FILE_PATH -echo 'NEXMO_SECRET=${nexmo_api_secret}' >> $CONFIG_FILE_PATH -echo 'NEXMO_ACCOUNT_BALANCE_METRIC=0' >> $CONFIG_FILE_PATH -echo 'NEXMO_UNSUPPORTED_REGIONS=${nexmo_unsupported_regions}' >> $CONFIG_FILE_PATH - -echo 'TWILIO_ACCOUNT_SID=${twilio_account_sid}' >> $CONFIG_FILE_PATH -echo 'TWILIO_MESSAGING_SERVICE_SID=${twilio_messaging_service_sid}' >> $CONFIG_FILE_PATH -echo 'TWILIO_VERIFY_SERVICE_SID=${twilio_verify_service_sid}' >> $CONFIG_FILE_PATH -echo 'TWILIO_AUTH_TOKEN=${twilio_auth_token}' >> $CONFIG_FILE_PATH -echo 'TWILIO_UNSUPPORTED_REGIONS=${twilio_unsupported_regions}' >> $CONFIG_FILE_PATH - -echo 'MAX_DELIVERY_ATTEMPTS=2' >> $CONFIG_FILE_PATH -echo 'VERIFY_CONFIG_ON_STARTUP=1' >> $CONFIG_FILE_PATH - -echo 'LOG_FORMAT=json' >> $CONFIG_FILE_PATH -echo 'LOG_LEVEL=info' >> $CONFIG_FILE_PATH - - -ATTESTATION_SERVICE_CLOUDWATCH_LOG_GROUP_NAME=${cloudwatch_attestation_service_log_group_name} -ATTESTATION_SERVICE_CLOUDWATCH_LOG_STREAM_NAME=${cloudwatch_attestation_service_log_stream_name} - -if [[ -z $ATTESTATION_SERVICE_CLOUDWATCH_LOG_GROUP_NAME || -z $ATTESTATION_SERVICE_CLOUDWATCH_LOG_STREAM_NAME ]]; then - DOCKER_LOGGING_PARAMS='' -else - DOCKER_LOGGING_PARAMS="--log-driver=awslogs --log-opt awslogs-group=$ATTESTATION_SERVICE_CLOUDWATCH_LOG_GROUP_NAME --log-opt awslogs-stream=$ATTESTATION_SERVICE_CLOUDWATCH_LOG_STREAM_NAME" -fi - -docker run -d --name celo-attestation-service $DOCKER_LOGGING_PARAMS --restart always --entrypoint /bin/bash --network host --env-file $CONFIG_FILE_PATH -p 80:80 $CELO_IMAGE_ATTESTATION -c " cd /celo-monorepo/packages/attestation-service && yarn run db:migrate && yarn start " diff --git a/packages/terraform-modules-public/aws/testnet/modules/startup-scripts/run-proxy-node.sh b/packages/terraform-modules-public/aws/testnet/modules/startup-scripts/run-proxy-node.sh deleted file mode 100644 index 75c4f5eddc2..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/startup-scripts/run-proxy-node.sh +++ /dev/null @@ -1,27 +0,0 @@ -#! /bin/bash -CELO_IMAGE=${celo_image} -NETWORK_ID=${celo_network_id} -CELO_VALIDATOR_SIGNER_ADDRESS=${validator_signer_address} - -NODE_DIRECTORY=/home/ubuntu/celo-proxy-node - -mkdir $NODE_DIRECTORY -cd $NODE_DIRECTORY -docker run -v $PWD:/root/.celo --rm $CELO_IMAGE init /celo/genesis.json - -BOOTNODE_ENODES="$(docker run --rm --entrypoint cat $CELO_IMAGE /celo/bootnodes)" -PROXY_ADDRESS=${proxy_address} -echo -n '${proxy_private_key_password}' > .password -echo -n '${proxy_private_key_file_contents}' > keystore/${proxy_private_key_filename} -echo -n '${proxy_node_private_key}' > .nodeprivatekey - -CLOUDWATCH_LOG_GROUP_NAME=${cloudwatch_log_group_name} -CLOUDWATCH_LOG_STREAM_NAME=${cloudwatch_log_stream_name} - -if [[ -z $CLOUDWATCH_LOG_GROUP_NAME || -z $CLOUDWATCH_LOG_STREAM_NAME ]]; then - DOCKER_LOGGING_PARAMS='' -else - DOCKER_LOGGING_PARAMS="--log-driver=awslogs --log-opt awslogs-group=$CLOUDWATCH_LOG_GROUP_NAME --log-opt awslogs-stream=$CLOUDWATCH_LOG_STREAM_NAME" -fi - -docker run -d --name celo-proxy $DOCKER_LOGGING_PARAMS --restart unless-stopped -p 30303:30303 -p 30303:30303/udp -p 30503:30503 -p 30503:30503/udp -v $PWD:/root/.celo $CELO_IMAGE --verbosity 3 --networkid $NETWORK_ID --nousb --syncmode full --proxy.proxy --proxy.proxiedvalidatoraddress $CELO_VALIDATOR_SIGNER_ADDRESS --proxy.internalendpoint :30503 --etherbase $PROXY_ADDRESS --unlock $PROXY_ADDRESS --password /root/.celo/.password --allow-insecure-unlock --bootnodes $BOOTNODE_ENODES --ethstats=${validator_name}@${ethstats_host} --nodekey /root/.celo/.nodeprivatekey diff --git a/packages/terraform-modules-public/aws/testnet/modules/startup-scripts/run-validator-node.sh b/packages/terraform-modules-public/aws/testnet/modules/startup-scripts/run-validator-node.sh deleted file mode 100644 index 254abc03fb7..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/startup-scripts/run-validator-node.sh +++ /dev/null @@ -1,29 +0,0 @@ -# !/bin/bash -CELO_IMAGE=${celo_image} -NETWORK_ID=${celo_network_id} - -NODE_DIRECTORY=/home/ubuntu/celo-validator-node - -mkdir $NODE_DIRECTORY -cd $NODE_DIRECTORY -CELO_VALIDATOR_SIGNER_ADDRESS=${validator_signer_address} - -PROXY_ENODE=${proxy_enode} -PROXY_INTERNAL_IP=${proxy_internal_ip} -PROXY_EXTERNAL_IP=${proxy_external_ip} - -echo -n '${validator_signer_private_key_password}' > .password -docker run -v $PWD:/root/.celo --rm $CELO_IMAGE init /celo/genesis.json -echo -n '${validator_signer_private_key_file_contents}' > keystore/${validator_signer_private_key_filename} - -CLOUDWATCH_LOG_GROUP_NAME=${cloudwatch_log_group_name} -CLOUDWATCH_LOG_STREAM_NAME=${cloudwatch_log_stream_name} - -if [[ -z $CLOUDWATCH_LOG_GROUP_NAME || -z $CLOUDWATCH_LOG_STREAM_NAME ]]; then - DOCKER_LOGGING_PARAMS='' -else - DOCKER_LOGGING_PARAMS="--log-driver=awslogs --log-opt awslogs-group=$CLOUDWATCH_LOG_GROUP_NAME --log-opt awslogs-stream=$CLOUDWATCH_LOG_STREAM_NAME" -fi - -docker run -d --name celo-validator $DOCKER_LOGGING_PARAMS --restart unless-stopped -p 30303:30303 -p 30303:30303/udp -v $PWD:/root/.celo $CELO_IMAGE --verbosity 3 --networkid $NETWORK_ID --syncmode full --mine --etherbase $CELO_VALIDATOR_SIGNER_ADDRESS --nodiscover --nousb --proxy.proxied --proxy.proxyenodeurlpair=enode://$PROXY_ENODE@$PROXY_INTERNAL_IP:30503\;enode://$PROXY_ENODE@$PROXY_EXTERNAL_IP:30303 --unlock=$CELO_VALIDATOR_SIGNER_ADDRESS --password /root/.celo/.password --ethstats=${validator_name}@${ethstats_host} - diff --git a/packages/terraform-modules-public/aws/testnet/modules/validator/main.tf b/packages/terraform-modules-public/aws/testnet/modules/validator/main.tf deleted file mode 100644 index c73d036bca8..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/validator/main.tf +++ /dev/null @@ -1,52 +0,0 @@ -module "ami" { - source = "../ami" -} - -resource "aws_instance" "celo_validator" { - for_each = var.validators - - ami = module.ami.ami_ids.ubuntu_18_04 - instance_type = var.instance_type - subnet_id = var.subnet_id - vpc_security_group_ids = [var.security_group_id] - key_name = var.key_pair_name - iam_instance_profile = var.iam_instance_profile - - root_block_device { - volume_size = var.volume_size - } - - user_data = join("\n", [ - file("${path.module}/../startup-scripts/install-base.sh"), - var.cloudwatch_collect_disk_and_memory_usage ? file("${path.module}/../startup-scripts/install-cloudwatch-agent.sh") : "", - file("${path.module}/../startup-scripts/install-docker.sh"), - file("${path.module}/../startup-scripts/install-chrony.sh"), - templatefile("${path.module}/../startup-scripts/run-validator-node.sh", { - celo_image = var.celo_image - celo_network_id = var.celo_network_id - ethstats_host = var.ethstats_host - validator_signer_address = each.value.signer_address - validator_signer_private_key_file_contents = each.value.signer_private_key_file_contents - validator_signer_private_key_filename = each.value.signer_private_key_filename - validator_signer_private_key_password = each.value.signer_private_key_password - validator_name = each.value.name - proxy_enode = each.value.proxy_enode - proxy_internal_ip = each.value.proxy_private_ip - proxy_external_ip = each.value.proxy_public_ip - cloudwatch_log_group_name = var.cloudwatch_log_group_name - cloudwatch_log_stream_name = "celo_validator_${each.key}" - }), - file("${path.module}/../startup-scripts/final-hardening.sh") - ]) - - tags = { - Name = "celo-validator-${each.value.name}" - } - - lifecycle { - ignore_changes = [ - ami, - user_data - ] - } -} \ No newline at end of file diff --git a/packages/terraform-modules-public/aws/testnet/modules/validator/outputs.tf b/packages/terraform-modules-public/aws/testnet/modules/validator/outputs.tf deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/packages/terraform-modules-public/aws/testnet/modules/validator/variables.tf b/packages/terraform-modules-public/aws/testnet/modules/validator/variables.tf deleted file mode 100644 index 7ba84c14e78..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/validator/variables.tf +++ /dev/null @@ -1,69 +0,0 @@ -variable instance_type { - type = string - description = "AWS instance type for this node" -} - -variable subnet_id { - type = string - description = "Subnet ID to place this proxy. This should be a public subnet from your Celo VPC." -} - -variable security_group_id { - type = string - description = "VPC Security group for this instance" -} - -variable key_pair_name { - type = string - description = "Name of the SSH key pair to access this node from the bastion" -} - -variable volume_size { - type = number - description = "GB size for the EBS volume" - default = 256 -} - -variable celo_image { - type = string - description = "Name of the docker image to run" -} - -variable celo_network_id { - type = string - description = "Celo network ID to join" -} - -variable ethstats_host { - type = string - description = "Hostname for ethstats" -} - -variable validators { - description = "Map of validator configurations" - type = map(object({ - name = string - signer_address = string - signer_private_key_file_contents = string - signer_private_key_password = string - signer_private_key_filename = string - proxy_enode = string - proxy_private_ip = string - proxy_public_ip = string - })) -} - -variable iam_instance_profile { - type = string - default = null -} - -variable cloudwatch_log_group_name { - type = string - default = "" -} - -variable cloudwatch_collect_disk_and_memory_usage { - type = bool - default = false -} diff --git a/packages/terraform-modules-public/aws/testnet/modules/vpc/main.tf b/packages/terraform-modules-public/aws/testnet/modules/vpc/main.tf deleted file mode 100644 index f9882e96517..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/vpc/main.tf +++ /dev/null @@ -1,246 +0,0 @@ -resource "aws_vpc" "celo" { - cidr_block = var.cidr_blocks.vpc - - tags = { - Name = var.name - } -} - -resource "aws_default_security_group" "default" { - vpc_id = aws_vpc.celo.id - - ingress { - protocol = -1 - self = true - from_port = 0 - to_port = 0 - } -} - -resource "aws_internet_gateway" "igw" { - vpc_id = aws_vpc.celo.id - - tags = { - Name = "celo-internet-gateway" - } -} - -data "aws_availability_zones" "available" { - state = "available" -} - - -module "celo_public_subnet_az1" { - source = "./modules/subnet-public" - - vpc_id = aws_vpc.celo.id - cidr_block = var.cidr_blocks.subnet_az1_public - internet_gateway_id = aws_internet_gateway.igw.id - availability_zone_id = data.aws_availability_zones.available.zone_ids[0] - allowed_ssh_clients_cidr_block = var.cidr_blocks.allowed_ssh_clients -} - -module "celo_private_subnet_az1" { - source = "./modules/subnet-private" - - vpc_id = aws_vpc.celo.id - cidr_block = var.cidr_blocks.subnet_az1_private - availability_zone_id = data.aws_availability_zones.available.zone_ids[0] - nat_gateway_id = module.celo_public_subnet_az1.nat_gateway_id - vpc_cidr_block = aws_vpc.celo.cidr_block -} - -module "celo_public_subnet_az2" { - source = "./modules/subnet-public" - - vpc_id = aws_vpc.celo.id - cidr_block = var.cidr_blocks.subnet_az2_public - internet_gateway_id = aws_internet_gateway.igw.id - availability_zone_id = data.aws_availability_zones.available.zone_ids[1] - allowed_ssh_clients_cidr_block = var.cidr_blocks.allowed_ssh_clients -} - -module "celo_private_subnet_az2" { - source = "./modules/subnet-private" - - vpc_id = aws_vpc.celo.id - cidr_block = var.cidr_blocks.subnet_az2_private - availability_zone_id = data.aws_availability_zones.available.zone_ids[1] - nat_gateway_id = module.celo_public_subnet_az2.nat_gateway_id - vpc_cidr_block = aws_vpc.celo.cidr_block -} - -resource "aws_security_group" "attestation_service" { - name = "celo-attestation-service" - vpc_id = aws_vpc.celo.id - - ingress { - from_port = 22 - to_port = 22 - protocol = "tcp" - security_groups = [aws_security_group.bastion.id] - } - - ingress { - from_port = 80 - to_port = 80 - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] - } - - egress { - from_port = 0 - to_port = 0 - protocol = -1 - cidr_blocks = ["0.0.0.0/0"] - } -} - -resource "aws_security_group" "attestation_db" { - name = "celo-attestation-db" - vpc_id = aws_vpc.celo.id - - ingress { - from_port = 5432 - to_port = 5432 - protocol = "tcp" - security_groups = [aws_security_group.attestation_service.id] - } -} - -resource "aws_security_group" "bastion" { - name = "celo-bastion" - vpc_id = aws_vpc.celo.id - - ingress { - from_port = 22 - to_port = 22 - protocol = "tcp" - cidr_blocks = [var.cidr_blocks.allowed_ssh_clients] - } - - egress { - from_port = 0 - to_port = 0 - protocol = -1 - cidr_blocks = ["0.0.0.0/0"] - } -} - -resource "aws_security_group" "full_node" { - name = "celo-full-node" - vpc_id = aws_vpc.celo.id - - ingress { - from_port = 22 - to_port = 22 - protocol = "tcp" - security_groups = [aws_security_group.bastion.id] - } - - ingress { - from_port = 30303 - to_port = 30303 - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] - } - - ingress { - from_port = 30303 - to_port = 30303 - protocol = "udp" - cidr_blocks = ["0.0.0.0/0"] - } - - egress { - from_port = 0 - to_port = 0 - protocol = -1 - cidr_blocks = ["0.0.0.0/0"] - } -} - -resource "aws_security_group" "proxy" { - name = "celo-proxy" - vpc_id = aws_vpc.celo.id -} - -resource "aws_security_group" "validator" { - name = "celo-validator" - vpc_id = aws_vpc.celo.id -} - -resource "aws_security_group_rule" "validator_allow_private_ssh" { - type = "ingress" - from_port = 22 - to_port = 22 - protocol = "tcp" - security_group_id = aws_security_group.validator.id - source_security_group_id = aws_security_group.bastion.id -} - -resource "aws_security_group_rule" "validator_allow_proxy_inbound" { - type = "ingress" - from_port = 30303 - to_port = 30303 - protocol = "tcp" - security_group_id = aws_security_group.validator.id - source_security_group_id = aws_security_group.proxy.id -} - -resource "aws_security_group_rule" "validator_allow_all_outbound" { - type = "egress" - from_port = 0 - to_port = 0 - protocol = -1 - security_group_id = aws_security_group.validator.id - cidr_blocks = ["0.0.0.0/0"] -} - -resource "aws_security_group_rule" "proxy_allow_internal_ssh" { - type = "ingress" - from_port = 22 - to_port = 22 - protocol = "tcp" - security_group_id = aws_security_group.proxy.id - source_security_group_id = aws_security_group.bastion.id -} - -resource "aws_security_group_rule" "proxy_allow_external_tcp_inbound" { - type = "ingress" - from_port = 30303 - to_port = 30303 - protocol = "tcp" - security_group_id = aws_security_group.proxy.id - cidr_blocks = ["0.0.0.0/0"] -} - -resource "aws_security_group_rule" "proxy_allow_external_udp_inbound" { - type = "ingress" - from_port = 30303 - to_port = 30303 - protocol = "udp" - security_group_id = aws_security_group.proxy.id - cidr_blocks = ["0.0.0.0/0"] -} - -resource "aws_security_group_rule" "proxy_allow_validator_inbound" { - type = "ingress" - from_port = 30503 - to_port = 30503 - protocol = "tcp" - security_group_id = aws_security_group.proxy.id - source_security_group_id = aws_security_group.validator.id -} - -resource "aws_security_group_rule" "proxy_allow_all_outbound" { - type = "egress" - from_port = 0 - to_port = 0 - protocol = -1 - security_group_id = aws_security_group.proxy.id - cidr_blocks = ["0.0.0.0/0"] -} - - - diff --git a/packages/terraform-modules-public/aws/testnet/modules/vpc/modules/subnet-private/main.tf b/packages/terraform-modules-public/aws/testnet/modules/vpc/modules/subnet-private/main.tf deleted file mode 100644 index d7adaf39ac9..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/vpc/modules/subnet-private/main.tf +++ /dev/null @@ -1,82 +0,0 @@ -resource "aws_subnet" "private" { - vpc_id = var.vpc_id - cidr_block = var.cidr_block - availability_zone_id = var.availability_zone_id - - tags = { - Name = "celo-private-${var.availability_zone_id}" - } -} - -resource "aws_route_table" "private" { - vpc_id = var.vpc_id - - route { - cidr_block = "0.0.0.0/0" - nat_gateway_id = var.nat_gateway_id - } - - tags = { - Name = "celo-private-route-table-${var.availability_zone_id}" - } -} - -resource "aws_route_table_association" "private" { - subnet_id = aws_subnet.private.id - route_table_id = aws_route_table.private.id -} - -resource "aws_network_acl" "private" { - vpc_id = var.vpc_id - subnet_ids = [aws_subnet.private.id] - - tags = { - Name = "celo-private-acl-${var.availability_zone_id}" - } - - ingress { - rule_no = 100 - protocol = "tcp" - from_port = 22 - to_port = 22 - cidr_block = var.vpc_cidr_block - action = "allow" - } - - ingress { - rule_no = 110 - protocol = "tcp" - from_port = 5432 - to_port = 5432 - cidr_block = var.vpc_cidr_block - action = "allow" - } - - ingress { - rule_no = 130 - protocol = "tcp" - from_port = 30303 - to_port = 30303 - cidr_block = var.vpc_cidr_block - action = "allow" - } - - ingress { - rule_no = 140 - protocol = "tcp" - from_port = 1024 - to_port = 65535 - cidr_block = "0.0.0.0/0" - action = "allow" - } - - egress { - rule_no = 100 - protocol = -1 - from_port = 0 - to_port = 0 - cidr_block = "0.0.0.0/0" - action = "allow" - } -} - diff --git a/packages/terraform-modules-public/aws/testnet/modules/vpc/modules/subnet-private/outputs.tf b/packages/terraform-modules-public/aws/testnet/modules/vpc/modules/subnet-private/outputs.tf deleted file mode 100644 index 4ebd1cc6577..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/vpc/modules/subnet-private/outputs.tf +++ /dev/null @@ -1,3 +0,0 @@ -output id { - value = aws_subnet.private.id -} \ No newline at end of file diff --git a/packages/terraform-modules-public/aws/testnet/modules/vpc/modules/subnet-private/variables.tf b/packages/terraform-modules-public/aws/testnet/modules/vpc/modules/subnet-private/variables.tf deleted file mode 100644 index f1b14ac8a69..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/vpc/modules/subnet-private/variables.tf +++ /dev/null @@ -1,24 +0,0 @@ -variable vpc_id { - type = string - description = "VPC ID this subnet will belong to" -} - -variable cidr_block { - type = string - description = "CIDR block for this subnet" -} - -variable availability_zone_id { - type = string - description = "Availability zone for this subnet" -} - -variable nat_gateway_id { - type = string - description = "NAT Gateway so this subnet can reach the internet" -} - -variable vpc_cidr_block { - type = string - description = "CIDR block for the VPC this subnet belongs to" -} diff --git a/packages/terraform-modules-public/aws/testnet/modules/vpc/modules/subnet-public/main.tf b/packages/terraform-modules-public/aws/testnet/modules/vpc/modules/subnet-public/main.tf deleted file mode 100644 index 5502f75d979..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/vpc/modules/subnet-public/main.tf +++ /dev/null @@ -1,118 +0,0 @@ -resource "aws_subnet" "public" { - vpc_id = var.vpc_id - cidr_block = var.cidr_block - availability_zone_id = var.availability_zone_id - - tags = { - Name = "celo-public-${var.availability_zone_id}" - } -} - -resource "aws_eip" "nat" { - vpc = true - - tags = { - Name = "celo-nat-eip" - } -} - -resource "aws_nat_gateway" "nat" { - allocation_id = aws_eip.nat.id - subnet_id = aws_subnet.public.id - - tags = { - Name = "celo-nat-gateway-${var.availability_zone_id}" - } -} - -resource "aws_route_table" "public" { - vpc_id = var.vpc_id - - route { - cidr_block = "0.0.0.0/0" - gateway_id = var.internet_gateway_id - } - - tags = { - Name = "celo-public-route-table-${var.availability_zone_id}" - } -} - -resource "aws_route_table_association" "public" { - subnet_id = aws_subnet.public.id - route_table_id = aws_route_table.public.id -} - - -resource "aws_network_acl" "public" { - vpc_id = var.vpc_id - subnet_ids = [aws_subnet.public.id] - - tags = { - Name = "celo-public-acl-${var.availability_zone_id}" - } - - ingress { - rule_no = 100 - protocol = "tcp" - from_port = 22 - to_port = 22 - cidr_block = var.allowed_ssh_clients_cidr_block - action = "allow" - } - - ingress { - rule_no = 110 - protocol = "tcp" - from_port = 80 - to_port = 80 - cidr_block = "0.0.0.0/0" - action = "allow" - } - - ingress { - rule_no = 120 - protocol = "tcp" - from_port = 443 - to_port = 443 - cidr_block = "0.0.0.0/0" - action = "allow" - } - - ingress { - rule_no = 130 - protocol = "tcp" - from_port = 30303 - to_port = 30303 - cidr_block = "0.0.0.0/0" - action = "allow" - } - - ingress { - rule_no = 131 - protocol = "udp" - from_port = 30303 - to_port = 30303 - cidr_block = "0.0.0.0/0" - action = "allow" - } - - ingress { - rule_no = 140 - protocol = "tcp" - from_port = 1024 - to_port = 65535 - cidr_block = "0.0.0.0/0" - action = "allow" - } - - egress { - rule_no = 200 - protocol = -1 - to_port = 0 - from_port = 0 - cidr_block = "0.0.0.0/0" - action = "allow" - } -} - diff --git a/packages/terraform-modules-public/aws/testnet/modules/vpc/modules/subnet-public/outputs.tf b/packages/terraform-modules-public/aws/testnet/modules/vpc/modules/subnet-public/outputs.tf deleted file mode 100644 index 4c66e4ce3db..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/vpc/modules/subnet-public/outputs.tf +++ /dev/null @@ -1,7 +0,0 @@ -output nat_gateway_id { - value = aws_nat_gateway.nat.id -} - -output id { - value = aws_subnet.public.id -} \ No newline at end of file diff --git a/packages/terraform-modules-public/aws/testnet/modules/vpc/modules/subnet-public/variables.tf b/packages/terraform-modules-public/aws/testnet/modules/vpc/modules/subnet-public/variables.tf deleted file mode 100644 index 51d31c59079..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/vpc/modules/subnet-public/variables.tf +++ /dev/null @@ -1,24 +0,0 @@ -variable vpc_id { - type = string - description = "VPC ID this subnet will belong to" -} - -variable cidr_block { - type = string - description = "CIDR block for this subnet" -} - -variable availability_zone_id { - type = string - description = "Availability zone for this subnet" -} - -variable internet_gateway_id { - type = string - description = "ID for the internet gateway this subnet will route to" -} - -variable "allowed_ssh_clients_cidr_block" { - type = string - description = "CIDR block of allowed SSH clients." -} \ No newline at end of file diff --git a/packages/terraform-modules-public/aws/testnet/modules/vpc/outputs.tf b/packages/terraform-modules-public/aws/testnet/modules/vpc/outputs.tf deleted file mode 100644 index 8269bcb62ca..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/vpc/outputs.tf +++ /dev/null @@ -1,27 +0,0 @@ -output security_group_ids { - value = { - attestation_service = aws_security_group.attestation_service.id - attestation_db = aws_security_group.attestation_db.id - bastion = aws_security_group.bastion.id - full_node = aws_security_group.full_node.id - validator = aws_security_group.validator.id - proxy = aws_security_group.proxy.id - } -} - -output subnet_ids { - value = { - az1 = { - private = module.celo_private_subnet_az1.id - public = module.celo_public_subnet_az1.id - } - az2 = { - private = module.celo_private_subnet_az2.id - public = module.celo_public_subnet_az2.id - } - } -} - -output id { - value = aws_vpc.celo.id -} \ No newline at end of file diff --git a/packages/terraform-modules-public/aws/testnet/modules/vpc/variables.tf b/packages/terraform-modules-public/aws/testnet/modules/vpc/variables.tf deleted file mode 100644 index ed7ccc830f3..00000000000 --- a/packages/terraform-modules-public/aws/testnet/modules/vpc/variables.tf +++ /dev/null @@ -1,25 +0,0 @@ -variable name { - type = string - description = "Name of the VPC" - default = "celo-vpc" -} - -variable cidr_blocks { - type = object({ - vpc = string - subnet_az1_public = string - subnet_az1_private = string - subnet_az2_public = string - subnet_az2_private = string - allowed_ssh_clients = string - }) - description = "The cidr_blocks for the different subnets in a redundant Celo network" - default = { - vpc = "10.10.0.0/16" - subnet_az1_public = "10.10.0.0/24" - subnet_az1_private = "10.10.1.0/24" - subnet_az2_public = "10.10.10.0/24" - subnet_az2_private = "10.10.11.0/24" - allowed_ssh_clients = "0.0.0.0/0" - } -} \ No newline at end of file diff --git a/packages/terraform-modules-public/aws/testnet/outputs.tf b/packages/terraform-modules-public/aws/testnet/outputs.tf deleted file mode 100644 index 23470b6e0ea..00000000000 --- a/packages/terraform-modules-public/aws/testnet/outputs.tf +++ /dev/null @@ -1,3 +0,0 @@ -output vpc { - value = module.celo_vpc -} \ No newline at end of file diff --git a/packages/terraform-modules-public/aws/testnet/variables.tf b/packages/terraform-modules-public/aws/testnet/variables.tf deleted file mode 100644 index 6d4150e1b3f..00000000000 --- a/packages/terraform-modules-public/aws/testnet/variables.tf +++ /dev/null @@ -1,192 +0,0 @@ -variable region { - type = string - description = "AWS Region to provision this cluster" -} - -variable cidr_blocks { - type = object({ - vpc = string - subnet_az1_public = string - subnet_az1_private = string - subnet_az2_public = string - subnet_az2_private = string - allowed_ssh_clients = string - }) - description = "The cidr_blocks for the different subnets in a redundant Celo network" - default = { - vpc = "10.10.0.0/16" - subnet_az1_public = "10.10.0.0/24" - subnet_az1_private = "10.10.1.0/24" - subnet_az2_public = "10.10.10.0/24" - subnet_az2_private = "10.10.11.0/24" - allowed_ssh_clients = "0.0.0.0/0" - } -} - -variable instance_types { - description = "The instance type for each component" - type = map(string) - - default = { - bastion = "t3.micro" - proxy = "t3.medium" # t3.medium to keep costs low in dev. Use c5.xlarge or similar in production - validator = "t3.medium" # t3.medium to keep costs low in dev. Use c5.xlarge or similar in production - attestation_service = "t3.medium" # t3.medium to keep costs low in dev. Use t3.large or similar in production - } -} - -variable iam_instance_profiles { - description = "Optional IAM instances profile names for each component. For example, a profile that gives CloudWatch agent permissions (https://amzn.to/3gpfCt5)" - type = map(string) - - default = { - bastion = null - proxy = null - validator = null - attestation_service = null - } -} - -variable cloudwatch_log_group_names { - description = "Optional log group names to log Docker output to CloudWatch. If this is set for a node type, that node will install the CloudWatch agent. Make sure iam_instance_profile is also set on that node type with CloudWatch agent permissions." - type = map(string) - - default = { - proxy = "" - validator = "" - attestation_service = "" - attestation_node = "" - } - -} - -variable cloudwatch_collect_disk_and_memory_usage { - description = "Determines if the CloudWatch agent should be installed to collect disk and memory usage" - type = bool - default = false -} - -variable key_pair_name { - type = string - description = "AWS Key Pair name for SSH access" -} - -variable celo_image { - type = string - description = "Docker image for Celo nodes" -} - -variable celo_network_id { - type = string - description = "ID of the Celo network to join" -} - -variable celo_image_attestation { - type = string - description = "Docker image for Celo attestation service" -} - -variable ethstats_host { - type = string - description = "Hostname for ethstats" -} - -variable twilio_messaging_service_sid { - type = string -} - -variable twilio_verify_service_sid { - type = string -} - -variable twilio_account_sid { - type = string -} - -variable twilio_unsupported_regions { - type = string -} - -variable twilio_auth_token { - type = string -} - -variable nexmo_api_key { - type = string -} - -variable nexmo_api_secret { - type = string -} - -variable nexmo_unsupported_regions { - type = string -} - -variable proxies { - description = "Configuration for zero or more proxies in each availability zone." - type = object({ - az1 = map(object({ - validator_name = string - validator_signer_address = string - proxy_address = string - proxy_private_key_filename = string - proxy_private_key_file_contents = string - proxy_private_key_password = string - proxy_node_private_key = string - proxy_enode = string - })) - az2 = map(object({ - validator_name = string - validator_signer_address = string - proxy_address = string - proxy_private_key_filename = string - proxy_private_key_file_contents = string - proxy_private_key_password = string - proxy_node_private_key = string - proxy_enode = string - })) - }) -} - -variable validators { - description = "Configuration for zero or more validators in each availability zone" - type = object({ - az1 = map(object({ - name = string - signer_address = string - signer_private_key_filename = string - signer_private_key_file_contents = string - signer_private_key_password = string - })) - az2 = map(object({ - name = string - signer_address = string - signer_private_key_filename = string - signer_private_key_file_contents = string - signer_private_key_password = string - })) - }) -} - -variable attestation_services { - description = "Configuration for zero or more attestation nodes in each availability zone" - type = object({ - az1 = map(object({ - validator_name = string - validator_address = string - attestation_signer_address = string - attestation_signer_private_key_filename = string - attestation_signer_private_key_file_contents = string - attestation_signer_private_key_password = string - })) - az2 = map(object({ - validator_name = string - validator_address = string - attestation_signer_address = string - attestation_signer_private_key_filename = string - attestation_signer_private_key_file_contents = string - attestation_signer_private_key_password = string - })) - }) -} \ No newline at end of file diff --git a/packages/terraform-modules-public/example/.gitignore b/packages/terraform-modules-public/example/.gitignore deleted file mode 100644 index 6fd45f6f257..00000000000 --- a/packages/terraform-modules-public/example/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.tfvars - diff --git a/packages/terraform-modules-public/gcp/README.md b/packages/terraform-modules-public/gcp/README.md deleted file mode 100644 index 6f64a963407..00000000000 --- a/packages/terraform-modules-public/gcp/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# Terraform Celo Validator Stack for GCP - -## Overview - -[Terraform](https://www.terraform.io) is a tool by Hashicorp that allows developers to treat _"infrastructure as code"_, easying the management and repeatibility of the infrastructure. Infrastructure and all kind of cloud resources are defined in modules, and Terraform creates/changes/destroys resources when changes are applied. - -Inside the [testnet](./testnet) folder you will find a module (and submodules) to create the setup for running a Celo Validator on Google Cloud Platform. The following resources can be created via these modules: - -- `proxy` module for creating a Geth Proxy which peers with other Celo nodes over the public Internet -- `validator` module for deploying a Validator which peers *only* with the proxy -- `tx-node` for deploying a transaction node (also known as full-node) which is used to support the attestation service, which connects to the RPC via the VPC -- `attestation-service` for deploying the Attestation Service (https://docs.celo.org/getting-started/baklava-testnet/running-a-validator#running-the-attestation-service) - -The proxy, validator and tx-node services expose metrics for collection via Prometheus or similar. See [example/metrics.md](./example/metrics.md) for more info. - -## Stackdriver Logging, Monitoring and Alerting -Support for GCP's Stackdriver platform has been added, which makes it easy to get visibility into how your Celo validator stack is performing. - -## Quick start -Look inside the [example](./example) folder and follow the steps in the README.md there to get started. \ No newline at end of file diff --git a/packages/terraform-modules-public/gcp/celo-infra/main.tf b/packages/terraform-modules-public/gcp/celo-infra/main.tf deleted file mode 100644 index f97db443e9b..00000000000 --- a/packages/terraform-modules-public/gcp/celo-infra/main.tf +++ /dev/null @@ -1,273 +0,0 @@ -provider "google" { - project = var.gcloud_project - region = var.gcloud_region - zone = var.gcloud_zone -} - -locals { - firewall_target_tags_txnode = ["${var.celo_env}-txnode"] - firewall_target_tags_validator = ["${var.celo_env}-validator"] - firewall_target_tags_proxy = ["${var.celo_env}-proxy"] - firewall_target_tags_attestation_service = ["${var.celo_env}-attestation-service"] - firewall_target_tags_backup_node = ["${var.celo_env}-backup-node"] -} - -# Dummy variable for network dependency -variable network_depends_on { - type = any - default = null -} - -data "google_compute_network" "celo" { - name = var.network_name - depends_on = [var.network_depends_on] -} - -data "google_compute_subnetwork" "celo" { - name = var.network_name - region = var.gcloud_region - depends_on = [var.network_depends_on] -} - -# GCP resources -resource "google_compute_firewall" "ssh_firewall" { - name = "${var.celo_env}-ssh-firewall" - network = var.network_name - - target_tags = concat( - local.firewall_target_tags_txnode, - local.firewall_target_tags_validator, - local.firewall_target_tags_proxy, - local.firewall_target_tags_attestation_service, - local.firewall_target_tags_backup_node - ) - - allow { - protocol = "tcp" - ports = ["22"] - } -} - -resource "google_compute_firewall" "geth_firewall" { - name = "${var.celo_env}-geth-firewall" - network = var.network_name - - target_tags = concat(local.firewall_target_tags_txnode, local.firewall_target_tags_proxy, local.firewall_target_tags_backup_node) - - allow { - protocol = "tcp" - ports = ["30303"] - } - - allow { - protocol = "udp" - ports = ["30303"] - } -} - -#opening tcp/30303 to the validator is unnecessary, as the validator peers via the proxy and has no public IP -#resource "google_compute_firewall" "geth_firewall_validator" { -# name = "${var.celo_env}-geth-firewall-validator" -# network = var.network_name - -# target_tags = concat(local.firewall_target_tags_validator) - -# allow { - # protocol = "tcp" - # ports = ["30303"] -# } -#} - -resource "google_compute_firewall" "geth_metrics_firewall" { - name = "${var.celo_env}-geth-metrics-firewall" - network = var.network_name - - target_tags = concat(local.firewall_target_tags_txnode, local.firewall_target_tags_validator, local.firewall_target_tags_proxy, local.firewall_target_tags_backup_node) - - # allow all IPs internal to the VPC - source_ranges = [data.google_compute_subnetwork.celo.ip_cidr_range] - - allow { - protocol = "tcp" - ports = ["6060"] - } -} - -resource "google_compute_firewall" "rpc_firewall" { - name = "${var.celo_env}-rpc-firewall" - network = var.network_name - - target_tags = local.firewall_target_tags_txnode - - source_ranges = [data.google_compute_subnetwork.celo.ip_cidr_range] - - allow { - protocol = "tcp" - ports = ["8545", "8546"] - } -} - -resource "google_compute_firewall" "proxy" { - name = "${var.celo_env}-proxy-firewall" - network = var.network_name - - target_tags = local.firewall_target_tags_proxy - source_ranges = [data.google_compute_subnetwork.celo.ip_cidr_range] - - allow { - protocol = "tcp" - ports = ["30503"] - } -} - -resource "google_compute_firewall" "attestation-service" { - name = "${var.celo_env}-attestation-service-firewall" - network = var.network_name - - target_tags = local.firewall_target_tags_attestation_service - #source_ranges = [data.google_compute_subnetwork.celo.ip_cidr_range] - - allow { - protocol = "tcp" - ports = ["80"] - } -} - -module "tx_node" { - source = "./modules/tx-node" - # variables - block_time = var.block_time - celo_env = var.celo_env - gcloud_project = var.gcloud_project - instance_type = var.instance_types["txnode"] - ethstats_host = var.ethstats_host - geth_exporter_docker_image_repository = var.geth_exporter_docker_image_repository - geth_exporter_docker_image_tag = var.geth_exporter_docker_image_tag - geth_node_docker_image_repository = var.geth_node_docker_image_repository - geth_node_docker_image_tag = var.geth_node_docker_image_tag - geth_verbosity = var.geth_verbosity - in_memory_discovery_table = var.in_memory_discovery_table - network_id = var.network_id - network_name = var.network_name - tx_node_count = var.tx_node_count - attestation_signer_addresses = var.attestation_signer_addresses - attestation_signer_private_keys = var.attestation_signer_private_keys - attestation_signer_account_passwords = var.attestation_signer_account_passwords - service_account_scopes = var.service_account_scopes -} - -module "backup_node" { - source = "./modules/backup_node" - # variables - block_time = var.block_time - celo_env = var.celo_env - gcloud_project = var.gcloud_project - instance_type = var.instance_types["backup_node"] - ethstats_host = var.ethstats_host - geth_exporter_docker_image_repository = var.geth_exporter_docker_image_repository - geth_exporter_docker_image_tag = var.geth_exporter_docker_image_tag - geth_node_docker_image_repository = var.geth_node_docker_image_repository - geth_node_docker_image_tag = var.geth_node_docker_image_tag - geth_verbosity = var.geth_verbosity - in_memory_discovery_table = var.in_memory_discovery_table - network_id = var.network_id - network_name = var.network_name - backup_node_count = var.backup_node_count - service_account_scopes = var.service_account_scopes -} - -module "proxy" { - source = "./modules/proxy" - # variables - block_time = var.block_time - celo_env = var.celo_env - gcloud_project = var.gcloud_project - instance_type = var.instance_types["proxy"] - ethstats_host = var.ethstats_host - geth_exporter_docker_image_repository = var.geth_exporter_docker_image_repository - geth_exporter_docker_image_tag = var.geth_exporter_docker_image_tag - geth_node_docker_image_repository = var.geth_node_docker_image_repository - geth_node_docker_image_tag = var.geth_node_docker_image_tag - geth_verbosity = var.geth_verbosity - in_memory_discovery_table = var.in_memory_discovery_table - istanbul_request_timeout_ms = var.istanbul_request_timeout_ms - network_id = var.network_id - network_name = var.network_name - tx_node_count = var.tx_node_count - validator_count = var.validator_count - reset_geth_data = var.reset_geth_data - - proxy_name = var.proxy_name - proxy_addresses = var.proxy_addresses - proxy_private_keys = var.proxy_private_keys - proxy_account_passwords = var.proxy_account_passwords - validator_signer_account_addresses = var.validator_signer_account_addresses - service_account_scopes = var.service_account_scopes -} - -module "validator" { - source = "./modules/validator" - # variables - block_time = var.block_time - celo_env = var.celo_env - gcloud_project = var.gcloud_project - instance_type = var.instance_types["validator"] - ethstats_host = var.ethstats_host - geth_exporter_docker_image_repository = var.geth_exporter_docker_image_repository - geth_exporter_docker_image_tag = var.geth_exporter_docker_image_tag - geth_node_docker_image_repository = var.geth_node_docker_image_repository - geth_node_docker_image_tag = var.geth_node_docker_image_tag - geth_verbosity = var.geth_verbosity - in_memory_discovery_table = var.in_memory_discovery_table - istanbul_request_timeout_ms = var.istanbul_request_timeout_ms - network_id = var.network_id - network_name = var.network_name - tx_node_count = var.tx_node_count - validator_count = var.validator_count - reset_geth_data = var.reset_geth_data - - validator_name = var.validator_name - validator_signer_account_addresses = var.validator_signer_account_addresses - validator_signer_account_passwords = var.validator_signer_account_passwords - validator_signer_private_keys = var.validator_signer_private_keys - proxy_enodes = var.proxy_enodes - proxy_internal_ips = module.proxy.internal_ip_addresses - proxy_external_ips = module.proxy.external_ip_addresses - - service_account_scopes = var.service_account_scopes -} - -module "attestation-service" { - source = "./modules/attestation-service" - # Variables - celo_env = var.celo_env - gcloud_region = var.gcloud_region - gcloud_project = var.gcloud_project - instance_type = var.instance_types["attestation_service"] - network_name = var.network_name - attestation_service_count = var.attestation_service_count - db_username = var.attestation_service_db_username - db_password = var.attestation_service_db_password - attestation_service_docker_image_repository = var.attestation_service_docker_image_repository - attestation_service_docker_image_tag = var.attestation_service_docker_image_tag - account_address = var.attestation_signer_addresses - attestation_key = var.attestation_signer_private_keys - validator_signer_account_addresses = var.validator_signer_account_addresses - validator_release_gold_addresses = var.validator_release_gold_addresses - celo_provider = var.attestation_service_celo_provider != "" ? var.attestation_service_celo_provider : "http://${module.tx_node.internal_ip_addresses[0]}:8545" - #celo_provider = var.attestation_service_celo_provider != "" ? var.attestation_service_celo_provider : "http://localhost:8545" - sms_providers = var.attestation_service_sms_providers - nexmo_key = var.attestation_service_nexmo_key - nexmo_secret = var.attestation_service_nexmo_secret - nexmo_blacklist = var.attestation_service_nexmo_blacklist - nexmo_unsupported_regions = var.attestation_service_nexmo_unsupported_regions - twilio_account_sid = var.attestation_service_twilio_account_sid - twilio_messaging_service_sid = var.attestation_service_twilio_messaging_service_sid - twilio_verify_service_sid = var.attestation_service_twilio_verify_service_sid - twilio_auth_token = var.attestation_service_twilio_auth_token - twilio_blacklist = var.attestation_service_twilio_blacklist - twilio_unsupported_regions = var.attestation_service_twilio_unsupported_regions - messagebird_api_key = var.attestation_service_messagebird_api_key - messagebird_unsupported_regions = var.attestation_service_messagebird_unsupported_regions - service_account_scopes = var.service_account_scopes -} diff --git a/packages/terraform-modules-public/gcp/celo-infra/modules/attestation-service/main.tf b/packages/terraform-modules-public/gcp/celo-infra/modules/attestation-service/main.tf deleted file mode 100644 index 21f07da7843..00000000000 --- a/packages/terraform-modules-public/gcp/celo-infra/modules/attestation-service/main.tf +++ /dev/null @@ -1,98 +0,0 @@ -locals { - name_prefix = "${var.gcloud_project}-attestation-svc" -} - -resource "google_sql_database_instance" "main" { - count = var.attestation_service_count > 0 ? 1 : 0 - name = "${local.name_prefix}-db-${random_id.db_name.hex}" - database_version = "POSTGRES_9_6" - region = var.gcloud_region - - settings { - tier = "db-f1-micro" - } -} - -resource "google_sql_user" "celo" { - count = var.attestation_service_count > 0 ? 1 : 0 - name = var.db_username - instance = google_sql_database_instance.main[0].name - password = var.db_password -} - -resource "google_compute_address" "attestation_service" { - count = var.attestation_service_count > 0 ? var.attestation_service_count : 0 - name = "${local.name_prefix}-address" - address_type = "EXTERNAL" -} - -resource "google_compute_address" "attestation_service_internal" { - count = var.attestation_service_count > 0 ? var.attestation_service_count : 0 - name = "${local.name_prefix}-internal-address" - address_type = "INTERNAL" - purpose = "GCE_ENDPOINT" -} - -resource "google_compute_instance" "attestation_service" { - count = var.attestation_service_count > 0 ? var.attestation_service_count : 0 - name = "${local.name_prefix}-${count.index}" - machine_type = var.instance_type - - deletion_protection = false - - tags = ["${var.celo_env}-attestation-service"] - - allow_stopping_for_update = false # cannot update in place w/o a persistent disk - - boot_disk { - initialize_params { - image = "debian-cloud/debian-10" - } - } - - network_interface { - network = var.network_name - network_ip = google_compute_address.attestation_service_internal[count.index].address - access_config { - nat_ip = google_compute_address.attestation_service[count.index].address - } - } - - metadata_startup_script = templatefile( - format("%s/startup.sh", path.module), { - rid : count.index, - attestation_key : "0x${var.attestation_key[count.index]}", - account_address : var.account_address[count.index], - validator_signer_address : var.validator_signer_account_addresses[count.index], - validator_release_gold_address : var.validator_release_gold_addresses[count.index], - celo_provider : var.celo_provider, - attestation_service_docker_image_repository : var.attestation_service_docker_image_repository, - attestation_service_docker_image_tag : var.attestation_service_docker_image_tag, - db_username : google_sql_user.celo[0].name, - db_password : google_sql_user.celo[0].password, - db_connection_name : google_sql_database_instance.main[0].connection_name, - sms_providers : var.sms_providers, - nexmo_key : var.nexmo_key, - nexmo_secret : var.nexmo_secret, - nexmo_blacklist : var.nexmo_blacklist, - nexmo_unsupported_regions : var.nexmo_unsupported_regions, - twilio_account_sid : var.twilio_account_sid, - twilio_messaging_service_sid : var.twilio_messaging_service_sid, - twilio_verify_service_sid : var.twilio_verify_service_sid, - twilio_auth_token : var.twilio_auth_token, - twilio_blacklist : var.twilio_blacklist, - twilio_unsupported_regions : var.twilio_unsupported_regions, - messagebird_api_key : var.messagebird_api_key, - messagebird_unsupported_regions : var.messagebird_unsupported_regions - } - ) - - service_account { - scopes = var.service_account_scopes - } - -} - -resource "random_id" "db_name" { - byte_length = 4 -} diff --git a/packages/terraform-modules-public/gcp/celo-infra/modules/attestation-service/startup.sh b/packages/terraform-modules-public/gcp/celo-infra/modules/attestation-service/startup.sh deleted file mode 100644 index 3f03124f275..00000000000 --- a/packages/terraform-modules-public/gcp/celo-infra/modules/attestation-service/startup.sh +++ /dev/null @@ -1,324 +0,0 @@ -#!/bin/bash - - -# ---- Configure logrotate ---- -echo "Configuring logrotate" | logger -cat <<'EOF' > '/etc/logrotate.d/rsyslog' -/var/log/syslog -/var/log/mail.info -/var/log/mail.warn -/var/log/mail.err -/var/log/mail.log -/var/log/daemon.log -/var/log/kern.log -/var/log/auth.log -/var/log/user.log -/var/log/lpr.log -/var/log/cron.log -/var/log/debug -/var/log/messages -{ - rotate 3 - daily - missingok - notifempty - delaycompress - compress - sharedscripts - postrotate - #invoke-rc.d rsyslog rotate > /dev/null # does not work on debian10 - kill -HUP `pidof rsyslogd` - endscript -} -EOF - -# ---- Tune rsyslog to avoid redundantly logging docker output -echo "Updating rsyslog.conf to avoid redundantly logging docker output" -cat <<'EOF' > /etc/rsyslog.conf -# /etc/rsyslog.conf configuration file for rsyslog -# -# For more information install rsyslog-doc and see -# /usr/share/doc/rsyslog-doc/html/configuration/index.html - -################# -#### MODULES #### -################# - -module(load="imuxsock") # provides support for local system logging -module(load="imklog") # provides kernel logging support - -########################### -#### GLOBAL DIRECTIVES #### -########################### - -# -# Use traditional timestamp format. -# To enable high precision timestamps, comment out the following line. -# -$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat - -# -# Set the default permissions for all log files. -# -$FileOwner root -$FileGroup adm -$FileCreateMode 0640 -$DirCreateMode 0755 -$Umask 0022 - -# -# Where to place spool and state files -# -$WorkDirectory /var/spool/rsyslog - -# -# Include all config files in /etc/rsyslog.d/ -# -$IncludeConfig /etc/rsyslog.d/*.conf - - -############### -#### RULES #### -############### - -# -# First some standard log files. Log by facility. -# -auth,authpriv.* /var/log/auth.log -*.*;auth,authpriv.none -/var/log/syslog -kern.* -/var/log/kern.log - -# -# Some "catch-all" log files. -# -*.=debug;\ - auth,authpriv.none;\ - news.none;mail.none -/var/log/debug -*.=info;*.=notice;*.=warn;\ - auth,authpriv.none;\ - cron,daemon.none;\ - mail,news.none -/var/log/messages - -# -# Emergencies are sent to everybody logged in. -# -*.emerg :omusrmsg:* -EOF - -# ---- Restart rsyslogd -echo "Restarting rsyslogd" -systemctl restart rsyslog - -# ---- Useful aliases ---- -echo "Configuring aliases" | logger -echo "alias ll='ls -laF'" >> /etc/skel/.bashrc -echo "alias ll='ls -laF'" >> /root/.bashrc -echo "alias gattach='docker exec -it geth geth attach'" >> /etc/skel/.bashrc - -function save_variable { - local var=$1 - local file=$2 - - [ -n "$var" ] && echo -n "$var" > "$file" -} - -# ---- Install Stackdriver Agent -echo "Installing Stackdriver agent" | logger -curl -sSO https://dl.google.com/cloudagents/add-monitoring-agent-repo.sh -bash add-monitoring-agent-repo.sh -apt update -y -apt install -y stackdriver-agent -systemctl restart stackdriver-agent - -# ---- Install Fluent Log Collector -echo "Installing google fluent log collector agent" | logger -curl -sSO https://dl.google.com/cloudagents/add-logging-agent-repo.sh -bash add-logging-agent-repo.sh -apt update -y -apt install -y google-fluentd -apt install -y google-fluentd-catch-all-config-structured -systemctl restart google-fluentd - -# ---- Setup swap -echo "Setting up swapfile" | logger -fallocate -l 2G /swapfile -chmod 600 /swapfile -mkswap /swapfile -swapon /swapfile -swapon -s -# ---- Install Docker ---- - -echo "Installing Docker..." | logger -apt update -y && apt upgrade -y -apt install -y apt-transport-https ca-certificates curl software-properties-common gnupg2 htop screen -curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add - -add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable" -apt update -y && apt upgrade -y -apt install -y docker-ce -apt upgrade -y -systemctl start docker - -# ---- Config /etc/screenrc ---- -echo "Configuring /etc/screenrc" | logger -cat <<'EOF' >> '/etc/screenrc' -bindkey -k k1 select 1 # F1 = screen 1 -bindkey -k k2 select 2 # F2 = screen 2 -bindkey -k k3 select 3 # F3 = screen 3 -bindkey -k k4 select 4 # F4 = screen 4 -bindkey -k k5 select 5 # F5 = screen 5 -bindkey -k k6 select 6 # F6 = screen 6 -bindkey -k k7 select 7 # F7 = screen 7 -bindkey -k k8 select 8 # F8 = screen 8 -bindkey -k k9 select 9 # F9 = screen 9 -bindkey -k F1 prev # F11 = prev -bindkey -k F2 next # F12 = next -EOF - -echo "Configuring Docker..." -cat <<'EOF' > '/etc/docker/daemon.json' -{ - "log-driver": "json-file", - "log-opts": { - "max-size": "10m", - "max-file": "3", - "mode": "non-blocking" - } -} -EOF - -echo "Restarting docker" | logger -systemctl restart docker - -# ---- Set Up and Run Attestation Service ---- -echo "Configuring Celo attestation service" | logger - -DATA_DIR=/root/.celo -mkdir -p $DATA_DIR -ATTESTATION_KEY='${attestation_key}' -ACCOUNT_ADDRESS='${account_address}' -ATTESTATION_SIGNER_ADDRESS='${account_address}' -CELO_VALIDATOR_ADDRESS='${validator_release_gold_address}' -CELO_PROVIDER='${celo_provider}' -SMS_PROVIDERS='${sms_providers}' -NEXMO_KEY='${nexmo_key}' -NEXMO_SECRET='${nexmo_secret}' -NEXMO_BLACKLIST='${nexmo_blacklist}' -NEXMO_UNSUPPORTED_REGIONS='${nexmo_unsupported_regions}' -TWILIO_ACCOUNT_SID='${twilio_account_sid}' -TWILIO_MESSAGING_SERVICE_SID='${twilio_messaging_service_sid}' -TWILIO_VERIFY_SERVICE_SID='${twilio_verify_service_sid}' -TWILIO_AUTH_TOKEN='${twilio_auth_token}' -TWILIO_BLACKLIST='${twilio_blacklist}' -TWILIO_UNSUPPORTED_REGIONS='${twilio_unsupported_regions}' -MESSAGEBIRD_API_KEY='${messagebird_api_key}' -MESSAGEBIRD_UNSUPPORTED_REGIONS='${messagebird_unsupported_regions}' - - -ATTESTATION_SERVICE_DOCKER_IMAGE='${attestation_service_docker_image_repository}:${attestation_service_docker_image_tag}' -docker pull "$ATTESTATION_SERVICE_DOCKER_IMAGE" - -# Run the Cloud SQL Proxy -echo "Configuring Cloud SQL Proxy" | logger -cat </etc/systemd/system/cloudsql.service -[Unit] -Description=Docker Container %N -Requires=docker.service -After=docker.service - -[Service] -Restart=always -ExecStart=/usr/bin/docker run \\ - --rm \\ - -v /cloudsql:/cloudsql \\ - -p 127.0.0.1:5432:5432 \\ - gcr.io/cloudsql-docker/gce-proxy:1.11 \\ - /cloud_sql_proxy \\ - -instances=${db_connection_name}=tcp:0.0.0.0:5432 -ExecStop=/usr/bin/docker stop -t 60 %N - -[Install] -WantedBy=default.target -EOF -DATABASE_URL="postgres://${db_username}:${db_password}@127.0.0.1:5432/postgres" -systemctl daemon-reload -systemctl enable cloudsql.service -systemctl restart cloudsql.service - -# Saving variables -save_variable "$DATABASE_URL" "$DATA_DIR/databaseUrl" -save_variable "$ATTESTATION_KEY" "$DATA_DIR/attestationKey" -save_variable "$ATTESTATION_SIGNER_ADDRESS" "$DATA_DIR/attestationSignerAddress" -save_variable "$ACCOUNT_ADDRESS" "$DATA_DIR/accountAddress" -save_variable "$CELO_VALIDATOR_ADDRESS" "$DATA_DIR/validatorAddress" -save_variable "$CELO_PROVIDER" "$DATA_DIR/celoProvider" -save_variable "$SMS_PROVIDERS" "$DATA_DIR/smsProviders" -save_variable "$NEXMO_KEY" "$DATA_DIR/nexmoKey" -save_variable "$NEXMO_SECRET" "$DATA_DIR/nexmoSecret" -save_variable "$NEXMO_BLACKLIST" "$DATA_DIR/nexmoBlacklist" -save_variable "$TWILIO_ACCOUNT_SID" "$DATA_DIR/twilioAccountSid" -save_variable "$TWILIO_MESSAGING_SERVICE_SID" "$DATA_DIR/twilioMessagingServiceSid" -save_variable "$TWILIO_VERIFY_SERVICE_SID" "$DATA_DIR/twilioVerifyServiceSid" -save_variable "$TWILIO_AUTH_TOKEN" "$DATA_DIR/twilioAuthToken" -save_variable "$TWILIO_BLACKLIST" "$DATA_DIR/twilioBlacklist" -save_variable "$TWILIO_UNSUPPORTED_REGIONS" "$DATA_DIR/twilioUnsupportedRegions" -save_variable "$MESSAGEBIRD_API_KEY" "$DATA_DIR/messagebird_api_key" -save_variable "$MESSAGEBIRD_UNSUPPORTED_REGIONS" "$DATA_DIR/messagebird_unsupported_regions" - -cat </etc/systemd/system/attestation-service.service -[Unit] -Description=Docker Container %N -Requires=docker.service -After=docker.service - -[Service] -Restart=always -ExecStart=/usr/bin/docker run \\ - --rm \\ - --name attestation-service \\ - --net=host \\ - --entrypoint /bin/bash \\ - -v $DATA_DIR:$DATA_DIR \\ - -e NODE_ENV=production \\ - -e PORT=80 \\ - -e DATABASE_URL="$DATABASE_URL" \\ - -e ACCOUNT_ADDRESS="$ACCOUNT_ADDRESS" \\ - -e ATTESTATION_SIGNER_ADDRESS="$ATTESTATION_SIGNER_ADDRESS" \\ - -e CELO_VALIDATOR_ADDRESS="$CELO_VALIDATOR_ADDRESS" \\ - -e ATTESTATION_KEY="$ATTESTATION_KEY" \\ - -e CELO_PROVIDER="$CELO_PROVIDER" \\ - -e SMS_PROVIDERS="$SMS_PROVIDERS" \\ - -e NEXMO_KEY="$NEXMO_KEY" \\ - -e NEXMO_SECRET="$NEXMO_SECRET" \\ - -e NEXMO_BLACKLIST="$NEXMO_BLACKLIST" \\ - -e NEXMO_UNSUPPORTED_REGIONS="$NEXMO_UNSUPPORTED_REGIONS" \\ - -e TWILIO_ACCOUNT_SID="$TWILIO_ACCOUNT_SID" \\ - -e TWILIO_MESSAGING_SERVICE_SID="$TWILIO_MESSAGING_SERVICE_SID" \\ - -e TWILIO_VERIFY_SERVICE_SID="$TWILIO_VERIFY_SERVICE_SID" \\ - -e TWILIO_AUTH_TOKEN="$TWILIO_AUTH_TOKEN" \\ - -e TWILIO_BLACKLIST="$TWILIO_BLACKLIST" \\ - -e TWILIO_UNSUPPORTED_REGIONS="$TWILIO_UNSUPPORTED_REGIONS" \\ - -e MESSAGEBIRD_API_KEY="$MESSAGEBIRD_API_KEY" \\ - -e MESSAGEBIRD_UNSUPPORTED_REGIONS="$MESSAGEBIRD_UNSUPPORTED_REGIONS" \\ - - $ATTESTATION_SERVICE_DOCKER_IMAGE -c "\\ - ( \\ - cd /celo-monorepo/packages/attestation-service && \\ - yarn run db:migrate && \\ - yarn start \\ - )" -ExecStop=/usr/bin/docker stop -t 30 %N - -[Install] -WantedBy=default.target -EOF -systemctl daemon-reload -systemctl enable attestation-service.service -systemctl restart attestation-service.service - -echo "Adding DC to docker group" -usermod -aG docker dc - -#--- remove compilers -echo "Removing compilers" | logger -sudo apt remove -y build-essential gcc make linux-compiler-gcc-8-x86 cpp -sudo apt -y autoremove \ No newline at end of file diff --git a/packages/terraform-modules-public/gcp/celo-infra/modules/attestation-service/variables.tf b/packages/terraform-modules-public/gcp/celo-infra/modules/attestation-service/variables.tf deleted file mode 100644 index db03e5aab62..00000000000 --- a/packages/terraform-modules-public/gcp/celo-infra/modules/attestation-service/variables.tf +++ /dev/null @@ -1,153 +0,0 @@ -variable attestation_service_count { - type = number - description = "Number of Attestation Service to deploy" -} - -variable celo_env { - type = string - description = "Name of the testnet Celo environment" -} - -variable gcloud_region { - type = string - description = "Name of the Google Cloud region to use" -} - -variable gcloud_project { - type = string - description = "Name of the Google Cloud project to use" -} - -variable instance_type { - description = "The instance type" - type = string - default = "n1-standard-1" -} - -variable attestation_service_docker_image_repository { - type = string - description = "The docker image repository for the attestation service" -} - -variable attestation_service_docker_image_tag { - type = string - description = "The docker image tag for the attestation service" -} - -variable db_username { - type = string - description = "The User for the database" -} - -variable db_password { - type = string - description = "The password for the database" -} - -variable network_name { - type = string - description = "The name of the network to use" -} - -variable account_address { - type = list(string) - description = "The account address for signing the attestations. Must be an authorized address of the associated validator" -} - -variable attestation_key { - type = list(string) - description = "The account private key for signing the attestations. Must be the private key of an authorized address for the associated validator" -} - -variable validator_signer_account_addresses { - type = list(string) - description = "Array with the Validator account addresses" -} - -variable validator_release_gold_addresses { - type = list(string) - description = "Array with the Validator release gold addresses" -} - -variable celo_provider { - type = string - description = "The URL for the RPC interface for the Celo network" -} - -variable sms_providers { - type = string - description = "The SMS Service provider. eg 'nexmo,messagebird,twilio'" -} - -variable nexmo_key { - type = string - description = "Nexmo api key (check nexmo documentation)" -} - -variable nexmo_secret { - type = string - description = "Nexmo api secret (check nexmo documentation)" -} - -variable nexmo_blacklist { - type = string - description = "Nexmo blacklisted country codes, separated by comma (check nexmo documentation)" -} - -variable nexmo_unsupported_regions { - type = string - description = "Nexmo unsupported country codes, separated by comma (check nexmo documentation)" -} - -variable twilio_account_sid { - type = string - description = "Twilio account SID (check twilio documentation)" -} - -variable twilio_messaging_service_sid { - type = string - description = "Twilio account messaging service SID (check twilio documentation)" -} - -variable twilio_verify_service_sid { - type = string - description = "Twilio account verify service SID (check twilio documentation)" -} - -variable twilio_auth_token { - type = string - description = "Twilio account Auth Token (check twilio documentation)" -} - -variable twilio_blacklist { - type = string - description = "Twilio blacklisted country codes, separated by comma (check twilio documentation)" -} - -variable twilio_unsupported_regions { - type = string - description = "Twilio unsupported country codes, separated by comma (check twilio documentation)" -} - -variable messagebird_api_key { - type = string - description = "Messagebird API key" - default = "" -} - -variable messagebird_unsupported_regions { - type = string - description = "Messagebird unsupported country codes, separated by comma (check Messagebird documentation)" - default = "" -} - -variable "service_account_scopes" { - description = "Scopes to apply to the service account which all nodes in the cluster will inherit" - type = list(string) - - default = [ - "https://www.googleapis.com/auth/monitoring.write", - "https://www.googleapis.com/auth/logging.write", - "https://www.googleapis.com/auth/sqlservice.admin" - ] -} \ No newline at end of file diff --git a/packages/terraform-modules-public/gcp/celo-infra/modules/backup_node/main.tf b/packages/terraform-modules-public/gcp/celo-infra/modules/backup_node/main.tf deleted file mode 100644 index 4bf47dc6bf2..00000000000 --- a/packages/terraform-modules-public/gcp/celo-infra/modules/backup_node/main.tf +++ /dev/null @@ -1,98 +0,0 @@ -locals { - attached_disk_name = "celo-data" - #having project in the instance name helps keep you from torching prod when you think you're working on staging - name_prefix = "${var.gcloud_project}-backup-node" -} - -resource "google_compute_address" "backup_node" { - #name = "${local.name_prefix}-address-${count.index}-${random_id.backup-node[count.index].hex}" - name = "${local.name_prefix}-address-${count.index}" - address_type = "EXTERNAL" - - count = var.backup_node_count - - lifecycle { - create_before_destroy = true - } -} - -resource "google_compute_address" "backup_node_internal" { - name = "${local.name_prefix}-internal-address-${count.index}" - address_type = "INTERNAL" - purpose = "GCE_ENDPOINT" - - count = var.backup_node_count -} - -resource "google_compute_instance" "backup_node" { - name = "${local.name_prefix}-${count.index}" - machine_type = var.instance_type - - deletion_protection = false - - count = var.backup_node_count - - tags = ["${var.celo_env}-backup-node"] - - allow_stopping_for_update = true - - boot_disk { - initialize_params { - image = "debian-cloud/debian-10" - size = 10 - } - } - - attached_disk { - source = google_compute_disk.backup_node[count.index].self_link - device_name = local.attached_disk_name - } - - network_interface { - network = var.network_name - network_ip = google_compute_address.backup_node_internal[count.index].address - access_config { - nat_ip = google_compute_address.backup_node[count.index].address - } - } - - metadata_startup_script = templatefile( - format("%s/startup.sh", path.module), { - attached_disk_name : local.attached_disk_name, - block_time : var.block_time, - ethstats_host : var.ethstats_host, - geth_exporter_docker_image_repository : var.geth_exporter_docker_image_repository, - geth_exporter_docker_image_tag : var.geth_exporter_docker_image_tag, - geth_node_docker_image_repository : var.geth_node_docker_image_repository, - geth_node_docker_image_tag : var.geth_node_docker_image_tag, - geth_verbosity : var.geth_verbosity, - in_memory_discovery_table : var.in_memory_discovery_table, - ip_address : google_compute_address.backup_node[count.index].address, - max_peers : var.backup_node_max_peers, - network_id : var.network_id, - gcloud_project : var.gcloud_project, - reset_geth_data : var.reset_geth_data, - rid : count.index, - } - ) - - service_account { - scopes = var.service_account_scopes - } -} - -resource "random_id" "backup_node" { - count = var.backup_node_count - byte_length = 2 -} - -resource "google_compute_disk" "backup_node" { - name = "${local.name_prefix}-celo-data-disk-${count.index}" - count = var.backup_node_count - - #type = "pd-ssd" - type = "pd-standard" #disk I/O doesn't yet warrant SSD backed validators/proxies - # in GB - size = 50 - physical_block_size_bytes = 4096 -} \ No newline at end of file diff --git a/packages/terraform-modules-public/gcp/celo-infra/modules/backup_node/outputs.tf b/packages/terraform-modules-public/gcp/celo-infra/modules/backup_node/outputs.tf deleted file mode 100644 index ee4206f34b3..00000000000 --- a/packages/terraform-modules-public/gcp/celo-infra/modules/backup_node/outputs.tf +++ /dev/null @@ -1,11 +0,0 @@ -output internal_ip_addresses { - value = google_compute_address.backup_node_internal.*.address -} - -output ip_addresses { - value = google_compute_address.backup_node.*.address -} - -output self_links { - value = google_compute_instance.backup_node.*.self_link -} diff --git a/packages/terraform-modules-public/gcp/celo-infra/modules/backup_node/startup.sh b/packages/terraform-modules-public/gcp/celo-infra/modules/backup_node/startup.sh deleted file mode 100644 index d89ef28979d..00000000000 --- a/packages/terraform-modules-public/gcp/celo-infra/modules/backup_node/startup.sh +++ /dev/null @@ -1,412 +0,0 @@ -#!/bin/bash - - -# ---- Configure logrotate ---- -echo "Configuring logrotate" | logger -cat <<'EOF' > '/etc/logrotate.d/rsyslog' -/var/log/syslog -/var/log/mail.info -/var/log/mail.warn -/var/log/mail.err -/var/log/mail.log -/var/log/daemon.log -/var/log/kern.log -/var/log/auth.log -/var/log/user.log -/var/log/lpr.log -/var/log/cron.log -/var/log/debug -/var/log/messages -{ - rotate 3 - daily - missingok - notifempty - delaycompress - compress - sharedscripts - postrotate - #invoke-rc.d rsyslog rotate > /dev/null # does not work on debian10 - kill -HUP `pidof rsyslogd` - endscript -} -EOF - -# ---- Tune rsyslog to avoid redundantly logging docker output -echo "Updating rsyslog.conf to avoid redundantly logging docker output" -cat <<'EOF' > /etc/rsyslog.conf -# /etc/rsyslog.conf configuration file for rsyslog -# -# For more information install rsyslog-doc and see -# /usr/share/doc/rsyslog-doc/html/configuration/index.html - -################# -#### MODULES #### -################# - -module(load="imuxsock") # provides support for local system logging -module(load="imklog") # provides kernel logging support - -########################### -#### GLOBAL DIRECTIVES #### -########################### - -# -# Use traditional timestamp format. -# To enable high precision timestamps, comment out the following line. -# -$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat - -# -# Set the default permissions for all log files. -# -$FileOwner root -$FileGroup adm -$FileCreateMode 0640 -$DirCreateMode 0755 -$Umask 0022 - -# -# Where to place spool and state files -# -$WorkDirectory /var/spool/rsyslog - -# -# Include all config files in /etc/rsyslog.d/ -# -$IncludeConfig /etc/rsyslog.d/*.conf - - -############### -#### RULES #### -############### - -# -# First some standard log files. Log by facility. -# -auth,authpriv.* /var/log/auth.log -*.*;auth,authpriv.none -/var/log/syslog -kern.* -/var/log/kern.log - - -# -# Some "catch-all" log files. -# -*.=debug;\ - auth,authpriv.none;\ - news.none;mail.none -/var/log/debug -*.=info;*.=notice;*.=warn;\ - auth,authpriv.none;\ - cron,daemon.none;\ - mail,news.none -/var/log/messages - -# -# Emergencies are sent to everybody logged in. -# -*.emerg :omusrmsg:* -EOF - -# ---- Restart rsyslogd -echo "Restarting rsyslogd" -systemctl restart rsyslog - -# ---- Create backup script -echo "Creating chaindata backup script" | logger -cat <<'EOF' > /root/backup.sh -#!/bin/bash -# This script stops geth, tars up the chaindata (with gzip compression), and copies it to GCS. -# The 'chaindata' GCS bucket has versioning enabled, so if a corrupted tarball is uploaded, an older version can be selected for restore. -# This takes quit some time, and takes quite a bit of local disk. -# The rsync variant (below) is more efficient, but tarballs are more portable. -set -x - -echo "Starting chaindata backup" | logger -systemctl stop geth.service -sleep 5 -tar -C /root/.celo/celo -zcvf /root/.celo/celo/chaindata.tgz chaindata -gsutil cp /root/.celo/celo/chaindata.tgz gs://${gcloud_project}-chaindata -rm -f /root/.celo/celo/chaindata.tgz -echo "Chaindata backup completed" | logger -sleep 3 -systemctl start geth.service -EOF -chmod u+x /root/backup.sh - -# ---- Create rsync backup script -echo "Creating rsync chaindata backup script" | logger -cat <<'EOF' > /root/backup_rsync.sh -#!/bin/bash -# This script stops geth, and uses rsync to copy chaindata to GCS. -set -x -CELO_DIR="/root/.celo/celo" - -echo "Starting rsync chaindata backup" | logger -systemctl stop geth.service -sleep 5 -gsutil -m rsync -d -r /root/.celo/celo/chaindata gs://${gcloud_project}-chaindata-rsync -echo "rsync chaindata backup completed" | logger -sleep 3 -systemctl start geth.service -EOF -chmod u+x /root/backup_rsync.sh - -# ---- Add backups to cron -# note that this will make the backup_node geth unavailable during the backup, which is why -# we run this on a dedicated backup node now instead of the attestation service txnode -cat <<'EOF' > /root/backup.crontab -# m h dom mon dow command -# backup full tarball once a day at 00:57 -57 0 * * * /root/backup.sh > /dev/null 2>&1 -# backup via rsync run every six hours at 00:17 past the hour -17 */6 * * * /root/backup_rsync.sh > /dev/null 2>&1 -EOF -/usr/bin/crontab /root/backup.crontab - -# ---- Create restore script -echo "Creating chaindata restore script" | logger -cat <<'EOF' > /root/restore.sh -#!/bin/bash -set -x - -# test to see if chaindata exists in bucket -gsutil -q stat gs://${gcloud_project}-chaindata/chaindata.tgz -if [ $? -eq 0 ] -then - #chaindata exists in bucket - mkdir -p /root/.celo/celo - mkdir -p /root/.celo/celo/restore - echo "downloading chaindata from gs://${gcloud_project}-chaindata/chaindata.tgz" | logger - gsutil cp gs://${gcloud_project}-chaindata/chaindata.tgz /root/.celo/celo/restore/chaindata.tgz - echo "stopping geth to untar chaindata" | logger - systemctl stop geth.service - sleep 3 - echo "Deleting old chaindata" | logger - rm -rf /root/.celo/celo/chaindata/* - echo "untarring chaindata" | logger - tar zxvf /root/.celo/celo/restore/chaindata.tgz --directory /root/.celo/celo - echo "removing chaindata tarball" | logger - rm -rf /root/.celo/celo/restore/chaindata.tgz - sleep 3 - echo "starting geth" | logger - systemctl start geth.service - else - echo "No chaindata.tgz found in bucket gs://${gcloud_project}-chaindata, aborting warp restore" | logger - fi -EOF -chmod u+x /root/restore.sh - -# ---- Create rsync restore script -echo "Creating rsync chaindata restore script" | logger -cat <<'EOF' > /root/restore_rsync.sh -#!/bin/bash -set -x - -# test to see if chaindata exists in the rsync chaindata bucket -gsutil -q stat gs://${gcloud_project}-chaindata-rsync/CURRENT -if [ $? -eq 0 ] -then - #chaindata exists in bucket - echo "stopping geth" | logger - systemctl stop geth.service - echo "downloading chaindata via rsync from gs://${gcloud_project}-chaindata-rsync" | logger - mkdir -p /root/.celo/celo/chaindata - gsutil -m rsync -d -r gs://${gcloud_project}-chaindata-rsync /root/.celo/celo/chaindata - echo "restarting geth" | logger - sleep 3 - systemctl start geth.service - else - echo "No chaindata found in bucket gs://${gcloud_project}-chaindata-rsync, aborting warp restore" | logger - fi -EOF -chmod u+x /root/restore_rsync.sh - -# ---- Useful aliases ---- -echo "Configuring aliases" | logger -echo "alias ll='ls -laF'" >> /etc/skel/.bashrc -echo "alias ll='ls -laF'" >> /root/.bashrc -echo "alias gattach='docker exec -it geth geth attach'" >> /etc/skel/.bashrc - -# ---- Install Stackdriver Agent -echo "Installing Stackdriver agent" | logger -curl -sSO https://dl.google.com/cloudagents/add-monitoring-agent-repo.sh -bash add-monitoring-agent-repo.sh -apt update -y -apt install -y stackdriver-agent -systemctl restart stackdriver-agent - -# ---- Install Fluent Log Collector -echo "Installing google fluent log collector agent" | logger -curl -sSO https://dl.google.com/cloudagents/add-logging-agent-repo.sh -bash add-logging-agent-repo.sh -apt update -y -apt install -y google-fluentd -apt install -y google-fluentd-catch-all-config-structured -systemctl restart google-fluentd - - - -# ---- Set Up Persistent Disk ---- - -# gives a path similar to `/dev/sdb` -DISK_PATH=$(readlink -f /dev/disk/by-id/google-${attached_disk_name}) -DATA_DIR=/root/.celo - -echo "Setting up persistent disk ${attached_disk_name} at $DISK_PATH..." - -DISK_FORMAT=ext4 -CURRENT_DISK_FORMAT=$(lsblk -i -n -o fstype $DISK_PATH) - -echo "Checking if disk $DISK_PATH format $CURRENT_DISK_FORMAT matches desired $DISK_FORMAT..." - -# If the disk has already been formatted previously (this will happen -# if this instance has been recreated with the same disk), we skip formatting -if [[ $CURRENT_DISK_FORMAT == $DISK_FORMAT ]]; then - echo "Disk $DISK_PATH is correctly formatted as $DISK_FORMAT" -else - echo "Disk $DISK_PATH is not formatted correctly, formatting as $DISK_FORMAT..." - mkfs.ext4 -m 0 -F -E lazy_itable_init=0,lazy_journal_init=0,discard $DISK_PATH -fi - -# Mounting the volume -echo "Mounting $DISK_PATH onto $DATA_DIR" -mkdir -p $DATA_DIR -DISK_UUID=$(blkid $DISK_PATH | cut -d '"' -f2) -echo "UUID=$DISK_UUID $DATA_DIR auto discard,defaults 0 0" >> /etc/fstab -mount $DATA_DIR - -# ---- Setup swap -echo "Setting up swapfile" | logger -fallocate -l 4G /root/.celo/swapfile -chmod 600 /root/.celo/swapfile -mkswap /root/.celo/swapfile -swapon /root/.celo/swapfile -swapon -s - -# Remove existing chain data -[[ ${reset_geth_data} == "true" ]] && rm -rf $DATA_DIR/geth -mkdir -p $DATA_DIR/account - -# ---- Install Docker ---- - -echo "Installing Docker..." | logger -apt update -y && apt upgrade -y -apt install -y apt-transport-https ca-certificates curl software-properties-common gnupg2 htop screen -curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add - -add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable" -apt update -y && apt upgrade -y -apt install -y docker-ce -apt upgrade -y -systemctl start docker - -# ---- Config /etc/screenrc ---- -echo "Configuring /etc/screenrc" | logger -cat <<'EOF' >> '/etc/screenrc' -bindkey -k k1 select 1 # F1 = screen 1 -bindkey -k k2 select 2 # F2 = screen 2 -bindkey -k k3 select 3 # F3 = screen 3 -bindkey -k k4 select 4 # F4 = screen 4 -bindkey -k k5 select 5 # F5 = screen 5 -bindkey -k k6 select 6 # F6 = screen 6 -bindkey -k k7 select 7 # F7 = screen 7 -bindkey -k k8 select 8 # F8 = screen 8 -bindkey -k k9 select 9 # F9 = screen 9 -bindkey -k F1 prev # F11 = prev -bindkey -k F2 next # F12 = next -EOF - -echo "Configuring Docker..." | logger -cat <<'EOF' > '/etc/docker/daemon.json' -{ - "log-driver": "json-file", - "log-opts": { - "max-size": "10m", - "max-file": "3", - "mode": "non-blocking" - } -} -EOF - -echo "Restarting docker" | logger -systemctl restart docker - -# ---- Set Up and Run Geth ---- - -echo "Configuring Geth" | logger - -GETH_NODE_DOCKER_IMAGE=${geth_node_docker_image_repository}:${geth_node_docker_image_tag} - -echo "Pulling geth..." -docker pull $GETH_NODE_DOCKER_IMAGE - -IN_MEMORY_DISCOVERY_TABLE_FLAG="" -[[ ${in_memory_discovery_table} == "true" ]] && IN_MEMORY_DISCOVERY_TABLE_FLAG="--use-in-memory-discovery-table" - -# Load configuration to files -mkdir -p $DATA_DIR/account - -echo -n '${rid}' > $DATA_DIR/replica_id -echo -n '${ip_address}' > $DATA_DIR/ipAddress - -cat </etc/systemd/system/geth.service -[Unit] -Description=Docker Container %N -Requires=docker.service -After=docker.service - -[Service] -Restart=always -ExecStart=/usr/bin/docker run \\ - --rm \\ - --name geth \\ - --net=host \\ - -v $DATA_DIR:$DATA_DIR \\ - --entrypoint /bin/sh \\ - $GETH_NODE_DOCKER_IMAGE -c "\\ - geth \\ - --nousb \\ - --maxpeers ${max_peers} \\ - --rpc \\ - --rpcapi=eth,net,web3 \\ - --networkid=${network_id} \\ - --syncmode=full \\ - --consoleformat=json \\ - --consoleoutput=stdout \\ - --verbosity=${geth_verbosity} \\ - --nat=extip:${ip_address} \\ - --metrics \\ - --pprof \\ - $IN_MEMORY_DISCOVERY_TABLE_FLAG \\ - --light.serve 0 \\ - " -ExecStop=/usr/bin/docker stop -t 300 %N - -[Install] -WantedBy=default.target -EOF - -echo "Adding DC to docker group" | logger -usermod -aG docker dc - -# --- run restore script -# this script tries to restore chaindata from a GCS hosted tarball. -# if the chaindata doesn't exist on GCS, geth will start normal (slow) p2p sync -echo "Attempting to restore chaindata from backup tarball" -bash /root/restore.sh - -# todo: add some logic to look at the chaindata tarball bucket versus the rsync bucket and pick the best one. -# for now we try both, with rsync taking precedence b/c it runs last. - -# --- run rsync restore script -# this script tries to restore chaindata from a GCS hosted bucket via rsync. -# if the chaindata doesn't exist on GCS, geth will start normal (slow) p2p sync, perhaps boosted by what the tarball provided -echo "Attempting to restore chaindata from backup via rsync" -bash /root/restore_rsync.sh - -echo "Starting Geth" -systemctl daemon-reload -systemctl enable geth.service - -#--- remove compilers -echo "Removing compilers" | logger -sudo apt remove -y build-essential gcc make linux-compiler-gcc-8-x86 cpp -sudo apt -y autoremove \ No newline at end of file diff --git a/packages/terraform-modules-public/gcp/celo-infra/modules/backup_node/variables.tf b/packages/terraform-modules-public/gcp/celo-infra/modules/backup_node/variables.tf deleted file mode 100644 index 46140bce417..00000000000 --- a/packages/terraform-modules-public/gcp/celo-infra/modules/backup_node/variables.tf +++ /dev/null @@ -1,87 +0,0 @@ -variable block_time { - type = number - description = "Number of seconds between each block" -} - -variable celo_env { - type = string - description = "Name of the Celo environment" -} - -variable gcloud_project { - type = string - description = "Name of the Google Cloud project to use" -} - -variable instance_type { - description = "The instance type" - type = string - default = "n1-standard-1" -} - -variable ethstats_host { - type = string - description = "Ethstats url or IP address" -} - -variable geth_exporter_docker_image_repository { - type = string - description = "Repository of the geth exporter docker image" -} - -variable geth_exporter_docker_image_tag { - type = string - description = "Tag of the geth exporter docker image" -} - -variable geth_node_docker_image_repository { - type = string - description = "Repository of the geth docker image" -} - -variable geth_node_docker_image_tag { - type = string - description = "Tag of the geth docker image" -} - -variable geth_verbosity { - type = number - description = "Verbosity of the tx-nodes" -} - -variable in_memory_discovery_table { - type = bool - description = "Specifies whether to use an in memory discovery table" -} - -variable network_id { - type = number - description = "The network ID number" -} - -variable network_name { - type = string - description = "Name of the GCP network the tx-node VM is in" -} - -variable backup_node_count { - type = number - description = "Number of backup_nodes to create" -} - -variable reset_geth_data { - type = bool - description = "Specifies if the existing chain data should be removed while creating the instance" - default = true -} - -variable backup_node_max_peers { - type = number - description = "Max number of peers to connect with" - default = 120 -} - -variable service_account_scopes { - type = list(string) - description = "Scopes to apply to the service account which all nodes in the cluster will inherit" -} \ No newline at end of file diff --git a/packages/terraform-modules-public/gcp/celo-infra/modules/proxy/main.tf b/packages/terraform-modules-public/gcp/celo-infra/modules/proxy/main.tf deleted file mode 100644 index d9c64668e3a..00000000000 --- a/packages/terraform-modules-public/gcp/celo-infra/modules/proxy/main.tf +++ /dev/null @@ -1,94 +0,0 @@ -locals { - attached_disk_name = "celo-data" - name_prefix = "${var.gcloud_project}-proxy" -} - -resource "google_compute_address" "proxy" { - name = "${local.name_prefix}-address-${count.index}" - address_type = "EXTERNAL" - - count = var.validator_count -} - -resource "google_compute_address" "proxy_internal" { - name = "${local.name_prefix}-internal-address-${count.index}" - address_type = "INTERNAL" - purpose = "GCE_ENDPOINT" - - count = var.validator_count -} - -resource "google_compute_instance" "proxy" { - name = "${local.name_prefix}-${count.index}" - machine_type = var.instance_type - - #deletion_protection = false - deletion_protection = true - - count = var.validator_count - - tags = ["${var.celo_env}-proxy"] - - allow_stopping_for_update = true - - boot_disk { - initialize_params { - image = "debian-cloud/debian-10" - } - } - - attached_disk { - source = google_compute_disk.proxy[count.index].self_link - device_name = local.attached_disk_name - } - - network_interface { - network = var.network_name - network_ip = google_compute_address.proxy_internal[count.index].address - access_config { - nat_ip = google_compute_address.proxy[count.index].address - } - } - - metadata_startup_script = templatefile( - format("%s/startup.sh", path.module), { - attached_disk_name : local.attached_disk_name, - block_time : var.block_time, - ethstats_host : var.ethstats_host, - geth_exporter_docker_image_repository : var.geth_exporter_docker_image_repository, - geth_exporter_docker_image_tag : var.geth_exporter_docker_image_tag, - geth_node_docker_image_repository : var.geth_node_docker_image_repository, - geth_node_docker_image_tag : var.geth_node_docker_image_tag, - geth_verbosity : var.geth_verbosity, - in_memory_discovery_table : var.in_memory_discovery_table, - ip_address : google_compute_address.proxy[count.index].address, - istanbul_request_timeout_ms : var.istanbul_request_timeout_ms, - max_peers : var.proxy_max_peers, - network_id : var.network_id, - gcloud_project : var.gcloud_project, - rid : count.index, - proxy_name : var.proxy_name, - proxy_address: var.proxy_addresses[count.index], - proxy_private_key : var.proxy_private_keys[count.index], - proxy_geth_account_secret : var.proxy_account_passwords[count.index], - validator_account_address : var.validator_signer_account_addresses[count.index], - reset_geth_data : var.reset_geth_data - } - ) - - service_account { - scopes = var.service_account_scopes - } - -} - -resource "google_compute_disk" "proxy" { - name = "${local.name_prefix}-disk-${count.index}" - count = var.validator_count - - #type = "pd-ssd" - type = "pd-standard" #disk I/O doesn't yet warrant SSD backed validators/proxies - # in GB - size = 50 - physical_block_size_bytes = 4096 -} diff --git a/packages/terraform-modules-public/gcp/celo-infra/modules/proxy/outputs.tf b/packages/terraform-modules-public/gcp/celo-infra/modules/proxy/outputs.tf deleted file mode 100644 index a61e005d184..00000000000 --- a/packages/terraform-modules-public/gcp/celo-infra/modules/proxy/outputs.tf +++ /dev/null @@ -1,7 +0,0 @@ -output internal_ip_addresses { - value = google_compute_address.proxy_internal.*.address -} - -output external_ip_addresses { - value = google_compute_address.proxy.*.address -} diff --git a/packages/terraform-modules-public/gcp/celo-infra/modules/proxy/startup.sh b/packages/terraform-modules-public/gcp/celo-infra/modules/proxy/startup.sh deleted file mode 100644 index 67dfb732bb7..00000000000 --- a/packages/terraform-modules-public/gcp/celo-infra/modules/proxy/startup.sh +++ /dev/null @@ -1,393 +0,0 @@ -#!/bin/bash - - -# ---- Configure logrotate ---- -echo "Configuring logrotate" | logger -cat <<'EOF' > '/etc/logrotate.d/rsyslog' -/var/log/syslog -/var/log/mail.info -/var/log/mail.warn -/var/log/mail.err -/var/log/mail.log -/var/log/daemon.log -/var/log/kern.log -/var/log/auth.log -/var/log/user.log -/var/log/lpr.log -/var/log/cron.log -/var/log/debug -/var/log/messages -{ - rotate 3 - daily - missingok - notifempty - delaycompress - compress - sharedscripts - postrotate - #invoke-rc.d rsyslog rotate > /dev/null # does not work on debian10 - kill -HUP `pidof rsyslogd` - endscript -} -EOF - -# ---- Tune rsyslog to avoid redundantly logging docker output -echo "Updating rsyslog.conf to avoid redundantly logging docker output" -cat <<'EOF' > /etc/rsyslog.conf -# /etc/rsyslog.conf configuration file for rsyslog -# -# For more information install rsyslog-doc and see -# /usr/share/doc/rsyslog-doc/html/configuration/index.html - -################# -#### MODULES #### -################# - -module(load="imuxsock") # provides support for local system logging -module(load="imklog") # provides kernel logging support - -########################### -#### GLOBAL DIRECTIVES #### -########################### - -# -# Use traditional timestamp format. -# To enable high precision timestamps, comment out the following line. -# -$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat - -# -# Set the default permissions for all log files. -# -$FileOwner root -$FileGroup adm -$FileCreateMode 0640 -$DirCreateMode 0755 -$Umask 0022 - -# -# Where to place spool and state files -# -$WorkDirectory /var/spool/rsyslog - -# -# Include all config files in /etc/rsyslog.d/ -# -$IncludeConfig /etc/rsyslog.d/*.conf - - -############### -#### RULES #### -############### - -# -# First some standard log files. Log by facility. -# -auth,authpriv.* /var/log/auth.log -*.*;auth,authpriv.none -/var/log/syslog -kern.* -/var/log/kern.log - - -# -# Some "catch-all" log files. -# -*.=debug;\ - auth,authpriv.none;\ - news.none;mail.none -/var/log/debug -*.=info;*.=notice;*.=warn;\ - auth,authpriv.none;\ - cron,daemon.none;\ - mail,news.none -/var/log/messages - -# -# Emergencies are sent to everybody logged in. -# -*.emerg :omusrmsg:* -EOF - -# ---- Restart rsyslogd -echo "Restarting rsyslogd" -systemctl restart rsyslog - -# ---- Create restore script -echo "Creating chaindata restore script" | logger -cat <<'EOF' > /root/restore.sh -#!/bin/bash -set -x - -# test to see if chaindata exists in bucket -gsutil -q stat gs://${gcloud_project}-chaindata/chaindata.tgz -if [ $? -eq 0 ] -then - #chaindata exists in bucket - mkdir -p /root/.celo/celo - mkdir -p /root/.celo/celo/restore - echo "downloading chaindata from gs://${gcloud_project}-chaindata/chaindata.tgz" | logger - gsutil cp gs://${gcloud_project}-chaindata/chaindata.tgz /root/.celo/celo/restore/chaindata.tgz - echo "stopping geth to untar chaindata" | logger - systemctl stop geth.service - sleep 3 - echo "Deleting old chaindata" | logger - rm -rf /root/.celo/celo/chaindata/* - echo "untarring chaindata" | logger - tar zxvf /root/.celo/celo/restore/chaindata.tgz --directory /root/.celo/celo - echo "removing chaindata tarball" | logger - rm -rf /root/.celo/celo/restore/chaindata.tgz - sleep 3 - echo "starting geth" | logger - systemctl start geth.service - else - echo "No chaindata.tgz found in bucket gs://${gcloud_project}-chaindata, aborting warp restore" | logger - fi -EOF -chmod u+x /root/restore.sh - -# ---- Create rsync restore script -echo "Creating rsync chaindata restore script" | logger -cat <<'EOF' > /root/restore_rsync.sh -#!/bin/bash -set -x - -# test to see if chaindata exists in the rsync chaindata bucket -gsutil -q stat gs://${gcloud_project}-chaindata-rsync/CURRENT -if [ $? -eq 0 ] -then - #chaindata exists in bucket - echo "stopping geth" | logger - systemctl stop geth.service - echo "downloading chaindata via rsync from gs://${gcloud_project}-chaindata-rsync" | logger - mkdir -p /root/.celo/celo/chaindata - gsutil -m rsync -d -r gs://${gcloud_project}-chaindata-rsync /root/.celo/celo/chaindata - echo "restarting geth" | logger - sleep 3 - systemctl start geth.service - else - echo "No chaindata found in bucket gs://${gcloud_project}-chaindata-rsync, aborting warp restore" | logger - fi -EOF -chmod u+x /root/restore_rsync.sh - -# ---- Useful aliases ---- -echo "Configuring aliases" | logger -echo "alias ll='ls -laF'" >> /etc/skel/.bashrc -echo "alias ll='ls -laF'" >> /root/.bashrc -echo "alias gattach='docker exec -it geth geth attach'" >> /etc/skel/.bashrc - -# ---- Install Stackdriver Agent -echo "Installing Stackdriver agent" | logger -curl -sSO https://dl.google.com/cloudagents/add-monitoring-agent-repo.sh -bash add-monitoring-agent-repo.sh -apt update -y -apt install -y stackdriver-agent -systemctl restart stackdriver-agent - -# ---- Install Fluent Log Collector -echo "Installing google fluent log collector agent" | logger -curl -sSO https://dl.google.com/cloudagents/add-logging-agent-repo.sh -bash add-logging-agent-repo.sh -apt update -y -apt install -y google-fluentd -apt install -y google-fluentd-catch-all-config-structured -systemctl restart google-fluentd - -# ---- Set Up Persistent Disk ---- - -# gives a path similar to `/dev/sdb` -DISK_PATH=$(readlink -f /dev/disk/by-id/google-${attached_disk_name}) -DATA_DIR=/root/.celo - -echo "Setting up persistent disk ${attached_disk_name} at $DISK_PATH..." - -DISK_FORMAT=ext4 -CURRENT_DISK_FORMAT=$(lsblk -i -n -o fstype $DISK_PATH) - -echo "Checking if disk $DISK_PATH format $CURRENT_DISK_FORMAT matches desired $DISK_FORMAT..." - -# If the disk has already been formatted previously (this will happen -# if this instance has been recreated with the same disk), we skip formatting -if [[ $CURRENT_DISK_FORMAT == $DISK_FORMAT ]]; then - echo "Disk $DISK_PATH is correctly formatted as $DISK_FORMAT" -else - echo "Disk $DISK_PATH is not formatted correctly, formatting as $DISK_FORMAT..." - mkfs.ext4 -m 0 -F -E lazy_itable_init=0,lazy_journal_init=0,discard $DISK_PATH -fi - -# Mounting the volume -echo "Mounting $DISK_PATH onto $DATA_DIR" -mkdir -p $DATA_DIR -DISK_UUID=$(blkid $DISK_PATH | cut -d '"' -f2) -echo "UUID=$DISK_UUID $DATA_DIR auto discard,defaults 0 0" >> /etc/fstab -mount $DATA_DIR - -# ---- Setup swap -echo "Setting up swapfile" | logger -fallocate -l 4G /root/.celo/swapfile -chmod 600 /root/.celo/swapfile -mkswap /root/.celo/swapfile -swapon /root/.celo/swapfile -swapon -s - -# Remove existing chain data -[[ ${reset_geth_data} == "true" ]] && rm -rf $DATA_DIR/geth -mkdir -p $DATA_DIR/account - -# ---- Install Docker ---- - -echo "Installing Docker..." | logger -apt update -y && apt upgrade -y -apt install -y apt-transport-https ca-certificates curl software-properties-common gnupg2 htop screen -curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add - -add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable" -apt update -y && apt upgrade -y -apt install -y docker-ce -apt upgrade -y -systemctl start docker - -# ---- Config /etc/screenrc ---- -echo "Configuring /etc/screenrc" | logger -cat <<'EOF' >> '/etc/screenrc' -bindkey -k k1 select 1 # F1 = screen 1 -bindkey -k k2 select 2 # F2 = screen 2 -bindkey -k k3 select 3 # F3 = screen 3 -bindkey -k k4 select 4 # F4 = screen 4 -bindkey -k k5 select 5 # F5 = screen 5 -bindkey -k k6 select 6 # F6 = screen 6 -bindkey -k k7 select 7 # F7 = screen 7 -bindkey -k k8 select 8 # F8 = screen 8 -bindkey -k k9 select 9 # F9 = screen 9 -bindkey -k F1 prev # F11 = prev -bindkey -k F2 next # F12 = next -EOF - -echo "Configuring Docker..." | logger -cat <<'EOF' > '/etc/docker/daemon.json' -{ - "log-driver": "json-file", - "log-opts": { - "max-size": "10m", - "max-file": "3", - "mode": "non-blocking" - } -} -EOF - -echo "Restarting docker" | logger -systemctl restart docker - -# ---- Set Up and Run Geth ---- - -echo "Configuring Geth" | logger - -DATA_DIR=/root/.celo - -GETH_NODE_DOCKER_IMAGE=${geth_node_docker_image_repository}:${geth_node_docker_image_tag} - -echo "Pulling geth..." | logger -docker pull $GETH_NODE_DOCKER_IMAGE - -IN_MEMORY_DISCOVERY_TABLE_FLAG="" -[[ ${in_memory_discovery_table} == "true" ]] && IN_MEMORY_DISCOVERY_TABLE_FLAG="--use-in-memory-discovery-table" - -# Load configuration to files -mkdir -p $DATA_DIR/account - -echo -n '${rid}' > $DATA_DIR/replica_id -echo -n '${ip_address}' > $DATA_DIR/ipAddress -echo -n '${proxy_private_key}' > $DATA_DIR/pkey -echo -h '${proxy_geth_account_secret}' > $DATA_DIR/account/accountSecret - -echo "Starting geth..." | logger -# We need to override the entrypoint in the geth image (which is originally `geth`). -# `geth account import` fails when the account has already been imported. In -# this case, we do not want to pipefail - -docker run \ - --rm \ - --net=host \ - -v $DATA_DIR:$DATA_DIR \ - --entrypoint /bin/sh \ - -i $GETH_NODE_DOCKER_IMAGE \ - -c "geth account import --password $DATA_DIR/account/accountSecret $DATA_DIR/pkey | true" - -cat </etc/systemd/system/geth.service -[Unit] -Description=Docker Container %N -Requires=docker.service -After=docker.service - -[Service] -Restart=always -ExecStart=/usr/bin/docker run \\ - --rm \\ - --name geth \\ - --net=host \\ - -v $DATA_DIR:$DATA_DIR \\ - --entrypoint /bin/sh \\ - $GETH_NODE_DOCKER_IMAGE -c "\\ - geth \\ - --etherbase ${proxy_address} \\ - --unlock ${proxy_address} \\ - --password $DATA_DIR/account/accountSecret \\ - --allow-insecure-unlock \\ - --nousb \\ - --rpc \\ - --rpcaddr 0.0.0.0 \\ - --rpcapi=eth,net,web3 \\ - --rpccorsdomain='*' \\ - --rpcvhosts=* \\ - --ws \\ - --wsaddr 0.0.0.0 \\ - --wsorigins=* \\ - --wsapi=eth,net,web3 \\ - --nodekey=$DATA_DIR/pkey \\ - --networkid=${network_id} \\ - --syncmode=full \\ - --consoleformat=json \\ - --consoleoutput=stdout \\ - --verbosity=${geth_verbosity} \\ - --celostats=${proxy_name}@${ethstats_host} \\ - --istanbul.blockperiod=${block_time} \\ - --istanbul.requesttimeout=${istanbul_request_timeout_ms} \\ - --maxpeers=${max_peers} \\ - --nat=extip:${ip_address} \\ - --metrics \\ - --pprof \\ - $IN_MEMORY_DISCOVERY_TABLE_FLAG \\ - --proxy.proxy \\ - --proxy.proxiedvalidatoraddress ${validator_account_address} \\ - --proxy.internalendpoint :30503 \\ - --light.serve 0 \\ - " -ExecStop=/usr/bin/docker stop -t 60 %N - -[Install] -WantedBy=default.target -EOF - -echo "Starting Geth" | logger -systemctl daemon-reload -systemctl enable geth.service - -echo "Adding DC to docker group" | logger -usermod -aG docker dc - -# --- run restore script -# this script tries to restore chaindata from a GCS hosted tarball. -# if the chaindata doesn't exist on GCS, geth will start normal (slow) p2p sync -echo "Restoring chaindata from backup tarball" | logger -bash /root/restore.sh - -# todo: add some logic to look at the chaindata tarball bucket versus the rsync bucket and pick the best one. -# for now we try both, with rsync taking precedence b/c it runs last. - -# --- run rsync restore script -# this script tries to restore chaindata from a GCS hosted bucket via rsync. -# if the chaindata doesn't exist on GCS, geth will start normal (slow) p2p sync, perhaps boosted by what the tarball provided -echo "Restoring chaindata from backup via rsync" | logger -bash /root/restore_rsync.sh - -#--- remove compilers -echo "Removing compilers" | logger -sudo apt remove -y build-essential gcc make linux-compiler-gcc-8-x86 cpp -sudo apt -y autoremove \ No newline at end of file diff --git a/packages/terraform-modules-public/gcp/celo-infra/modules/proxy/variables.tf b/packages/terraform-modules-public/gcp/celo-infra/modules/proxy/variables.tf deleted file mode 100644 index f17480938f5..00000000000 --- a/packages/terraform-modules-public/gcp/celo-infra/modules/proxy/variables.tf +++ /dev/null @@ -1,123 +0,0 @@ -variable block_time { - type = number - description = "Number of seconds between each block" -} - -variable celo_env { - type = string - description = "Name of the testnet Celo environment" -} - -variable gcloud_project { - type = string - description = "Name of the Google Cloud project to use" -} - -variable instance_type { - description = "The instance type" - type = string - default = "n1-standard-2" -} - -variable ethstats_host { - type = string - description = "Ethstats url or IP address" -} - -variable geth_exporter_docker_image_repository { - type = string - description = "Repository of the geth exporter docker image" -} - -variable geth_exporter_docker_image_tag { - type = string - description = "Tag of the geth exporter docker image" -} - -variable geth_node_docker_image_repository { - type = string - description = "Repository of the geth docker image" -} - -variable geth_node_docker_image_tag { - type = string - description = "Tag of the geth docker image" -} - -variable geth_verbosity { - type = number - description = "Verbosity of the proxy nodes" -} - -variable in_memory_discovery_table { - type = bool - description = "Specifies whether to use an in memory discovery table" -} - -variable istanbul_request_timeout_ms { - type = number - description = "The number of ms for the istanbul request timeout" -} - -variable network_id { - type = number - description = "The network ID number" -} - -variable network_name { - type = string - description = "Name of the GCP network the proxy VM is in" -} - -variable tx_node_count { - type = number - description = "Number of tx-nodes that are created" -} - -variable validator_count { - type = number - description = "Number of proxys to create" -} - -variable proxy_private_keys { - type = list(string) - description = "Array with the Proxy private keys" -} - -variable validator_signer_account_addresses { - type = list(string) - description = "Array with the Validator etherbase account addresses" -} - -variable reset_geth_data { - type = bool - description = "Specifies if the existing chain data should be removed while creating the instance" - default = true -} - -variable proxy_name { - type = string - description = "The proxy Name for celostats" -} - -variable proxy_addresses { - type = list(string) - description = "The proxy address for celostats" -} - -variable proxy_max_peers { - type = number - description = "Max number of peers to connect with" - default = 125 - #note this does not behave as expected. 120 means we get 20 (unelected). -} - -variable proxy_account_passwords { - type = list(string) - description = "Array with the proxy account passwords" -} - -variable "service_account_scopes" { - description = "Scopes to apply to the service account which all nodes in the cluster will inherit" - type = list(string) -} \ No newline at end of file diff --git a/packages/terraform-modules-public/gcp/celo-infra/modules/tx-node/main.tf b/packages/terraform-modules-public/gcp/celo-infra/modules/tx-node/main.tf deleted file mode 100644 index 417ad339b1e..00000000000 --- a/packages/terraform-modules-public/gcp/celo-infra/modules/tx-node/main.tf +++ /dev/null @@ -1,106 +0,0 @@ -locals { - attached_disk_name = "celo-data" - #having project in the instance name helps keep you from torching prod when you think you're working on staging - name_prefix = "${var.gcloud_project}-tx-node" -} - -resource "google_compute_address" "tx_node" { - name = "${local.name_prefix}-address-${count.index}-${random_id.tx_node[count.index].hex}" - address_type = "EXTERNAL" - - count = var.tx_node_count - - lifecycle { - create_before_destroy = true - } -} - -resource "google_compute_address" "tx_node_internal" { - name = "${local.name_prefix}-internal-address-${count.index}-${random_id.tx_node[count.index].hex}" - address_type = "INTERNAL" - purpose = "GCE_ENDPOINT" - - count = var.tx_node_count -} - -resource "google_compute_instance" "tx_node" { - name = "${local.name_prefix}-${count.index}" - machine_type = var.instance_type - - deletion_protection = false - - count = var.tx_node_count - - tags = ["${var.celo_env}-txnode"] - - allow_stopping_for_update = true - - boot_disk { - initialize_params { - image = "debian-cloud/debian-10" - size = 12 - } - } - - #375G local SSD is overkill for the txnode. - #scratch_disk { - # interface = "SCSI" - #} - - attached_disk { - source = google_compute_disk.txnode[count.index].self_link - device_name = local.attached_disk_name - } - - network_interface { - network = var.network_name - network_ip = google_compute_address.tx_node_internal[count.index].address - access_config { - nat_ip = google_compute_address.tx_node[count.index].address - } - } - - metadata_startup_script = templatefile( - format("%s/startup.sh", path.module), { - attached_disk_name : local.attached_disk_name, - block_time : var.block_time, - ethstats_host : var.ethstats_host, - geth_exporter_docker_image_repository : var.geth_exporter_docker_image_repository, - geth_exporter_docker_image_tag : var.geth_exporter_docker_image_tag, - geth_node_docker_image_repository : var.geth_node_docker_image_repository, - geth_node_docker_image_tag : var.geth_node_docker_image_tag, - geth_verbosity : var.geth_verbosity, - in_memory_discovery_table : var.in_memory_discovery_table, - ip_address : google_compute_address.tx_node[count.index].address, - max_peers : var.txnode_max_peers, - network_id : var.network_id, - gcloud_project : var.gcloud_project, - reset_geth_data : var.reset_geth_data, - rid : count.index, - attestation_signer_address : var.attestation_signer_addresses[count.index], - attestation_signer_private_key : var.attestation_signer_private_keys[count.index], - attestation_signer_geth_account_secret : var.attestation_signer_account_passwords[count.index], - } - ) - - service_account { - scopes = var.service_account_scopes - } -} - -resource "random_id" "tx_node" { - count = var.tx_node_count - - byte_length = 2 -} - -resource "google_compute_disk" "txnode" { - name = "${local.name_prefix}-celo-data-disk-${count.index}" - count = var.tx_node_count - - #type = "pd-ssd" - type = "pd-standard" #disk I/O doesn't yet warrant SSD backed validators/proxies - # in GB - size = 10 - physical_block_size_bytes = 4096 -} \ No newline at end of file diff --git a/packages/terraform-modules-public/gcp/celo-infra/modules/tx-node/outputs.tf b/packages/terraform-modules-public/gcp/celo-infra/modules/tx-node/outputs.tf deleted file mode 100644 index f8749b4ee87..00000000000 --- a/packages/terraform-modules-public/gcp/celo-infra/modules/tx-node/outputs.tf +++ /dev/null @@ -1,11 +0,0 @@ -output internal_ip_addresses { - value = google_compute_address.tx_node_internal.*.address -} - -output ip_addresses { - value = google_compute_address.tx_node.*.address -} - -output self_links { - value = google_compute_instance.tx_node.*.self_link -} diff --git a/packages/terraform-modules-public/gcp/celo-infra/modules/tx-node/startup.sh b/packages/terraform-modules-public/gcp/celo-infra/modules/tx-node/startup.sh deleted file mode 100644 index 435a14abf9d..00000000000 --- a/packages/terraform-modules-public/gcp/celo-infra/modules/tx-node/startup.sh +++ /dev/null @@ -1,438 +0,0 @@ -#!/bin/bash - - -# ---- Configure logrotate ---- -echo "Configuring logrotate" | logger -cat <<'EOF' > '/etc/logrotate.d/rsyslog' -/var/log/syslog -/var/log/mail.info -/var/log/mail.warn -/var/log/mail.err -/var/log/mail.log -/var/log/daemon.log -/var/log/kern.log -/var/log/auth.log -/var/log/user.log -/var/log/lpr.log -/var/log/cron.log -/var/log/debug -/var/log/messages -{ - rotate 3 - daily - missingok - notifempty - delaycompress - compress - sharedscripts - postrotate - #invoke-rc.d rsyslog rotate > /dev/null # does not work on debian10 - kill -HUP `pidof rsyslogd` - endscript -} -EOF - -# ---- Tune rsyslog to avoid redundantly logging docker output -echo "Updating rsyslog.conf to avoid redundantly logging docker output" -cat <<'EOF' > /etc/rsyslog.conf -# /etc/rsyslog.conf configuration file for rsyslog -# -# For more information install rsyslog-doc and see -# /usr/share/doc/rsyslog-doc/html/configuration/index.html - -################# -#### MODULES #### -################# - -module(load="imuxsock") # provides support for local system logging -module(load="imklog") # provides kernel logging support - -########################### -#### GLOBAL DIRECTIVES #### -########################### - -# -# Use traditional timestamp format. -# To enable high precision timestamps, comment out the following line. -# -$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat - -# -# Set the default permissions for all log files. -# -$FileOwner root -$FileGroup adm -$FileCreateMode 0640 -$DirCreateMode 0755 -$Umask 0022 - -# -# Where to place spool and state files -# -$WorkDirectory /var/spool/rsyslog - -# -# Include all config files in /etc/rsyslog.d/ -# -$IncludeConfig /etc/rsyslog.d/*.conf - - -############### -#### RULES #### -############### - -# -# First some standard log files. Log by facility. -# -auth,authpriv.* /var/log/auth.log -*.*;auth,authpriv.none -/var/log/syslog -kern.* -/var/log/kern.log - - -# -# Some "catch-all" log files. -# -*.=debug;\ - auth,authpriv.none;\ - news.none;mail.none -/var/log/debug -*.=info;*.=notice;*.=warn;\ - auth,authpriv.none;\ - cron,daemon.none;\ - mail,news.none -/var/log/messages - -# -# Emergencies are sent to everybody logged in. -# -*.emerg :omusrmsg:* -EOF - -# ---- Restart rsyslogd -echo "Restarting rsyslogd" -systemctl restart rsyslog - -# ---- Create backup script -echo "Creating chaindata backup script" | logger -cat <<'EOF' > /root/backup.sh -#!/bin/bash -# This script stops geth, tars up the chaindata (with gzip compression), and copies it to GCS. -# The 'chaindata' GCS bucket has versioning enabled, so if a corrupted tarball is uploaded, an older version can be selected for restore. -# This takes quit some time, and takes quite a bit of local disk. -# The rsync variant (below) is more efficient, but tarballs are more portable. -set -x - -echo "Starting chaindata backup" | logger -systemctl stop geth.service -sleep 5 -tar -C /root/.celo/celo -zcvf /root/.celo/celo/chaindata.tgz chaindata -gsutil cp /root/.celo/celo/chaindata.tgz gs://${gcloud_project}-chaindata -rm -f /root/.celo/celo/chaindata.tgz -echo "Chaindata backup completed" | logger -sleep 3 -systemctl start geth.service -EOF -chmod u+x /root/backup.sh - -# ---- Create rsync backup script -echo "Creating rsync chaindata backup script" | logger -cat <<'EOF' > /root/backup_rsync.sh -#!/bin/bash -# This script stops geth, and uses rsync to copy chaindata to GCS. -set -x -CELO_DIR="/root/.celo/celo" - -echo "Starting rsync chaindata backup" | logger -systemctl stop geth.service -sleep 5 -gsutil -m rsync -d -r /root/.celo/celo/chaindata gs://${gcloud_project}-chaindata-rsync -echo "rsync chaindata backup completed" | logger -sleep 3 -systemctl start geth.service -EOF -chmod u+x /root/backup_rsync.sh - -# ---- Add backups to cron -# note that this will make the txnode unavailable during the backup, so do not run on prod systems -cat <<'EOF' > /root/backup.crontab -# m h dom mon dow command -# backup full tarball once a week at 00:57 -#57 0 * * 0 /root/backup.sh > /dev/null 2>&1 -# backup via rsync run every day at 00:17 -#17 0 * * * /root/backup_rsync.sh > /dev/null 2>&1 -EOF -#/usr/bin/crontab /root/backup.crontab - -# ---- Create restore script -echo "Creating chaindata restore script" | logger -cat <<'EOF' > /root/restore.sh -#!/bin/bash -set -x - -# test to see if chaindata exists in bucket -gsutil -q stat gs://${gcloud_project}-chaindata/chaindata.tgz -if [ $? -eq 0 ] -then - #chaindata exists in bucket - mkdir -p /root/.celo/celo - mkdir -p /root/.celo/celo/restore - echo "downloading chaindata from gs://${gcloud_project}-chaindata/chaindata.tgz" | logger - gsutil cp gs://${gcloud_project}-chaindata/chaindata.tgz /root/.celo/celo/restore/chaindata.tgz - echo "stopping geth to untar chaindata" | logger - systemctl stop geth.service - sleep 3 - echo "Deleting old chaindata" | logger - rm -rf /root/.celo/celo/chaindata/* - echo "untarring chaindata" | logger - tar zxvf /root/.celo/celo/restore/chaindata.tgz --directory /root/.celo/celo - echo "removing chaindata tarball" | logger - rm -rf /root/.celo/celo/restore/chaindata.tgz - sleep 3 - echo "starting geth" | logger - systemctl start geth.service - else - echo "No chaindata.tgz found in bucket gs://${gcloud_project}-chaindata, aborting warp restore" | logger - fi -EOF -chmod u+x /root/restore.sh - -# ---- Create rsync restore script -echo "Creating rsync chaindata restore script" | logger -cat <<'EOF' > /root/restore_rsync.sh -#!/bin/bash -set -x - -# test to see if chaindata exists in the rsync chaindata bucket -gsutil -q stat gs://${gcloud_project}-chaindata-rsync/CURRENT -if [ $? -eq 0 ] -then - #chaindata exists in bucket - echo "stopping geth" | logger - systemctl stop geth.service - echo "downloading chaindata via rsync from gs://${gcloud_project}-chaindata-rsync" | logger - mkdir -p /root/.celo/celo/chaindata - gsutil -m rsync -d -r gs://${gcloud_project}-chaindata-rsync /root/.celo/celo/chaindata - echo "restarting geth" | logger - sleep 3 - systemctl start geth.service - else - echo "No chaindata found in bucket gs://${gcloud_project}-chaindata-rsync, aborting warp restore" | logger - fi -EOF -chmod u+x /root/restore_rsync.sh - -# ---- Useful aliases ---- -echo "Configuring aliases" | logger -echo "alias ll='ls -laF'" >> /etc/skel/.bashrc -echo "alias ll='ls -laF'" >> /root/.bashrc -echo "alias gattach='docker exec -it geth geth attach'" >> /etc/skel/.bashrc - -# ---- Install Stackdriver Agent -echo "Installing Stackdriver agent" | logger -curl -sSO https://dl.google.com/cloudagents/add-monitoring-agent-repo.sh -bash add-monitoring-agent-repo.sh -apt update -y -apt install -y stackdriver-agent -systemctl restart stackdriver-agent - -# ---- Install Fluent Log Collector -echo "Installing google fluent log collector agent" | logger -curl -sSO https://dl.google.com/cloudagents/add-logging-agent-repo.sh -bash add-logging-agent-repo.sh -apt update -y -apt install -y google-fluentd -apt install -y google-fluentd-catch-all-config-structured -systemctl restart google-fluentd - -# ---- Set Up Persistent Disk ---- - -# gives a path similar to `/dev/sdb` -DISK_PATH=$(readlink -f /dev/disk/by-id/google-${attached_disk_name}) -DATA_DIR=/root/.celo - -echo "Setting up persistent disk ${attached_disk_name} at $DISK_PATH..." - -DISK_FORMAT=ext4 -CURRENT_DISK_FORMAT=$(lsblk -i -n -o fstype $DISK_PATH) - -echo "Checking if disk $DISK_PATH format $CURRENT_DISK_FORMAT matches desired $DISK_FORMAT..." - -# If the disk has already been formatted previously (this will happen -# if this instance has been recreated with the same disk), we skip formatting -if [[ $CURRENT_DISK_FORMAT == $DISK_FORMAT ]]; then - echo "Disk $DISK_PATH is correctly formatted as $DISK_FORMAT" -else - echo "Disk $DISK_PATH is not formatted correctly, formatting as $DISK_FORMAT..." - mkfs.ext4 -m 0 -F -E lazy_itable_init=0,lazy_journal_init=0,discard $DISK_PATH -fi - -# Mounting the volume -echo "Mounting $DISK_PATH onto $DATA_DIR" -mkdir -p $DATA_DIR -DISK_UUID=$(blkid $DISK_PATH | cut -d '"' -f2) -echo "UUID=$DISK_UUID $DATA_DIR auto discard,defaults 0 0" >> /etc/fstab -mount $DATA_DIR - -# ---- Setup swap -echo "Setting up swapfile" | logger -fallocate -l 4G /root/.celo/swapfile -chmod 600 /root/.celo/swapfile -mkswap /root/.celo/swapfile -swapon /root/.celo/swapfile -swapon -s - -# Remove existing chain data -[[ ${reset_geth_data} == "true" ]] && rm -rf $DATA_DIR/geth -mkdir -p $DATA_DIR/account - -# ---- Install Docker ---- - -echo "Installing Docker..." | logger -apt update -y && apt upgrade -y -apt install -y apt-transport-https ca-certificates curl software-properties-common gnupg2 htop screen -curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add - -add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable" -apt update -y && apt upgrade -y -apt install -y docker-ce -apt upgrade -y -systemctl start docker - -# ---- Config /etc/screenrc ---- -echo "Configuring /etc/screenrc" | logger -cat <<'EOF' >> '/etc/screenrc' -bindkey -k k1 select 1 # F1 = screen 1 -bindkey -k k2 select 2 # F2 = screen 2 -bindkey -k k3 select 3 # F3 = screen 3 -bindkey -k k4 select 4 # F4 = screen 4 -bindkey -k k5 select 5 # F5 = screen 5 -bindkey -k k6 select 6 # F6 = screen 6 -bindkey -k k7 select 7 # F7 = screen 7 -bindkey -k k8 select 8 # F8 = screen 8 -bindkey -k k9 select 9 # F9 = screen 9 -bindkey -k F1 prev # F11 = prev -bindkey -k F2 next # F12 = next -EOF - -echo "Configuring Docker..." | logger -cat <<'EOF' > '/etc/docker/daemon.json' -{ - "log-driver": "json-file", - "log-opts": { - "max-size": "10m", - "max-file": "3", - "mode": "non-blocking" - } -} -EOF - -echo "Restarting docker" | logger -systemctl restart docker - -# ---- Set Up and Run Geth ---- - -echo "Configuring Geth" | logger - -GETH_NODE_DOCKER_IMAGE=${geth_node_docker_image_repository}:${geth_node_docker_image_tag} - -echo "Pulling geth..." -docker pull $GETH_NODE_DOCKER_IMAGE - -IN_MEMORY_DISCOVERY_TABLE_FLAG="" -[[ ${in_memory_discovery_table} == "true" ]] && IN_MEMORY_DISCOVERY_TABLE_FLAG="--use-in-memory-discovery-table" - -# Load configuration to files -mkdir -p $DATA_DIR/account - -echo -n '${rid}' > $DATA_DIR/replica_id -echo -n '${ip_address}' > $DATA_DIR/ipAddress -echo -n '${attestation_signer_geth_account_secret}' > $DATA_DIR/account/accountSecret -echo -n '${attestation_signer_private_key}' > $DATA_DIR/pkey - -echo "Starting geth..." | logger -# We need to override the entrypoint in the geth image (which is originally `geth`). -# `geth account import` fails when the account has already been imported. In -# this case, we do not want to pipefail - -docker run \ - --rm \ - --net=host \ - -v $DATA_DIR:$DATA_DIR \ - --entrypoint /bin/sh \ - -i $GETH_NODE_DOCKER_IMAGE \ - -c "geth account import --password $DATA_DIR/account/accountSecret $DATA_DIR/pkey | true" - -cat </etc/systemd/system/geth.service -[Unit] -Description=Docker Container %N -Requires=docker.service -After=docker.service - -[Service] -Restart=always -ExecStart=/usr/bin/docker run \\ - --rm \\ - --name geth \\ - --net=host \\ - -v $DATA_DIR:$DATA_DIR \\ - --entrypoint /bin/sh \\ - $GETH_NODE_DOCKER_IMAGE -c "\\ - geth \\ - --etherbase ${attestation_signer_address} \\ - --unlock ${attestation_signer_address} \\ - --password $DATA_DIR/account/accountSecret \\ - --allow-insecure-unlock \\ - --nousb \\ - --maxpeers ${max_peers} \\ - --rpc \\ - --rpcaddr 0.0.0.0 \\ - --rpcapi=eth,net,web3 \\ - --rpccorsdomain='*' \\ - --rpcvhosts=* \\ - --ws \\ - --wsaddr 0.0.0.0 \\ - --wsorigins=* \\ - --wsapi=eth,net,web3 \\ - --nodekey=$DATA_DIR/pkey \\ - --networkid=${network_id} \\ - --syncmode=full \\ - --consoleformat=json \\ - --consoleoutput=stdout \\ - --verbosity=${geth_verbosity} \\ - --nat=extip:${ip_address} \\ - --metrics \\ - --pprof \\ - $IN_MEMORY_DISCOVERY_TABLE_FLAG \\ - --light.serve 0 \\ - --syncmode lightest \\ - " -ExecStop=/usr/bin/docker stop -t 60 %N - -[Install] -WantedBy=default.target -EOF - -echo "Starting Geth" | logger -systemctl daemon-reload -systemctl enable geth.service - -echo "Adding DC to docker group" | logger -usermod -aG docker dc - -# note that we no longer restore chaindata since txnode is now using syncmode=lightest -# --- run restore script -# this script tries to restore chaindata from a GCS hosted tarball. -# if the chaindata doesn't exist on GCS, geth will start normal (slow) p2p sync -#echo "Restoring chaindata from backup tarball" | logger -#bash /root/restore.sh - -# todo: add some logic to look at the chaindata tarball bucket versus the rsync bucket and pick the best one. -# for now we try both, with rsync taking precedence b/c it runs last. - -# --- run rsync restore script -# this script tries to restore chaindata from a GCS hosted bucket via rsync. -# if the chaindata doesn't exist on GCS, geth will start normal (slow) p2p sync, perhaps boosted by what the tarball provided -#echo "Restoring chaindata from backup via rsync" | logger -#bash /root/restore_rsync.sh - -#--- remove compilers -echo "Removing compilers" | logger -sudo apt remove -y build-essential gcc make linux-compiler-gcc-8-x86 cpp -sudo apt -y autoremove \ No newline at end of file diff --git a/packages/terraform-modules-public/gcp/celo-infra/modules/tx-node/variables.tf b/packages/terraform-modules-public/gcp/celo-infra/modules/tx-node/variables.tf deleted file mode 100644 index 8b200e57cd6..00000000000 --- a/packages/terraform-modules-public/gcp/celo-infra/modules/tx-node/variables.tf +++ /dev/null @@ -1,112 +0,0 @@ -variable block_time { - type = number - description = "Number of seconds between each block" -} - -variable celo_env { - type = string - description = "Name of the Celo environment" -} - -variable gcloud_project { - type = string - description = "Name of the Google Cloud project to use" -} - -variable instance_type { - description = "The instance type" - type = string - default = "n1-standard-1" -} - -variable ethstats_host { - type = string - description = "Ethstats url or IP address" -} - -#variable genesis_content_base64 { -# type = string -# description = "Content of the genesis file encoded in base64" -#} - -variable geth_exporter_docker_image_repository { - type = string - description = "Repository of the geth exporter docker image" -} - -variable geth_exporter_docker_image_tag { - type = string - description = "Tag of the geth exporter docker image" -} - -variable geth_node_docker_image_repository { - type = string - description = "Repository of the geth docker image" -} - -variable geth_node_docker_image_tag { - type = string - description = "Tag of the geth docker image" -} - -variable geth_verbosity { - type = number - description = "Verbosity of the tx-nodes" -} - -variable in_memory_discovery_table { - type = bool - description = "Specifies whether to use an in memory discovery table" -} - -variable network_id { - type = number - description = "The network ID number" -} - -variable network_name { - type = string - description = "Name of the GCP network the tx-node VM is in" -} - -variable tx_node_count { - type = number - description = "Number of tx-nodes to create" -} - -#variable bootnodes_base64 { -# type = string -# description = "Bootnodes ethereum address encoded as base64" -#} - -variable reset_geth_data { - type = bool - description = "Specifies if the existing chain data should be removed while creating the instance" - default = true -} - -variable txnode_max_peers { - type = number - description = "Max number of peers to connect with" - default = 120 -} - -variable attestation_signer_addresses { - type = list(string) - description = "The address to use for signing attestation requests" -} - -variable attestation_signer_account_passwords { - type = list(string) - description = "Array with the attestation_signer account passwords" -} - -variable attestation_signer_private_keys { - type = list(string) - description = "Array with the attestation_signer private keys" -} - -variable service_account_scopes { - type = list(string) - description = "Scopes to apply to the service account which all nodes in the cluster will inherit" -} \ No newline at end of file diff --git a/packages/terraform-modules-public/gcp/celo-infra/modules/validator/main.tf b/packages/terraform-modules-public/gcp/celo-infra/modules/validator/main.tf deleted file mode 100644 index 5b0354f3b46..00000000000 --- a/packages/terraform-modules-public/gcp/celo-infra/modules/validator/main.tf +++ /dev/null @@ -1,86 +0,0 @@ -locals { - attached_disk_name = "celo-data" - #name_prefix = "${var.celo_env}-validator" - name_prefix = "${var.gcloud_project}-validator" -} - -resource "google_compute_address" "validator_internal" { - name = "${local.name_prefix}-internal-address-${count.index}" - address_type = "INTERNAL" - purpose = "GCE_ENDPOINT" - - count = var.validator_count -} - -resource "google_compute_instance" "validator" { - name = "${local.name_prefix}-${count.index}" - machine_type = var.instance_type - - #deletion_protection = false - deletion_protection = true - - count = var.validator_count - - tags = ["${var.celo_env}-validator"] - - allow_stopping_for_update = true - - boot_disk { - initialize_params { - image = "debian-cloud/debian-10" - } - } - - attached_disk { - source = google_compute_disk.validator[count.index].self_link - device_name = local.attached_disk_name - } - - network_interface { - network = var.network_name - network_ip = google_compute_address.validator_internal[count.index].address - } - - metadata_startup_script = templatefile( - format("%s/startup.sh", path.module), { - attached_disk_name : local.attached_disk_name, - block_time : var.block_time, - ethstats_host : var.ethstats_host, - geth_exporter_docker_image_repository : var.geth_exporter_docker_image_repository, - geth_exporter_docker_image_tag : var.geth_exporter_docker_image_tag, - geth_node_docker_image_repository : var.geth_node_docker_image_repository, - geth_node_docker_image_tag : var.geth_node_docker_image_tag, - geth_verbosity : var.geth_verbosity, - in_memory_discovery_table : var.in_memory_discovery_table, - ip_address : google_compute_address.validator_internal[count.index].address, - istanbul_request_timeout_ms : var.istanbul_request_timeout_ms, - max_peers : var.validator_max_peers, - network_id : var.network_id, - gcloud_project : var.gcloud_project, - rid : count.index, - validator_name : var.validator_name, - validator_account_address : var.validator_signer_account_addresses[count.index], - validator_private_key : var.validator_signer_private_keys[count.index], - validator_geth_account_secret : var.validator_signer_account_passwords[count.index], - proxy_enode : var.proxy_enodes[count.index], - proxy_internal_ip : var.proxy_internal_ips[count.index], - proxy_external_ip : var.proxy_external_ips[count.index], - reset_geth_data : var.reset_geth_data - } - ) - - service_account { - scopes = var.service_account_scopes - } -} - -resource "google_compute_disk" "validator" { - name = "${local.name_prefix}-celo-data-disk-${count.index}" - count = var.validator_count - - #type = "pd-ssd" - type = "pd-standard" #disk I/O doesn't yet warrant SSD backed validators/proxies - # in GB - size = 50 - physical_block_size_bytes = 4096 -} diff --git a/packages/terraform-modules-public/gcp/celo-infra/modules/validator/outputs.tf b/packages/terraform-modules-public/gcp/celo-infra/modules/validator/outputs.tf deleted file mode 100644 index 36040dbb6ab..00000000000 --- a/packages/terraform-modules-public/gcp/celo-infra/modules/validator/outputs.tf +++ /dev/null @@ -1,3 +0,0 @@ -output internal_ip_addresses { - value = google_compute_address.validator_internal.*.address -} diff --git a/packages/terraform-modules-public/gcp/celo-infra/modules/validator/startup.sh b/packages/terraform-modules-public/gcp/celo-infra/modules/validator/startup.sh deleted file mode 100644 index bf63d5e61dc..00000000000 --- a/packages/terraform-modules-public/gcp/celo-infra/modules/validator/startup.sh +++ /dev/null @@ -1,405 +0,0 @@ -#!/bin/bash - -# ---- Configure logrotate ---- -echo "Configuring logrotate" | logger -cat <<'EOF' > '/etc/logrotate.d/rsyslog' -/var/log/syslog -/var/log/mail.info -/var/log/mail.warn -/var/log/mail.err -/var/log/mail.log -/var/log/daemon.log -/var/log/kern.log -/var/log/auth.log -/var/log/user.log -/var/log/lpr.log -/var/log/cron.log -/var/log/debug -/var/log/messages -{ - rotate 3 - daily - missingok - notifempty - delaycompress - compress - sharedscripts - postrotate - #invoke-rc.d rsyslog rotate > /dev/null # does not work on debian10 - kill -HUP `pidof rsyslogd` - endscript -} -EOF - -# ---- Tune rsyslog to avoid redundantly logging docker output -echo "Updating rsyslog.conf to avoid redundantly logging docker output" -cat <<'EOF' > /etc/rsyslog.conf -# /etc/rsyslog.conf configuration file for rsyslog -# -# For more information install rsyslog-doc and see -# /usr/share/doc/rsyslog-doc/html/configuration/index.html - -################# -#### MODULES #### -################# - -module(load="imuxsock") # provides support for local system logging -module(load="imklog") # provides kernel logging support - -########################### -#### GLOBAL DIRECTIVES #### -########################### - -# -# Use traditional timestamp format. -# To enable high precision timestamps, comment out the following line. -# -$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat - -# -# Set the default permissions for all log files. -# -$FileOwner root -$FileGroup adm -$FileCreateMode 0640 -$DirCreateMode 0755 -$Umask 0022 - -# -# Where to place spool and state files -# -$WorkDirectory /var/spool/rsyslog - -# -# Include all config files in /etc/rsyslog.d/ -# -$IncludeConfig /etc/rsyslog.d/*.conf - - -############### -#### RULES #### -############### - -# -# First some standard log files. Log by facility. -# -auth,authpriv.* /var/log/auth.log -*.*;auth,authpriv.none -/var/log/syslog -kern.* -/var/log/kern.log - - -# -# Some "catch-all" log files. -# -*.=debug;\ - auth,authpriv.none;\ - news.none;mail.none -/var/log/debug -*.=info;*.=notice;*.=warn;\ - auth,authpriv.none;\ - cron,daemon.none;\ - mail,news.none -/var/log/messages - -# -# Emergencies are sent to everybody logged in. -# -*.emerg :omusrmsg:* -EOF - -# ---- Restart rsyslogd -echo "Restarting rsyslogd" -systemctl restart rsyslog - -# ---- Create restore script -echo "Creating chaindata restore script" | logger -cat <<'EOF' > /root/restore.sh -#!/bin/bash -set -x - -# test to see if chaindata exists in bucket -gsutil -q stat gs://${gcloud_project}-chaindata/chaindata.tgz -if [ $? -eq 0 ] -then - #chaindata exists in bucket - mkdir -p /root/.celo/celo - mkdir -p /root/.celo/celo/restore - echo "downloading chaindata from gs://${gcloud_project}-chaindata/chaindata.tgz" | logger - gsutil cp gs://${gcloud_project}-chaindata/chaindata.tgz /root/.celo/celo/restore/chaindata.tgz - echo "stopping geth to untar chaindata" | logger - systemctl stop geth.service - sleep 3 - echo "Deleting old chaindata" | logger - rm -rf /root/.celo/celo/chaindata/* - echo "untarring chaindata" | logger - tar zxvf /root/.celo/celo/restore/chaindata.tgz --directory /root/.celo/celo - echo "removing chaindata tarball" | logger - rm -rf /root/.celo/celo/restore/chaindata.tgz - sleep 3 - echo "starting geth" | logger - systemctl start geth.service - else - echo "No chaindata.tgz found in bucket gs://${gcloud_project}-chaindata, aborting warp restore" | logger - fi -EOF -chmod u+x /root/restore.sh - -# ---- Create rsync restore script -echo "Creating rsync chaindata restore script" | logger -cat <<'EOF' > /root/restore_rsync.sh -#!/bin/bash -set -x - -# test to see if chaindata exists in the rsync chaindata bucket -gsutil -q stat gs://${gcloud_project}-chaindata-rsync/CURRENT -if [ $? -eq 0 ] -then - #chaindata exists in bucket - echo "stopping geth" | logger - systemctl stop geth.service - echo "downloading chaindata via rsync from gs://${gcloud_project}-chaindata-rsync" | logger - mkdir -p /root/.celo/celo/chaindata - gsutil -m rsync -d -r gs://${gcloud_project}-chaindata-rsync /root/.celo/celo/chaindata - echo "restarting geth" | logger - sleep 3 - systemctl start geth.service - else - echo "No chaindata found in bucket gs://${gcloud_project}-chaindata-rsync, aborting warp restore" | logger - fi -EOF -chmod u+x /root/restore_rsync.sh - -# ---- Useful aliases ---- -echo "Configuring aliases" | logger -echo "alias ll='ls -laF'" >> /etc/skel/.bashrc -echo "alias ll='ls -laF'" >> /root/.bashrc -echo "alias gattach='docker exec -it geth geth attach'" >> /etc/skel/.bashrc - -# ---- Install Stackdriver Agent -echo "Installing Stackdriver agent" | logger -curl -sSO https://dl.google.com/cloudagents/add-monitoring-agent-repo.sh -bash add-monitoring-agent-repo.sh -apt update -y -apt install -y stackdriver-agent -systemctl restart stackdriver-agent - -# ---- Install Fluent Log Collector -echo "Installing google fluent log collector agent" | logger -curl -sSO https://dl.google.com/cloudagents/add-logging-agent-repo.sh -bash add-logging-agent-repo.sh -apt update -y -apt install -y google-fluentd -apt install -y google-fluentd-catch-all-config-structured -systemctl restart google-fluentd - -# ---- Set Up Persistent Disk ---- - -# gives a path similar to `/dev/sdb` -DISK_PATH=$(readlink -f /dev/disk/by-id/google-${attached_disk_name}) -DATA_DIR=/root/.celo - -echo "Setting up persistent disk ${attached_disk_name} at $DISK_PATH..." - -DISK_FORMAT=ext4 -CURRENT_DISK_FORMAT=$(lsblk -i -n -o fstype $DISK_PATH) - -echo "Checking if disk $DISK_PATH format $CURRENT_DISK_FORMAT matches desired $DISK_FORMAT..." - -# If the disk has already been formatted previously (this will happen -# if this instance has been recreated with the same disk), we skip formatting -if [[ $CURRENT_DISK_FORMAT == $DISK_FORMAT ]]; then - echo "Disk $DISK_PATH is correctly formatted as $DISK_FORMAT" -else - echo "Disk $DISK_PATH is not formatted correctly, formatting as $DISK_FORMAT..." - mkfs.ext4 -m 0 -F -E lazy_itable_init=0,lazy_journal_init=0,discard $DISK_PATH -fi - -# Mounting the volume -echo "Mounting $DISK_PATH onto $DATA_DIR" -mkdir -p $DATA_DIR -DISK_UUID=$(blkid $DISK_PATH | cut -d '"' -f2) -echo "UUID=$DISK_UUID $DATA_DIR auto discard,defaults 0 0" >> /etc/fstab -mount $DATA_DIR - -# ---- Setup swap -echo "Setting up swapfile" | logger -fallocate -l 4G /root/.celo/swapfile -chmod 600 /root/.celo/swapfile -mkswap /root/.celo/swapfile -swapon /root/.celo/swapfile -swapon -s - -# Remove existing chain data -[[ ${reset_geth_data} == "true" ]] && rm -rf $DATA_DIR/geth -mkdir -p $DATA_DIR/account - -# ---- Install Docker ---- - -echo "Installing Docker..." | logger -apt update -y && apt upgrade -y -apt install -y apt-transport-https ca-certificates curl software-properties-common gnupg2 htop screen -curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add - -add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable" -apt update -y && apt upgrade -y -apt install -y docker-ce -apt upgrade -y -systemctl start docker - -# ---- Config /etc/screenrc ---- -echo "Configuring /etc/screenrc" | logger -cat <<'EOF' >> '/etc/screenrc' -bindkey -k k1 select 1 # F1 = screen 1 -bindkey -k k2 select 2 # F2 = screen 2 -bindkey -k k3 select 3 # F3 = screen 3 -bindkey -k k4 select 4 # F4 = screen 4 -bindkey -k k5 select 5 # F5 = screen 5 -bindkey -k k6 select 6 # F6 = screen 6 -bindkey -k k7 select 7 # F7 = screen 7 -bindkey -k k8 select 8 # F8 = screen 8 -bindkey -k k9 select 9 # F9 = screen 9 -bindkey -k F1 prev # F11 = prev -bindkey -k F2 next # F12 = next -EOF - -echo "Configuring Docker..." | logger -cat <<'EOF' > '/etc/docker/daemon.json' -{ - "log-driver": "json-file", - "log-opts": { - "max-size": "10m", - "max-file": "3", - "mode": "non-blocking" - } -} -EOF - -echo "Restarting docker" | logger -systemctl restart docker - -# ---- Set Up and Run Geth ---- - -echo "Configuring Geth" | logger - -GETH_NODE_DOCKER_IMAGE=${geth_node_docker_image_repository}:${geth_node_docker_image_tag} - -ACCOUNT_ADDRESS=${validator_account_address} -echo "Address: $ACCOUNT_ADDRESS" - -echo "Proxy enode address: ${proxy_enode}" -echo "Proxy internal ip address: ${proxy_internal_ip}" -echo "Proxy external ip address: ${proxy_external_ip}" -PROXY_INTERNAL_ENODE="enode://${proxy_enode}@${proxy_internal_ip}:30503" -PROXY_EXTERNAL_ENODE="enode://${proxy_enode}@${proxy_external_ip}:30303" - -PROXY_URL="$PROXY_INTERNAL_ENODE;$PROXY_EXTERNAL_ENODE" -echo "Proxy URL: $PROXY_URL" - -echo "Pulling geth..." | logger -docker pull $GETH_NODE_DOCKER_IMAGE - -IN_MEMORY_DISCOVERY_TABLE_FLAG="" -[[ ${in_memory_discovery_table} == "true" ]] && IN_MEMORY_DISCOVERY_TABLE_FLAG="--use-in-memory-discovery-table" - -# Load configuration to files -echo -n '${rid}' > $DATA_DIR/replica_id -echo -n '${ip_address}' > $DATA_DIR/ipAddress -echo -n '${validator_private_key}' > $DATA_DIR/pkey -echo -n '${validator_account_address}' > $DATA_DIR/address -echo -n '${proxy_enode}' > $DATA_DIR/proxyEnodeAddress -echo -n '$PROXY_URL' > $DATA_DIR/proxyURL -echo -n '${validator_geth_account_secret}' > $DATA_DIR/account/accountSecret -echo -n $PROXY_INTERNAL_ENODE > /root/.celo/proxyInternalEnode -echo -n $PROXY_EXTERNAL_ENODE > /root/.celo/proxyExternalEnode - -echo "Starting geth..." | logger -# We need to override the entrypoint in the geth image (which is originally `geth`). -# `geth account import` fails when the account has already been imported. In -# this case, we do not want to pipefail - -docker run \ - --rm \ - --net=host \ - -v $DATA_DIR:$DATA_DIR \ - --entrypoint /bin/sh \ - -i $GETH_NODE_DOCKER_IMAGE \ - -c "geth account import --password $DATA_DIR/account/accountSecret $DATA_DIR/pkey | true" - -cat </etc/systemd/system/geth.service -[Unit] -Description=Docker Container %N -Requires=docker.service -After=docker.service - -[Service] -Restart=always -ExecStart=/usr/bin/docker run \\ - --rm \\ - --name geth \\ - --net=host \\ - -v $DATA_DIR:$DATA_DIR \\ - --entrypoint /bin/sh \\ - $GETH_NODE_DOCKER_IMAGE -c "\\ - geth \\ - --etherbase=$ACCOUNT_ADDRESS \\ - --password=$DATA_DIR/account/accountSecret \\ - --unlock=$ACCOUNT_ADDRESS \\ - --allow-insecure-unlock \\ - --nousb \\ - --mine \\ - --rpc \\ - --rpcaddr 0.0.0.0 \\ - --rpcapi=eth,net,web3 \\ - --rpccorsdomain='*' \\ - --rpcvhosts=* \\ - --ws \\ - --wsaddr 0.0.0.0 \\ - --wsorigins=* \\ - --wsapi=eth,net,web3 \\ - --networkid=${network_id} \\ - --syncmode=full \\ - --consoleformat=json \\ - --consoleoutput=stdout \\ - --verbosity=${geth_verbosity} \\ - --ethstats=${validator_name}@${ethstats_host} \\ - --istanbul.blockperiod=${block_time} \\ - --istanbul.requesttimeout=${istanbul_request_timeout_ms} \\ - --maxpeers=${max_peers} \\ - --nat=extip:${ip_address} \\ - --metrics \\ - --pprof \\ - $IN_MEMORY_DISCOVERY_TABLE_FLAG \\ - --nodiscover \\ - --proxy.proxied \\ - --proxy.proxyenodeurlpairs=\\"$PROXY_URL\\" \\ - --light.serve 0 \\ - " -ExecStop=/usr/bin/docker stop -t 60 %N - -[Install] -WantedBy=default.target -EOF - -echo "Starting Geth" | logger -systemctl daemon-reload -systemctl enable geth.service - -echo "Adding DC to docker group" | logger -usermod -aG docker dc - -# --- run restore script -# this script tries to restore chaindata from a GCS hosted tarball. -# if the chaindata doesn't exist on GCS, geth will start normal (slow) p2p sync -echo "Restoring chaindata from backup tarball" | logger -bash /root/restore.sh - -# todo: add some logic to look at the chaindata tarball bucket versus the rsync bucket and pick the best one. -# for now we try both, with rsync taking precedence b/c it runs last. - -# --- run rsync restore script -# this script tries to restore chaindata from a GCS hosted bucket via rsync. -# if the chaindata doesn't exist on GCS, geth will start normal (slow) p2p sync, perhaps boosted by what the tarball provided -echo "Restoring chaindata from backup via rsync" | logger -bash /root/restore_rsync.sh - -#--- remove compilers -echo "Removing compilers" | logger -sudo apt remove -y build-essential gcc make linux-compiler-gcc-8-x86 cpp -sudo apt -y autoremove diff --git a/packages/terraform-modules-public/gcp/celo-infra/modules/validator/variables.tf b/packages/terraform-modules-public/gcp/celo-infra/modules/validator/variables.tf deleted file mode 100644 index c09685a5a7d..00000000000 --- a/packages/terraform-modules-public/gcp/celo-infra/modules/validator/variables.tf +++ /dev/null @@ -1,132 +0,0 @@ -variable block_time { - type = number - description = "Number of seconds between each block" -} - -variable celo_env { - type = string - description = "Name of the Celo environment" -} - -variable gcloud_project { - type = string - description = "Name of the Google Cloud project to use" -} - -variable instance_type { - description = "The instance type" - type = string - default = "n1-standard-2" -} - -variable ethstats_host { - type = string - description = "Celostats url or IP address" -} - -variable geth_exporter_docker_image_repository { - type = string - description = "Repository of the geth exporter docker image" -} - -variable geth_exporter_docker_image_tag { - type = string - description = "Tag of the geth exporter docker image" -} - -variable geth_node_docker_image_repository { - type = string - description = "Repository of the geth docker image" -} - -variable geth_node_docker_image_tag { - type = string - description = "Tag of the geth docker image" -} - -variable geth_verbosity { - type = number - description = "Verbosity of the validator nodes" -} - -variable in_memory_discovery_table { - type = bool - description = "Specifies whether to use an in memory discovery table" -} - -variable istanbul_request_timeout_ms { - type = number - description = "The number of ms for the istanbul request timeout" -} - -variable network_id { - type = number - description = "The network ID number" -} - -variable network_name { - type = string - description = "Name of the GCP network the validator VM is in" -} - -variable tx_node_count { - type = number - description = "Number of tx-nodes that are created" -} - -variable validator_count { - type = number - description = "Number of validators to create" -} - -variable validator_signer_account_addresses { - type = list(string) - description = "Array with the Validator account addresses" -} - -variable validator_signer_private_keys { - type = list(string) - description = "Array with the Validator account private keys" -} - -variable validator_signer_account_passwords { - type = list(string) - description = "Array with the Validator account passwords" -} - -variable proxy_enodes { - type = list(string) - description = "Array list with the proxy enode address (without enode://)" -} - -variable proxy_internal_ips { - type = list(string) - description = "Array list with the proxy internal addresses" -} - -variable proxy_external_ips { - type = list(string) - description = "Array list with the proxy external addresses" -} - -variable reset_geth_data { - type = bool - description = "Specifies if the existing chain data should be removed while creating the instance" - default = true -} - -variable validator_name { - type = string - description = "The validator Name for ethstats" -} - -variable validator_max_peers { - type = number - description = "Max number of peers to connect with" - default = 120 -} - -variable "service_account_scopes" { - description = "Scopes to apply to the service account which all nodes in the cluster will inherit" - type = list(string) -} \ No newline at end of file diff --git a/packages/terraform-modules-public/gcp/celo-infra/outputs.tf b/packages/terraform-modules-public/gcp/celo-infra/outputs.tf deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/packages/terraform-modules-public/gcp/celo-infra/variables.tf b/packages/terraform-modules-public/gcp/celo-infra/variables.tf deleted file mode 100644 index 30e300709d0..00000000000 --- a/packages/terraform-modules-public/gcp/celo-infra/variables.tf +++ /dev/null @@ -1,311 +0,0 @@ -variable block_time { - type = number - description = "Number of seconds between each block" -} - -variable celo_env { - type = string - description = "Name of the testnet Celo environment" -} - -variable ethstats_host { - type = string - description = "Ethstats url or IP address" -} - -variable gcloud_project { - type = string - description = "Name of the Google Cloud project to use" -} - -variable instance_types { - description = "The instance type for each component" - type = map(string) - - default = { - validator = "n1-standard-2" - proxy = "n1-standard-2" - txnode = "n1-standard-1" - attestation_service = "n1-standard-1" - backup_node = "n1-standard-1" - } -} - -variable geth_exporter_docker_image_repository { - type = string - description = "Repository of the geth exporter docker image" -} - -variable geth_exporter_docker_image_tag { - type = string - description = "Tag of the geth exporter docker image" -} - -variable geth_node_docker_image_repository { - type = string - description = "Repository of the geth docker image" -} - -variable geth_node_docker_image_tag { - type = string - description = "Tag of the geth docker image" -} - -variable geth_verbosity { - type = number - description = "Verbosity of all geth nodes" -} - -variable in_memory_discovery_table { - type = bool - description = "Specifies whether to use an in memory discovery table" -} - -variable istanbul_request_timeout_ms { - type = number - description = "The number of ms for the istanbul request timeout" -} - -variable network_id { - type = number - description = "The network ID number" -} - -variable network_name { - type = string - description = "The name of the network to use" -} - -variable tx_node_count { - type = number - description = "Number of tx-nodes to create" -} - -variable backup_node_count { - type = number - description = "Number of backup_nodes to create" -} - -variable validator_count { - type = number - description = "Number of validators to create" -} - -# New vars -variable gcloud_region { - type = string - description = "Name of the Google Cloud region to use" -} - -variable gcloud_zone { - type = string - description = "Name of the Google Cloud zone to use" -} - -variable validator_signer_account_addresses { - type = list(string) - description = "Array with the Validator etherbase account addresses" -} - -variable validator_signer_private_keys { - type = list(string) - description = "Array with the Validator etherbase account private keys" -} - -variable validator_signer_account_passwords { - type = list(string) - description = "Array with the Validator etherbase account passwords" -} - -variable validator_release_gold_addresses { - type = list(string) - description = "Array with the Validator release gold address(es)" -} - -variable proxy_enodes { - type = list(string) - description = "Array list with the proxy enode address (without enode://)" -} - -variable proxy_private_keys { - type = list(string) - description = "Array with the Proxy private keys" -} - -variable proxy_account_passwords { - type = list(string) - description = "Array with the proxy etherbase account passwords" -} - -variable reset_geth_data { - type = bool - description = "Specifies if the existing chain data should be removed while creating the instance" -} - -# Attestation service vars -variable attestation_service_count { - type = number - description = "Number of Attestation Service to deploy" -} - -variable attestation_service_db_username { - type = string - description = "The User for the database" - default = "celo" -} - -variable attestation_service_db_password { - type = string - description = "The password for the database" - default = "secret" -} - -variable attestation_service_docker_image_repository { - type = string - description = "The docker image repository for the attestation service" - default = "" -} - -variable attestation_service_docker_image_tag { - type = string - description = "The docker image tag for the attestation service" - default = "" -} - -variable attestation_signer_addresses { - type = list(string) - description = "The account address for signing the attestations. Must be the address of the associated validator" - default = [""] -} - -variable attestation_signer_private_keys { - type = list(string) - description = "The account private key for signing the attestations. Must be the private key of the associated validator" - default = [""] -} - -variable attestation_signer_account_passwords { - type = list(string) - description = "Array with the attestation_signer account passwords" -} - - -variable attestation_service_celo_provider { - type = string - description = "The URL for the RPC interface for the Celo network" - default = "" -} - -variable attestation_service_sms_providers { - type = string - description = "The SMS Service provider. Must be nexmo or twilio" - default = "" -} - -variable attestation_service_nexmo_key { - type = string - description = "Nexmo api key (check nexmo documentation)" - default = "" -} - -variable attestation_service_nexmo_secret { - type = string - description = "Nexmo api secret (check nexmo documentation)" - default = "" -} - -variable attestation_service_nexmo_blacklist { - type = string - description = "Nexmo blacklisted country codes, separated by comma (check nexmo documentation)" - default = "" -} - -variable attestation_service_nexmo_unsupported_regions { - type = string - description = "Nexmo unsupported country codes, separated by comma (check nexmo documentation)" - default = "" -} - -variable attestation_service_twilio_account_sid { - type = string - description = "Twilio account SID (check twilio documentation)" - default = "" -} - -variable attestation_service_twilio_messaging_service_sid { - type = string - description = "Twilio account messaging service SID (check twilio documentation)" - default = "" -} - -variable attestation_service_twilio_verify_service_sid { - type = string - description = "Twilio account verify service SID (check twilio documentation)" - default = "" -} - -variable attestation_service_twilio_auth_token { - type = string - description = "Twilio account Auth Token (check twilio documentation)" - default = "" -} - -variable attestation_service_twilio_blacklist { - type = string - description = "Twilio blacklisted country codes, separated by comma (check twilio documentation)" - default = "" -} - -variable attestation_service_twilio_unsupported_regions { - type = string - description = "Twilio unsupported country codes, separated by comma (check twilio documentation)" - default = "" -} - -variable attestation_service_messagebird_api_key { - type = string - description = "Messagebird API key" - default = "" -} - -variable attestation_service_messagebird_unsupported_regions { - type = string - description = "Messagebird unsupported country codes, separated by comma (check Messagebird documentation)" - default = "" -} - -variable validator_name { - type = string - description = "The validator Name for ethstats" -} - -variable proxy_name { - type = string - description = "The proxy Name for ethstats" -} - -variable proxy_addresses { - type = list(string) - description = "The proxy address for ethstats" -} - -variable "stackdriver_logging_exclusions" { - description = "List of objects that define logs to exclude on stackdriver" - type = map(object({ - description = string - filter = string - })) -} - -variable "stackdriver_logging_metrics" { - description = "List of objects that define COUNT (DELTA) logging metric filters to apply to Stackdriver to graph and alert on useful signals" - type = map(object({ - description = string - filter = string - })) -} - -variable "service_account_scopes" { - description = "Scopes to apply to the service account which all nodes in the cluster will inherit" - type = list(string) -} \ No newline at end of file diff --git a/packages/terraform-modules-public/gcp/example/README.md b/packages/terraform-modules-public/gcp/example/README.md deleted file mode 100644 index ec311f55364..00000000000 --- a/packages/terraform-modules-public/gcp/example/README.md +++ /dev/null @@ -1,485 +0,0 @@ -# HOWTO - -1. Create and checkout new branch (optional) - - ```console - git checkout -b $new_branch - ``` - - -2. Update gcloud.env - - If starting from scratch, - - ```console - cp gcloud.env.example gcloud.env - ``` - - or just run - - ```console - ./bootstrap.sh - ``` - - which will create gcloud.env for you. - - Now set the project name. If this is a key rotation rather than a fresh install, also comment out the last line, which sets the service account name, e.g. - - `#export TF_VAR_GCP_DEFAULT_SERVICE_ACCOUNT="151785056447-compute@developer.gserviceaccount.com"` - - This account will be created when the project is created, and will be appended to gcloud.env by bootstrap.sh - - -3. Source gcloud.env - - ```console - source gcloud.env - ``` - - So that bootstrap.sh has the env vars it needs to properly provision the project. - - -4. Run bootstrap.sh - - ```console - ./bootstrap.sh - ``` - - This will take awhile, as it enables and configures a series of API's within GCP. - -5. Source gcloud.env again - - This is necessary because bootstrap.sh creates a service account that Terraform needs to know about - - ```console - source gcloud.env - ``` - - ** If storing terraform (tfstate) on GCS, now is a good time to browse to that bucket and remove permissions to the tfstate bucket for 'viewers' and 'editors' of the proejct. This will prevent an attacker who gets code execution on the proxy or validator or txnode or attestation service node from pulling the tfstate from gcs, which is important because the tfstate contains sensitive data such as signing keys ** - - -6. Update terraform.tfvars with the following critical and environment sensitive values: - - ``` - google = { - project = "celo-rc1" - region = "asia-southeast1" - zone = "asia-southeast1-c" - } - - validator_name = "Acme-RC1-Validator" - - proxy_name = "Acme-RC1-Proxy" - - validator_signer_accounts = { - account_addresses = [ - "0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", - ] - private_keys = [ - "cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", - ] - #define your own strong password here - account_passwords = [ - "Iez5lodohzaShap7ohH6ro5ohm9aecaezied4Esii3xeeBo1uxooP6aeluithu0u", - ] - release_gold_addresses = [ - "0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - ] - } - - proxy_accounts = { - - account_addresses = [ - "0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", - ] - private_keys = [ - "10xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", - ] - - #note that complete enode is not revealed from celocli account:new. FIXME - enodes = [ - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - ] - #define your own strong password here - account_passwords = [ - "oi0ahsas8ahghaaxeenoh0fo7ar2EoFa2aloj2chaveelu6Veegh4ahNgeikaegh", - ] - } - - attestation_signer_accounts = { - account_addresses = [ - "0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxf7", - ] - private_keys = [ - "46xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx58", - ] - #define your own strong password here - account_passwords = [ - "el5Lai2ohvex4ohv1ree9Noo2iethoolae6be0aijeishaemiexohtae3meika2u" - ] - } - - attestation_service_db = { - username = "celo" - #define your own strong password here - password = "Yeu4Chaotoh0eiG4xij2oob5phaekaeGeexel5thoo0xahsha2meihahLohk9wai" - } - - attestation_service_credentials = { - sms_providers = "twilio" - nexmo_key = "" - nexmo_secret = "" - nexmo_blacklist = "" - twilio_account_sid = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - twilio_messaging_service_sid = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - twilio_verify_service_sid = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - twilio_auth_token = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - twilio_blacklist = "" - } - - public_www_fqdn = "www.mysite.org" - ``` - - Note re: proxy enodes: these are critical and tell the validator which proxy to connect to. - - They aren't properly exposed yet by Celocli. - - Until this is resolved you'll need to pull this from the running proxy by running - - ```console - docker exec geth geth --exec "admin.nodeInfo['enode'].split('//')[1].split('@')[0]" attach | tr -d '"' - ``` - - and then updating the enodes value in terraform.tfvars - - We recommend using a unique proxy account address for each new validator. - - For key rotations we do not usually rotate attestation signers. - - The metadata is signed by the group vote signer, so there is no need to update metadata when rotating a validator signer. - -7. Update variables.tf - - Set the quantity of validators as desired. Note that a proxy is created automatically for each validator. This is also where you can adjust instance types to taste. - - -8. Run terraform plan - - ```console - terraform plan - ``` - - This will reveal ~50 some resources to create. - -9. Run terraform apply - - ```console - terraform apply -auto-approve - ``` - - This will create the resources. - -10. Run terraform apply again - - This is necessary because Terraform will error out trying to apply IAM policies to GCS buckets that are created but are waiting for 'eventual consistency' :) - - ```console - terraform apply -auto-approve - ``` - -11. Upload chaindata archive - - In order for this new project to quickly bootstrap nodes, it needs chain data. A GCS bucket has been created for this purpose, but it's empty. There are two ways to deal with this - - 1. Wait for the P2P network to sync, and then run /root/backup.sh or /root/backup_rsync.sh from the tx-node, once the p2p sync is completed. OR - - 2. Upload a tarball of chaindata to the GCS bucket: - - ```console - gsutil cp chaindata.tgz gs://${TF_VAR_project}-chaindata - ``` - - Chaindata inside the tarball (should) include the chaindata directory and all the containing DB files. - - -12. Redeploy nodes quickly - - Now that chaindata exists in the GCS bucket, you can run - - ```console - terraform destroy - ``` - - to destroy existing infrastructure and then run - - ```console - terraform apply -auto-approve - ``` - - to deploy new infrastructure which will be synced much more quickly. - - Note that `terraform destroy` won't destroy the chaindata in the GCS bucket, so can be run safely. Alternatively you can just set node count to 0 in variables.tf and `terraform apply`. - -13. Configure Dashboards - - Even with the Monitoring API enabled, for some reason you still need to go to the [GCP Monitoring Console](https://console.cloud.google.com/monitoring/dashboards) prior to uploading the Celo dashboard configuration. You'll see a modal that says "Finishing Workspace creation". Once this is done and then you can run - - ```console - cd dashboards && gcloud monitoring dashboards create --config-from-file=hud.json - ``` - - Voila, magically a Celo specific dashboard is created. The Celo dashboard is called "HUD", for Heads Up Display. - -14. Generate a proof of possession for the new validator signer - - Familiarize yourself with the [Celo Validator Signer Key Rotation](https://docs.celo.org/validator-guide/summary/key-rotation) docs before continuing. - - The validator signer key needs to be unlocked to complete this next step. You can either do this on the freshly deployed validator instance, or you can do it on your accounts node. Your choice. There are pros and cons to each. Specifically, you shouldn't be SSH'ing into production validators, but similarly the validator signer key shouldn't really be hanging around on the accounts node either. - - Edit [../scripts/generate_pop.sh]. Specifically you need to update the following variables: - - ```bash - SIGNER_TO_AUTHORIZE= - VALIDATOR_ACCOUNT_ADDRESS= - ``` - - Now run the following command, again on a node that has the validator signer key available: - - ```console - ./generate_pop.sh - ``` - - The generate_pop.sh script executes the following commands, which generate a proof of possession and a BLS proof of possession of the validator signer key. - - ```console - docker run -v $PWD:/root/.celo --rm -it $CELO_IMAGE --nousb account proof-of-possession $SIGNER_TO_AUTHORIZE $VALIDATOR_ACCOUNT_ADDRESS - docker run -v $PWD:/root/.celo --rm -it $CELO_IMAGE --nousb account proof-of-possession $SIGNER_TO_AUTHORIZE $VALIDATOR_ACCOUNT_ADDRESS --bls - ``` - - Copy the output of these commands, as we'll need them for the next step, in which these signatures will be used in the next step to authorize the new signer. - -15. Authorize new signer - - Do *not* do this until the new validator and proxy are alive and synced. - Once this step is completed, the new signer will take over for the old one at the beginning of the next epoch. - - Edit [../scripts/authorize_signer.sh]. Specifically you need to update the following variables: - - ```bash - CELO_VALIDATOR_RG_ADDRESS - SIGNER_TO_AUTHORIZE - ``` - - Update the next three variables using output from the previous step: - - ```bash - SIGNER_PROOF_OF_POSSESSION - BLS_PUBLIC_KEY - BLS_PROOF_OF_POSSESSION - ``` - - Note that the `SIGNER_PROOF_OF_POSSESSION` and the `BLS_PROOF_OF_POSSESSION` are the signature and BLS signature outputs from the previous step. - - You may also need to update the `--ledgerCustomAddresses=[1]` parameter to match whichever Ledger slot holds your Validator RG beneficiary key. - - Once you have made 100% sure that the new validator and proxy are ready to take over, run the following command, again on a node that has the validator signer key available: - - ```console - ./authorize_signer.sh - ``` - - This will execute the following command: - - ```console - npx celocli releasegold:authorize --contract $CELO_VALIDATOR_RG_ADDRESS --role validator \ - --signer $SIGNER_TO_AUTHORIZE --signature 0x$SIGNER_PROOF_OF_POSSESSION --blsKey $BLS_PUBLIC_KEY --blsPop $BLS_PROOF_OF_POSSESSION \ - --useLedger --ledgerCustomAddresses=[1] - ``` - -16. Wait for new epoch - - Now sit back, pull up [TheCelo](http://www.thecelo.com/) and wait for the new epoch to roll around. You can track your validator on the [PRL Block Map Site](https://cauldron.pretoriaresearchlab.io/rc1-block-map), and get a visual indication of when your new signer has taken over. - -17. Troubleshooting - - If your new validator isn't signing, check the following: - - * Make sure that both the proxy and the validator are synced. You can verify this in the geth console: - ```console - docker exec -it geth geth attach - eth.syncing - ``` - * Ensure that the validator signer key is unlocked on the validator: - ```console - docker exec -it geth geth attach - personal - ``` - * Ensure that the proxy has >100 peers - ```console - docker exec -it geth geth attach - admin.peers.length - ``` - * Check that the enode variable for the proxy is set correctly in terraform.tfvars. - * Verify network connectivity from the validator to the proxy on tcp/30503 - -18. Attestation Service - - First generate a new account to use for the attestation signer. - - ```console - celocli account:new - ``` - - Use these values for the `attestation_signer_accounts` attributes: - - * account_addresses - * private_keys - * account_passwords - - Put these into terraform.tfvars. - - Now, on a system which has access to the attestation_signer private key, generate a proof of possession for that key as follows: - - ```bash - #!/bin/bash - set -x - - ###### - # use this script on an attestation signer tx-node to generate a proof of possession, needed for key rotation - - CELO_IMAGE=us.gcr.io/celo-org/geth:1.1.0 - CELO_ATTESTATION_SIGNER_ADDRESS=YOUR_ATTESTATION_SIGNER_ADDRESS - CELO_VALIDATOR_RG_ADDRESS=YOUR_VALIDATOR_RELEASE_GOLD_ADDRESS - - # On the Attestation machine - docker run -v $PWD:/root/.celo --rm -it $CELO_IMAGE account proof-of-possession $CELO_ATTESTATION_SIGNER_ADDRESS $CELO_VALIDATOR_RG_ADDRESS - ``` - - Use the generated signature to authorize a new attestation signer as follows: - - ```bash - #!/bin/bash - set -x - - ###### - # use this script to authorize a new attestation signer - # signed by the validator release gold account - - CELO_ATTESTATION_SIGNER_SIGNATURE=YOUR_SIGNATURE_FROM_PREVIOUS_STEP - CELO_ATTESTATION_SIGNER_ADDRESS=YOUR_ATTESTATION_SIGNER_ADDRESS - CELO_VALIDATOR_RG_ADDRESS=YOUR_VALIDATOR_RELEASE_GOLD_ADDRESS - - npx celocli releasegold:authorize --contract $CELO_VALIDATOR_RG_ADDRESS --role attestation --signature 0x$CELO_ATTESTATION_SIGNER_SIGNATURE --signer $CELO_ATTESTATION_SIGNER_ADDRESS --useLedger --ledgerCustomAddresses=[1] - ``` - - 19. Update DNS with public IP of attestation service - - This could be done via terraform down the track. - This DNS name must be used in for the ATTESTATION_URL parameter used in the next step. - - Terminating SSL for the attestation service is presently out of scope for this doc, but can be set up quickly and easily using GCP load balancing, Cloudflare, or nginx as a reverse proxy. - - The attestation service requires that the following routes be exposed to the Internet to function correctly: - * POST /attestations - * POST /test_attestations - * GET /get_attestations - * POST /delivery_status_twilio - * GET /delivery_status_nexmo - * GET /status - * GET /healthz - * GET /metrics - - 20. Validator metadata - - First create validator metadata as follows: - - ```console - celocli account:create-metadata ./validator_metadata.json --from $CELO_VALIDATOR_RG_ADDRESS - ``` - - Claim the validator account on the group account: - - ```celocli account:claim-account ./validator_metadata.json --address $CELO_VALIDATOR_GROUP_RG_ADDRESS --from $CELO_ATTESTATION_SIGNER_ADDRESS``` - - Now claim your attestation URL. Note this must be run on a node that has the attestation signer key unlocked: - - ```console - celocli account:claim-attestation-service-url ./validator_metadata.json --url https://YOUR_ATTESTATION_URL --from $CELO_ATTESTATION_SIGNER_ADDRESS - ``` - - Now register this url on-chain: - ```console - celocli releasegold:set-account --contract $CELO_VALIDATOR_RG_ADDRESS --property metaURL --value "https://YOUR_VALIDATOR_METADATA_URL" - ``` - - Verify that this worked as expected by running: - - ```console - celocli account:get-metadata $CELO_VALIDATOR_RG_ADDRESS - ``` - - Verify that the attestation service works by running: - - ```console - celocli identity:test-attestation-service --from $CELO_ATTESTATION_SIGNER_ADDRESS --phoneNumber "YOUR_PHONE_NUM" --message "hello world" - ``` - - 21. Group metadata - - First create the group metadata - - ```console - celocli account:create-metadata ./group_metadata.json --from $CELO_VALIDATOR_GROUP_RG_ADDRESS - ``` - - Now set the group's name on chain - - ```console - celocli releasegold:set-account --contract $CELO_VALIDATOR_GROUP_RG_ADDRESS --property name --value YourGroupName - ``` - - ```console - celocli account:claim-domain ./group_metadata.json --domain YOURDOMAIN --from $CELO_VALIDATOR_GROUP_SIGNER_ADDRESS --useLedger --ledgerCustomAddresses=[2] - ``` - - This will output your claim signed under the provided signer address. This output should then be recorded via a DNS TXT Record on your domain. - - Now test that the metadata has been created successfully: - - ```console - celocli account:show-metadata ./group_metadata.json - ``` - - Next claim the validator address from the group account: - - ```console - celocli account:claim-account ./group_metadata.json --address $CELO_VALIDATOR_RG_ADDRESS --from $CELO_VALIDATOR_GROUP_SIGNER_ADDRESS --useLedger --ledgerCustomAddresses=[2] - ``` - - Now let's submit the corresponding claim from the validator account on the group account - - ```console - celocli account:claim-account ./validator_metadata.json --address $CELO_VALIDATOR_GROUP_RG_ADDRESS --from $CELO_ATTESTATION_SIGNER_ADDRESS - ```console - - Now upload the validator_metadata.json and group_metadata.json to a publicly available location. - - Finally, test that everything is properly configured: - - ```console - celocli account:get-metadata $CELO_VALIDATOR_GROUP_RG_ADDRESS - celocli account:get-metadata $CELO_VALIDATOR_RG_ADDRESS - ``` - -22. Verify validator and attestation performance - You can see how well your validator group is performing visually by looking at the [Mainnet Block Map](https://cauldron.pretoriaresearchlab.io/block-map) from [Pretoria Research Lab](https://cauldron.pretoriaresearchlab.io/). - - Pretoria has also created an [Attestation Map](https://cauldron.pretoriaresearchlab.io/attestations). - - The cLabs team also has a [firebase dashboard](https://metabase.celo-networks-dev.org/public/dashboard/b0a27650-1d62-4645-81d7-26ff7546ff0d?date_filter=past2weeks~&validator_address=0x474df04481f778b46Fc71204C72B6A8BE396F0FF) that allows you to visualize attestation performance, and also seeks to identify situations in which an attestation failed due to operator (rather than user) error. - -# Areas for improvement -* move sshd to non standard port to reduce brute force noise - - - diff --git a/packages/terraform-modules-public/gcp/example/bootstrap.sh b/packages/terraform-modules-public/gcp/example/bootstrap.sh deleted file mode 100755 index b01c26fab03..00000000000 --- a/packages/terraform-modules-public/gcp/example/bootstrap.sh +++ /dev/null @@ -1,128 +0,0 @@ -#!/bin/bash -set -x - -########## -# this will create a new project in GCP, and prepare the service account for it as well as necessary API's -# best practice is to use a separate git branch for each environment (eg blue/green) -# dependencies: gcloud cli, terraform cli - -GCLOUD_ENV_FILE="gcloud.env" - -echo "Sourcing gcloud env vars from gcloud.env." -if [ -f gcloud.env ]; then - source gcloud.env -else - cat <<'EOF' > $GCLOUD_ENV_FILE - export TF_VAR_org_id=YOUR_GCLOUD_ORG_ID - export TF_VAR_billing_account=YOUR_GCLOUD_BILLING_ACCOUNT_ID - export TF_VAR_project=YOUR_TERRAFORM_PROJECT_NAME - export TF_CREDS=~/.config/gcloud/${USER}-${TF_VAR_project}.json - export GOOGLE_APPLICATION_CREDENTIALS=${TF_CREDS} - export GOOGLE_PROJECT=${TF_VAR_project} -EOF -echo "Please set gcloud environment variables in $GCLOUD_ENV_FILE before running $0" -exit 1 -fi - -echo "Creating new gcloud project for terraform" -gcloud projects create ${TF_VAR_project} \ - --organization ${TF_VAR_org_id} \ - --set-as-default - -echo "Linking new gcloud project to billing account" -gcloud beta billing projects link ${TF_VAR_project} \ - --billing-account ${TF_VAR_billing_account} - -echo "Creating iam service account for terraform" -gcloud iam service-accounts create terraform \ - --display-name "Terraform admin account" - -echo "Creating gcloud keys on filesystem for terraform" -gcloud iam service-accounts keys create ${TF_CREDS} \ - --iam-account terraform@${TF_VAR_project}.iam.gserviceaccount.com - -echo "Granting storage.admin and logging.configWriter and project editor and monitoring.admin roles to terraform service account." -gcloud projects add-iam-policy-binding ${TF_VAR_project} \ - --member serviceAccount:terraform@${TF_VAR_project}.iam.gserviceaccount.com \ - --role roles/storage.admin -gcloud projects add-iam-policy-binding ${TF_VAR_project} \ - --member serviceAccount:terraform@${TF_VAR_project}.iam.gserviceaccount.com \ - --role roles/logging.configWriter -gcloud projects add-iam-policy-binding ${TF_VAR_project} \ - --member serviceAccount:terraform@${TF_VAR_project}.iam.gserviceaccount.com \ - --role roles/editor - gcloud projects add-iam-policy-binding ${TF_VAR_project} \ - --member serviceAccount:terraform@${TF_VAR_project}.iam.gserviceaccount.com \ - --role roles/monitoring.admin - -echo "Enabling required gcp API's for terraform" -gcloud services enable cloudresourcemanager.googleapis.com -gcloud services enable cloudbilling.googleapis.com -gcloud services enable iam.googleapis.com -gcloud services enable compute.googleapis.com -gcloud services enable serviceusage.googleapis.com -gcloud services enable stackdriver.googleapis.com -gcloud services enable clouderrorreporting.googleapis.com -gcloud services enable iap.googleapis.com #required for ssh into validator w/o public IP - -echo "Enumerating default service account email address" -GCP_DEFAULT_SERVICE_ACCOUNT=`gcloud iam service-accounts list | grep 'Compute Engine default service account' | cut -d ' ' -f 7` -echo "export TF_VAR_GCP_DEFAULT_SERVICE_ACCOUNT=\"$GCP_DEFAULT_SERVICE_ACCOUNT\"" >> gcloud.env -#plan is to use this from within TF to grant explicit access to a logs bucket rather than use a broad storage.rw scope - -echo "Creating a bucket for storing remote TFSTATE" -#note namespace on gcp cloud storage buckets is global, so this must be unique -TF_STATE_BUCKET=${TF_VAR_project}-tfstate -gsutil mb -p ${TF_VAR_project} gs://${TF_STATE_BUCKET} -#gsutil iam ch serviceAccount:terraform@${TF_VAR_project}.iam.gserviceaccount.com:objectCreator,objectViewer gs://${TF_STATE_BUCKET} -#above is redundant, given that tf svc acct has storage.admin role, but granting it explictly here anyway. -# this works, but results in 'no change'. default svc account can still hit the TF_STATE_BUCKET -#gsutil iam ch -d serviceAccount:${TF_VAR_GCP_DEFAULT_SERVICE_ACCOUNT} gs://${TF_STATE_BUCKET} -cat > iam.txt << EOF -{ - "bindings": [ - { - "members": [ - "projectOwner:${TF_VAR_project}" - ], - "role": "roles/storage.legacyBucketOwner" - }, - { - "members": [ - "projectViewer:${TF_VAR_project}" - ], - "role": "roles/storage.legacyBucketReader" - }, - { - "members": [ - "serviceAccount:terraform@${TF_VAR_project}.iam.gserviceaccount.com" - ], - "role": "roles/storage.objectCreator" - }, - { - "members": [ - "serviceAccount:terraform@${TF_VAR_project}.iam.gserviceaccount.com" - ], - "role": "roles/storage.objectViewer" - } - ] -} -EOF - - -cat > backend.tf << EOF -terraform { - backend "gcs" { - bucket = "${TF_STATE_BUCKET}" - prefix = "terraform/state" - } -} -EOF - -echo "Initializing terraform" -terraform init - -echo "Don't forget to 'source gcloud.env' before using Terraform!" -echo "A dynamically named service account was created that Terraform needs to know about" - - diff --git a/packages/terraform-modules-public/gcp/example/dashboards/hud.json b/packages/terraform-modules-public/gcp/example/dashboards/hud.json deleted file mode 100644 index 60195ccb11a..00000000000 --- a/packages/terraform-modules-public/gcp/example/dashboards/hud.json +++ /dev/null @@ -1,327 +0,0 @@ -{ - "displayName": "HUD", - "gridLayout": { - "columns": "2", - "widgets": [ - { - "title": "Blocks Signed", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_DELTA" - }, - "filter": "metric.type=\"logging.googleapis.com/user/tf_eth_block_signed\" resource.type=\"gce_instance\" metadata.user_labels.\"rc1-validator\"=\"\"", - "secondaryAggregation": {} - }, - "unitOverride": "1" - } - } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" - } - } - }, - { - "title": "Blocks Mined", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_DELTA" - }, - "filter": "metric.type=\"logging.googleapis.com/user/tf_eth_block_signed\" resource.type=\"gce_instance\"", - "secondaryAggregation": {} - }, - "unitOverride": "1" - } - } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" - } - } - }, - { - "title": "Memory Free", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_MEAN" - }, - "filter": "metric.type=\"agent.googleapis.com/memory/percent_used\" resource.type=\"gce_instance\" metric.label.\"state\"=\"free\"", - "secondaryAggregation": {} - }, - "unitOverride": "%" - } - } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" - } - } - }, - { - "title": "CPU", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_MEAN" - }, - "filter": "metric.type=\"agent.googleapis.com/cpu/load_1m\" resource.type=\"gce_instance\"", - "secondaryAggregation": {} - }, - "unitOverride": "1" - } - } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" - } - } - }, - { - "title": "Network", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" - }, - "filter": "metric.type=\"compute.googleapis.com/instance/network/sent_bytes_count\" resource.type=\"gce_instance\"", - "secondaryAggregation": {} - }, - "unitOverride": "By" - } - }, - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" - }, - "filter": "metric.type=\"compute.googleapis.com/instance/network/received_bytes_count\" resource.type=\"gce_instance\"", - "secondaryAggregation": {} - }, - "unitOverride": "By" - } - } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" - } - } - }, - { - "title": "Blocks Ingested", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "crossSeriesReducer": "REDUCE_PERCENTILE_99", - "perSeriesAligner": "ALIGN_SUM" - }, - "filter": "metric.type=\"logging.googleapis.com/user/tf_eth_blocks_ingested\" resource.type=\"gce_instance\"", - "secondaryAggregation": {} - }, - "unitOverride": "blocks" - } - } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" - } - } - }, - { - "title": "Eth Handshakes Failed", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" - }, - "filter": "metric.type=\"logging.googleapis.com/user/tf_eth_handshake_failed\" resource.type=\"gce_instance\"", - "secondaryAggregation": {} - }, - "unitOverride": "1" - } - } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" - } - } - }, - { - "title": "Genesis Mismatches", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" - }, - "filter": "metric.type=\"logging.googleapis.com/user/tf_eth_genesis_mismatch\" resource.type=\"gce_instance\"", - "secondaryAggregation": {} - }, - "unitOverride": "1" - } - } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" - } - } - }, - { - "title": "Disk Usage", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_MEAN" - }, - "filter": "metric.type=\"agent.googleapis.com/disk/percent_used\" resource.type=\"gce_instance\" metric.label.\"state\"=\"used\" metric.label.\"device\"!=\"overlay\" metric.label.\"device\"!=\"tmpfs\" metric.label.\"device\"!=\"tmpfs\" metric.label.\"device\"!=\"udev\"", - "secondaryAggregation": {} - }, - "unitOverride": "%" - } - } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" - } - } - }, - { - "title": "Disk I/O", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" - }, - "filter": "metric.type=\"compute.googleapis.com/instance/disk/read_bytes_count\" resource.type=\"gce_instance\"", - "secondaryAggregation": {} - }, - "unitOverride": "By" - } - }, - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" - }, - "filter": "metric.type=\"compute.googleapis.com/instance/disk/write_bytes_count\" resource.type=\"gce_instance\"", - "secondaryAggregation": {} - }, - "unitOverride": "By" - } - } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" - } - } - } - ] - } -} diff --git a/packages/terraform-modules-public/gcp/example/dashboards/readme.md b/packages/terraform-modules-public/gcp/example/dashboards/readme.md deleted file mode 100644 index c010acdf97b..00000000000 --- a/packages/terraform-modules-public/gcp/example/dashboards/readme.md +++ /dev/null @@ -1,6 +0,0 @@ -# Stackdriver Monitoring Dashboard - -There presently is no support for creating Stackdriver monitoring dashboards via Terraform -So instead we have use the gcloud cli to import the dashboard from a json file - -`gcloud monitoring dashboards create --config-from-file=hud.json` diff --git a/packages/terraform-modules-public/gcp/example/gcloud.env.example b/packages/terraform-modules-public/gcp/example/gcloud.env.example deleted file mode 100644 index 2462da1480e..00000000000 --- a/packages/terraform-modules-public/gcp/example/gcloud.env.example +++ /dev/null @@ -1,8 +0,0 @@ -export TF_VAR_org_id=YOUR_ORG_ID -export TF_VAR_billing_account=YOUR_BILLING_ACCOUNT -export TF_VAR_project=NAME_OF_PROJECT_TO_BE_CREATED_BY_THIS_SCRIPT -export TF_CREDS=~/.config/gcloud/${USER}-${TF_VAR_project}.json -export GOOGLE_APPLICATION_CREDENTIALS=${TF_CREDS} -export GOOGLE_PROJECT=${TF_VAR_project} -#next line is derived by bootstrap.sh, used later by TF to grant perms to write to logging bucket -#export TF_VAR_GCP_DEFAULT_SERVICE_ACCOUNT="PROJECTID-compute@developer.gserviceaccount.com" diff --git a/packages/terraform-modules-public/gcp/example/main.tf b/packages/terraform-modules-public/gcp/example/main.tf deleted file mode 100644 index b3bce24d4a8..00000000000 --- a/packages/terraform-modules-public/gcp/example/main.tf +++ /dev/null @@ -1,256 +0,0 @@ -provider "google" { - project = var.google["project"] - region = var.google["region"] - zone = var.google["zone"] -} - -resource "google_project_service" "compute" { - project = var.google["project"] - service = "compute.googleapis.com" - disable_dependent_services = true - disable_on_destroy = false -} - -resource "google_project_service" "db" { - project = var.google["project"] - service = "sqladmin.googleapis.com" - disable_dependent_services = true - disable_on_destroy = false -} - -resource "google_compute_network" "celo_network" { - name = var.network_name - timeouts { - delete = "15m" - } -} - -data "google_compute_subnetwork" "celo_subnetwork" { - name = google_compute_network.celo_network.name - region = var.google["region"] - depends_on = [google_compute_network.celo_network] -} - -resource "google_compute_router" "router" { - name = "${var.celo_env}-celo-router" - region = data.google_compute_subnetwork.celo_subnetwork.region - network = google_compute_network.celo_network.self_link - - bgp { - asn = 64514 - } -} - -resource "google_compute_router_nat" "nat" { - name = "${var.celo_env}-celo-router-nat" - router = google_compute_router.router.name - region = google_compute_router.router.region - nat_ip_allocate_option = "AUTO_ONLY" - source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES" - - log_config { - enable = false - filter = "ERRORS_ONLY" - } -} - -module "celo_cluster" { - source = "../celo-infra" - network_depends_on = [google_compute_network.celo_network] - - gcloud_project = var.google["project"] - gcloud_region = var.google["region"] - gcloud_zone = var.google["zone"] - network_name = google_compute_network.celo_network.name - celo_env = var.celo_env - instance_types = var.instance_types - service_account_scopes = var.service_account_scopes - - stackdriver_logging_exclusions = var.stackdriver_logging_exclusions - stackdriver_logging_metrics = var.stackdriver_logging_metrics - - - tx_node_count = var.replicas["txnode"] - backup_node_count = var.replicas["backup_node"] - validator_count = var.replicas["validator"] - - validator_signer_account_addresses = var.validator_signer_accounts["account_addresses"] - validator_signer_private_keys = var.validator_signer_accounts["private_keys"] - validator_signer_account_passwords = var.validator_signer_accounts["account_passwords"] - validator_release_gold_addresses = var.validator_signer_accounts["release_gold_addresses"] - - proxy_private_keys = var.proxy_accounts["private_keys"] - proxy_addresses = var.proxy_accounts["account_addresses"] - proxy_enodes = var.proxy_accounts["enodes"] - proxy_account_passwords = var.proxy_accounts["account_passwords"] - - validator_name = var.validator_name - proxy_name = var.proxy_name - - reset_geth_data = var.reset_geth_data - - ethstats_host = var.ethstats_host - in_memory_discovery_table = var.in_memory_discovery_table - geth_node_docker_image_repository = var.geth_node_docker_image["repository"] - geth_node_docker_image_tag = var.geth_node_docker_image["tag"] - network_id = var.network_id - block_time = var.block_time - istanbul_request_timeout_ms = var.istanbul_request_timeout_ms - geth_verbosity = var.geth_verbosity - geth_exporter_docker_image_repository = var.geth_exporter_docker_image["repository"] - geth_exporter_docker_image_tag = var.geth_exporter_docker_image["tag"] - - attestation_service_count = var.replicas["attestation_service"] - attestation_service_db_username = var.attestation_service_db["username"] - attestation_service_db_password = var.attestation_service_db["password"] - attestation_service_docker_image_repository = var.attestation_service_docker_image["repository"] - attestation_service_docker_image_tag = var.attestation_service_docker_image["tag"] - attestation_signer_addresses = var.attestation_signer_accounts["account_addresses"] - attestation_signer_private_keys = var.attestation_signer_accounts["private_keys"] - attestation_signer_account_passwords = var.attestation_signer_accounts["account_passwords"] - attestation_service_sms_providers = var.attestation_service_credentials["sms_providers"] - attestation_service_nexmo_key = var.attestation_service_credentials["nexmo_key"] - attestation_service_nexmo_secret = var.attestation_service_credentials["nexmo_secret"] - attestation_service_nexmo_blacklist = var.attestation_service_credentials["nexmo_blacklist"] - attestation_service_nexmo_unsupported_regions = var.attestation_service_credentials["nexmo_unsupported_regions"] - attestation_service_twilio_account_sid = var.attestation_service_credentials["twilio_account_sid"] - attestation_service_twilio_messaging_service_sid = var.attestation_service_credentials["twilio_messaging_service_sid"] - attestation_service_twilio_verify_service_sid = var.attestation_service_credentials["twilio_verify_service_sid"] - attestation_service_twilio_auth_token = var.attestation_service_credentials["twilio_auth_token"] - attestation_service_twilio_blacklist = var.attestation_service_credentials["twilio_blacklist"] - attestation_service_twilio_unsupported_regions = var.attestation_service_credentials["twilio_unsupported_regions"] - attestation_service_messagebird_api_key = var.attestation_service_credentials["messagebird_api_key"] - attestation_service_messagebird_unsupported_regions = var.attestation_service_credentials["messagebird_unsupported_regions"] -} - -resource "google_logging_project_exclusion" "logging_exclusion" { - for_each = var.stackdriver_logging_exclusions - - name = each.key #maybe make this a random_id to ensure no naming conflicts - description = each.value["description"] - filter = each.value["filter"] -} - -resource "random_id" "stackdriver_logging_exclusions" { - for_each = var.stackdriver_logging_exclusions - byte_length = 4 -} - -resource "random_id" "stackdriver_logging_metrics" { - for_each = var.stackdriver_logging_metrics - byte_length = 4 -} - -resource "google_logging_metric" "logging_metric" { - for_each = var.stackdriver_logging_metrics - name = each.key - description = each.value["description"] - filter = each.value["filter"] - metric_descriptor { - metric_kind = "DELTA" - value_type = "INT64" - display_name = each.value["description"] - } -} - -resource "google_logging_metric" "distribution_blocks_ingested" { - name = "tf_eth_blocks_ingested" - description = "Ethereum blocks ingested" - filter = "resource.type=\"gce_instance\" AND \"Imported new chain segment\"" - metric_descriptor { - metric_kind = "DELTA" - value_type = "DISTRIBUTION" - unit = "blocks" - display_name = "Blocks Ingested" - } - value_extractor = "REGEXP_EXTRACT(jsonPayload.message, \"\\\"blocks\\\":(\\\\d+)\")" - bucket_options { - explicit_buckets { - bounds = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,40,60,80,100,120,140,160,180,200,400,500,600,700,800,900,1000,1200,1400,1600,1800,2000,2200,2400,2600,2800,3000,3500,4000,5000] - } - } -} - -resource "google_storage_bucket" "chaindata_bucket" { - name = "${var.google["project"]}-chaindata" - location = "US" - - lifecycle_rule { - condition { - num_newer_versions = 10 # keep 10 copies of chaindata backups (use `gsutil ls -la $bucket` to see versioned objects) - } - action { - type = "Delete" - } - } - - versioning { - enabled = true - } -} - -resource "google_storage_bucket_iam_binding" "chaindata_binding_write" { - bucket = "${var.google["project"]}-chaindata" - role = "roles/storage.objectCreator" - members = [ - "serviceAccount:${var.GCP_DEFAULT_SERVICE_ACCOUNT}", - ] -} - -resource "google_storage_bucket_iam_binding" "chaindata_binding_read" { - bucket = "${var.google["project"]}-chaindata" - role = "roles/storage.objectViewer" - members = [ - "serviceAccount:${var.GCP_DEFAULT_SERVICE_ACCOUNT}", - ] -} - -resource "google_storage_bucket" "chaindata_rsync_bucket" { - name = "${var.google["project"]}-chaindata-rsync" - location = "US" - -} - -resource "google_storage_bucket_iam_binding" "chaindata_rsync_binding_write" { - bucket = "${var.google["project"]}-chaindata-rsync" - role = "roles/storage.objectCreator" - members = [ - "serviceAccount:${var.GCP_DEFAULT_SERVICE_ACCOUNT}", - ] -} - -resource "google_storage_bucket_iam_binding" "chaindata_rsync_binding_read" { - bucket = "${var.google["project"]}-chaindata-rsync" - role = "roles/storage.objectViewer" - members = [ - "serviceAccount:${var.GCP_DEFAULT_SERVICE_ACCOUNT}", - ] -} - -# validators need to expose metadata publicly -# uncomment the following two blocks if you would like to use GCS for this purpose - -#resource "google_storage_bucket" "public_www_bucket" { -# name = var.public_www_fqdn -# location = "US" -# force_destroy = true -# -# website { -# main_page_suffix = "index.html" -# not_found_page = "404.html" -# } -# cors { -# origin = ["https://${var.public_www_fqdn}"] -# method = ["GET", "HEAD"] -# response_header = ["*"] -# max_age_seconds = 3600 -# } -#} - -#resource "google_storage_bucket_iam_binding" "public_www_binding_read" { -# bucket = var.public_www_fqdn -# role = "roles/storage.objectViewer" -# members = [ -# "allUsers" -# ] -#} \ No newline at end of file diff --git a/packages/terraform-modules-public/gcp/example/metrics.md b/packages/terraform-modules-public/gcp/example/metrics.md deleted file mode 100644 index c159709eb22..00000000000 --- a/packages/terraform-modules-public/gcp/example/metrics.md +++ /dev/null @@ -1,49 +0,0 @@ -# metrics - -## geth exporter -The geth exporter is no longer needed, since Geth now includes the ability to export metrics natively. -Previously, the proxy, validator and tx-node services included the geth-exporter service to export geth metrics for Prometheus. Serving at port 9200, you could configure your Prometheus server to collect the metrics at endpoint http://:9200/metrics - -This has been deprecated and removed. - -## geth metrics -geth is now invoked started with --metrics and --pprof -which exposes metrics on http://localhost:6060/debug/metrics - -## prometheus -[prometheus](https://prometheus.io/) style metrics are now exposed and can be accessed at -http://localhost:6060/debug/metrics/prometheus -prometheus data can be scraped by prometheus using a static config target specified in -prometheus.yml, as follows: - -``` -global: - scrape_interval: 15s # By default, scrape targets every 15 seconds. - - # Attach these labels to any time series or alerts when communicating with - # external systems (federation, remote storage, Alertmanager). - external_labels: - monitor: 'codelab-monitor' - -# A scrape configuration containing exactly one endpoint to scrape: -# Here it's Prometheus itself. -scrape_configs: - # The job name is added as a label `job=` to any timeseries scraped from this config. - - job_name: 'prometheus' - - # Override the global default and scrape targets from this job every 5 seconds. - scrape_interval: 5s - - static_configs: - - targets: ['localhost:9090'] -``` - - -## visualization -grafana is a good choice for graphing -`docker run --rm -it --net=host grafana/grafana` - -## further reading -See https://blog.ethereum.org/2019/07/10/geth-v1-9-0/#metrics-collection for more information - - diff --git a/packages/terraform-modules-public/gcp/example/outputs.tf b/packages/terraform-modules-public/gcp/example/outputs.tf deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/packages/terraform-modules-public/gcp/example/scripts/authorize_attestation_signer.sh b/packages/terraform-modules-public/gcp/example/scripts/authorize_attestation_signer.sh deleted file mode 100644 index 93c95ed7192..00000000000 --- a/packages/terraform-modules-public/gcp/example/scripts/authorize_attestation_signer.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -set -x - -###### -# use this script to authorize a new attestation signer -# signed by the validator release gold account - -CELO_ATTESTATION_SIGNER_SIGNATURE=FIXME -CELO_ATTESTATION_SIGNER_ADDRESS=FIXME -CELO_VALIDATOR_RG_ADDRESS=FIXME -LEDGER_INDEX=0 - -npx celocli releasegold:authorize --contract $CELO_VALIDATOR_RG_ADDRESS --role attestation --signature 0x$CELO_ATTESTATION_SIGNER_SIGNATURE --signer $CELO_ATTESTATION_SIGNER_ADDRESS --useLedger --ledgerCustomAddresses=$LEDGER_INDEX - - diff --git a/packages/terraform-modules-public/gcp/example/scripts/authorize_signer.sh b/packages/terraform-modules-public/gcp/example/scripts/authorize_signer.sh deleted file mode 100755 index afe026772a7..00000000000 --- a/packages/terraform-modules-public/gcp/example/scripts/authorize_signer.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -set -x - -# authorize a new signer - -CELO_VALIDATOR_RG_ADDRESS=FIXME -SIGNER_TO_AUTHORIZE=FIXME -SIGNER_PROOF_OF_POSSESSION=FIXME -BLS_PUBLIC_KEY=FIXME -BLS_PROOF_OF_POSSESSION=FIXME -LEDGER_INDEX=0 - -# DO NOT RUN THE FOLLOWING UNTIL THE NEW SIGNER IS READY TO ROCK AND ROLL. -# NEW SIGNER WILL TAKE OVER FOR OLD SIGNER AT BEGINNING OF NEW EPOCH - -# From a node with access to the beneficiary key of VALIDATOR_ACCOUNT_ADDRESS -npx celocli releasegold:authorize --contract $CELO_VALIDATOR_RG_ADDRESS --role validator \ - --signer $SIGNER_TO_AUTHORIZE --signature 0x$SIGNER_PROOF_OF_POSSESSION --blsKey $BLS_PUBLIC_KEY --blsPop $BLS_PROOF_OF_POSSESSION \ - --useLedger --ledgerCustomAddresses=$LEDGER_INDEX - diff --git a/packages/terraform-modules-public/gcp/example/scripts/cUSD_mgt/activate.sh b/packages/terraform-modules-public/gcp/example/scripts/cUSD_mgt/activate.sh deleted file mode 100644 index f9865a40d0e..00000000000 --- a/packages/terraform-modules-public/gcp/example/scripts/cUSD_mgt/activate.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -set -x - -COLD_ADDRESS=FIXME # --ledgerCustomAddresses=[5] -GROUP=FIXME -LEDGER_CMD='--useLedger --ledgerCustomAddresses=[5]' - -npx celocli election:activate --from $COLD_ADDRESS $LEDGER_CMD - - - diff --git a/packages/terraform-modules-public/gcp/example/scripts/cUSD_mgt/cold_to_hot.sh b/packages/terraform-modules-public/gcp/example/scripts/cUSD_mgt/cold_to_hot.sh deleted file mode 100755 index c755cc8023d..00000000000 --- a/packages/terraform-modules-public/gcp/example/scripts/cUSD_mgt/cold_to_hot.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash -set -x - -#echo `which celocli` -#exit - - -CELOCLI='/Users/dc/.nvm/versions/node/v10.22.0/bin/celocli' -LEDGER_CMD='--useLedger --ledgerCustomAddresses=[5]' - -#$CELOCLI -v -#exit - -HOT_ADDRESS=FIXME -COLD_ADDRESS=FIXME # --ledgerCustomAddresses=[5] -#CUSD_RESERVE=1*10^18 -#CUSD_RESERVE=6738*10^18 -CUSD_RESERVE=0 -#tax reserve as of 8/31/2020 - -echo "Checking cUSD balance of COLD_ADDRESS" -COLD_ADDRESS_CUSD_BALANCE=$($CELOCLI account:balance $COLD_ADDRESS | grep "cUSD" | cut -d " " -f 2) -echo "COLD_ADDRESS cUSD Balance: $COLD_ADDRESS_CUSD_BALANCE" -COLD_ADDRESS_CUSD_BALANCE_SIMPLE_NOTATION=$(echo $COLD_ADDRESS_CUSD_BALANCE | sed -E 's/([+-]?[0-9.]+)[eE]\+?(-?)([0-9]+)/(\1*10^\2\3)/g') -echo "Cold_ADDRESS cUSD Balance (simple): $COLD_ADDRESS_CUSD_BALANCE_SIMPLE_NOTATION" -if (( $(echo "$COLD_ADDRESS_CUSD_BALANCE_SIMPLE_NOTATION > $CUSD_RESERVE" |bc -l) )); - then - echo "COLD_ADDRESS has > $CUSD_RESERVE cUSD" - CUSD_TO_SEND=$(echo "$COLD_ADDRESS_CUSD_BALANCE_SIMPLE_NOTATION - $CUSD_RESERVE" | bc) - echo "Sending $CUSD_TO_SEND to HOT_ADDRESS at $HOT_ADDRESS" - $CELOCLI transfer:dollars --from $COLD_ADDRESS --to $HOT_ADDRESS --value $CUSD_TO_SEND $LEDGER_CMD -else - echo "COLD_ADDRESS has < $CUSD_RESERVE cUSD, exiting" - exit 0 -fi diff --git a/packages/terraform-modules-public/gcp/example/scripts/cUSD_mgt/hot_exchange.sh b/packages/terraform-modules-public/gcp/example/scripts/cUSD_mgt/hot_exchange.sh deleted file mode 100755 index 0f4bd6a5a01..00000000000 --- a/packages/terraform-modules-public/gcp/example/scripts/cUSD_mgt/hot_exchange.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash -set -x - -HOT_ADDRESS=FIXME -#TARGET_CELO=25e18 -CELOCLI='/Users/dc/.nvm/versions/node/v10.22.0/bin/celocli' -MAXSLEEP=900 -EXCHANGERATE=6e18 -VALUE=25e18 - - -while : -do - echo "Checking cUSD balance of HOT_ADDRESS" - HOT_ADDRESS_USD_BALANCE=$($CELOCLI account:balance $HOT_ADDRESS | grep "cUSD" | cut -d " " -f 2) - echo "HOT_ADDRESS USD Balance: $HOT_ADDRESS_USD_BALANCE" - HOT_ADDRESS_USD_BALANCE_SIMPLE_NOTATION=$(echo $HOT_ADDRESS_USD_BALANCE | sed -E 's/([+-]?[0-9.]+)[eE]\+?(-?)([0-9]+)/(\1*10^\2\3)/g') - echo "HOT_ADDRESS USD Balance (simple): $HOT_ADDRESS_USD_BALANCE_SIMPLE_NOTATION" - if (( $(echo "$HOT_ADDRESS_USD_BALANCE_SIMPLE_NOTATION > 0" |bc -l) )); - then - echo "Exchanging cUSD for CELO" - $CELOCLI exchange:dollars --value $VALUE --from $HOT_ADDRESS --forAtLeast $EXCHANGERATE - if [ $? -eq 1 ] - then - echo "Exchange failed, please check exchange rate." - exit 1 - fi - sleep $((1 + RANDOM % $MAXSLEEP)) - else - echo "No cUSD balance on hot account, exiting" - exit 0 - fi -done diff --git a/packages/terraform-modules-public/gcp/example/scripts/cUSD_mgt/hot_to_cold.sh b/packages/terraform-modules-public/gcp/example/scripts/cUSD_mgt/hot_to_cold.sh deleted file mode 100755 index d046a6f7952..00000000000 --- a/packages/terraform-modules-public/gcp/example/scripts/cUSD_mgt/hot_to_cold.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -set -x - -HOT_ADDRESS=FIXME -COLD_ADDRESS=FIXME # --ledgerCustomAddresses=[5] -CELO_RESERVE=1*10^18 # keep some CELO handy for gas - -echo "Checking CELO balance of HOT_ADDRESS" -HOT_ADDRESS_CELO_BALANCE=$(npx celocli account:balance $HOT_ADDRESS | grep "CELO" | grep -v 'lockedCELO'| cut -d " " -f 2) -echo "HOT_ADDRESS CELO Balance: $HOT_ADDRESS_CELO_BALANCE" -HOT_ADDRESS_CELO_BALANCE_SIMPLE_NOTATION=$(echo $HOT_ADDRESS_CELO_BALANCE | sed -E 's/([+-]?[0-9.]+)[eE]\+?(-?)([0-9]+)/(\1*10^\2\3)/g') -echo "HOT_ADDRESS CELO Balance (simple): $HOT_ADDRESS_CELO_BALANCE_SIMPLE_NOTATION" -if (( $(echo "$HOT_ADDRESS_CELO_BALANCE_SIMPLE_NOTATION > $CELO_RESERVE" |bc -l) )); - then - echo "HOT_ADDRESS has > $CELO_RESERVE CELO" - CELO_TO_SEND=$(echo "$HOT_ADDRESS_CELO_BALANCE_SIMPLE_NOTATION - $CELO_RESERVE" | bc) - echo "Sending $CELO_TO_SEND to COLD_ADDRESS at $COLD_ADDRESS" - npx celocli transfer:celo --from $HOT_ADDRESS --to $COLD_ADDRESS --value $CELO_TO_SEND -else - echo "HOT_ADDRESS has < $CELO_RESERVE CELO, exiting" - exit 0 -fi \ No newline at end of file diff --git a/packages/terraform-modules-public/gcp/example/scripts/cUSD_mgt/reamde.md b/packages/terraform-modules-public/gcp/example/scripts/cUSD_mgt/reamde.md deleted file mode 100644 index 16747440fc3..00000000000 --- a/packages/terraform-modules-public/gcp/example/scripts/cUSD_mgt/reamde.md +++ /dev/null @@ -1,24 +0,0 @@ -# Celo USD Management Scripts -Collection of bash scripts to help automate the management of cUSD rewards from staking operation - -## Requirements -1. `celocli` installed locally, with a secure connection (eg SSH tunnel) to a trusted full node - `ssh -L 8545:localhost:8545 celo-manager` - `celocli node:synced` should return 'true' - -# General Flow -1. sweep_cusd.sh - Checks balance of cUSD on validator and group accounts, sweeps them to the Ledger - -2. cold_to_hot.sh - Moves cUSD (net of reserve) to hot wallet for automated exchange w/ stability protocol. - -3. hot_exchange.sh - Trades cUSD for CELO by interacting w/ the on-chain stability protocol. This should run in a (detached) screen. - Note that the hot wallet needs to be unlocked on the full node this runs on. - `personal.unlockAccount("0xE6DDd7bb03E5e8338Be22f33ee47849fB2BF66A2", "$password", 86400)` - This will take some time to run, as exchanging too much too fast will result in considerable slippage. - -4. hot_to_cold.sh - Moves CELO from the hot account back to the Ledger - \ No newline at end of file diff --git a/packages/terraform-modules-public/gcp/example/scripts/cUSD_mgt/sweep_cusd.sh b/packages/terraform-modules-public/gcp/example/scripts/cUSD_mgt/sweep_cusd.sh deleted file mode 100755 index 25aad415974..00000000000 --- a/packages/terraform-modules-public/gcp/example/scripts/cUSD_mgt/sweep_cusd.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash -set -x -CELO_VALIDATOR_GROUP_RG_ADDRESS=FIXME # --ledgerCustomAddresses=[0] -CELO_VALIDATOR_RG_ADDRESS=FIXME # --ledgerCustomAddresses=[1] -SWEEP_ADDRESS=FIXME # --ledgerCustomAddresses=[5] -CELO_RESERVE=1000000000000000000 -#CELO_RESERVE=1e18 - -echo "Checking cUSD balance of CELO_VALIDATOR_GROUP_RG_ADDRESS" -npx celocli account:balance $CELO_VALIDATOR_GROUP_RG_ADDRESS -GROUP_USD_BALANCE=$(npx celocli account:balance $CELO_VALIDATOR_GROUP_RG_ADDRESS | grep "cUSD" | cut -d " " -f 2) -echo "Group USD Balance: $GROUP_USD_BALANCE" -GROUP_USD_BALANCE_SIMPLE_NOTATION=$(echo $GROUP_USD_BALANCE | sed -E 's/([+-]?[0-9.]+)[eE]\+?(-?)([0-9]+)/(\1*10^\2\3)/g') -echo "Group USD Balance (simple): $GROUP_USD_BALANCE_SIMPLE_NOTATION" -if (( $(echo "$GROUP_USD_BALANCE_SIMPLE_NOTATION > 0" |bc -l) )); -then - echo "Transferring cUSD from CELO_VALIDATOR_GROUP_RG_ADDRESS to SWEEP_ADDRESS" - npx celocli releasegold:transfer-dollars --contract $CELO_VALIDATOR_GROUP_RG_ADDRESS --to $SWEEP_ADDRESS --value $GROUP_USD_BALANCE --useLedger --ledgerCustomAddresses=[0] -else - echo "No cUSD balance on group account, skipping" -fi - -echo "Checking cUSD balance of CELO_VALIDATOR_RG_ADDRESS" -npx celocli account:balance $CELO_VALIDATOR_RG_ADDRESS -VALIDATOR_USD_BALANCE=$(npx celocli account:balance $CELO_VALIDATOR_RG_ADDRESS | grep "cUSD" | cut -d " " -f 2) -echo "Validator USD Balance: $VALIDATOR_USD_BALANCE" -VALIDATOR_USD_BALANCE_SIMPLE_NOTATION=$(echo $VALIDATOR_USD_BALANCE | sed -E 's/([+-]?[0-9.]+)[eE]\+?(-?)([0-9]+)/(\1*10^\2\3)/g') -echo "Validator USD Balance (simple): $VALIDATOR_USD_BALANCE_SIMPLE_NOTATION" - -if (( $(echo "$VALIDATOR_USD_BALANCE_SIMPLE_NOTATION > 0" |bc -l) )); then - echo "VALIDATOR_USD_BALANCE is greater than 0" - echo "Transferring cUSD from CELO_VALIDATOR_RG_ADDRESS to SWEEP_ADDRESS" - npx celocli releasegold:transfer-dollars --contract $CELO_VALIDATOR_RG_ADDRESS --to $SWEEP_ADDRESS --value $VALIDATOR_USD_BALANCE --useLedger --ledgerCustomAddresses=[1] -else - echo "VALIDATOR_USD_BALANCE is == 0" - echo "No cUSD balance on validator account, skipping" -fi - - diff --git a/packages/terraform-modules-public/gcp/example/scripts/cUSD_mgt/vote_cold.sh b/packages/terraform-modules-public/gcp/example/scripts/cUSD_mgt/vote_cold.sh deleted file mode 100755 index eceaadf75ae..00000000000 --- a/packages/terraform-modules-public/gcp/example/scripts/cUSD_mgt/vote_cold.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash -set -x - -COLD_ADDRESS=FIXME # --ledgerCustomAddresses=[5] -CELO_RESERVE=1*10^18 -GROUP=FIXME -LEDGER_CMD='--useLedger --ledgerCustomAddresses=[5]' - -echo "Checking CELO balance of COLD_ADDRESS" -COLD_ADDRESS_CELO_BALANCE=$(npx celocli account:balance $COLD_ADDRESS | grep "CELO" | grep -v 'lockedCELO' | cut -d " " -f 2) -echo "COLD_ADDRESS CELO Balance: $COLD_ADDRESS_CELO_BALANCE" -COLD_ADDRESS_CELO_BALANCE_SIMPLE_NOTATION=$(echo $COLD_ADDRESS_CELO_BALANCE | sed -E 's/([+-]?[0-9.]+)[eE]\+?(-?)([0-9]+)/(\1*10^\2\3)/g') -echo "COLD_ADDRESS CELO Balance (simple): $COLD_ADDRESS_CELO_BALANCE_SIMPLE_NOTATION" -if (( $(echo "$COLD_ADDRESS_CELO_BALANCE_SIMPLE_NOTATION > $CELO_RESERVE" |bc -l) )); - then - echo "COLD_ADDRESS has > $CELO_RESERVE CELO, let's lock it and vote it" - CELO_TO_LOCK=$(echo "$COLD_ADDRESS_CELO_BALANCE_SIMPLE_NOTATION - $CELO_RESERVE" | bc) - echo "Locking $CELO_TO_LOCK on COLD_ADDRESS at $COLD_ADDRESS" - npx celocli lockedgold:lock --from $COLD_ADDRESS --value $CELO_TO_LOCK $LEDGER_CMD - if [[ $? -eq 1 ]]; - then - echo "Locked gold operation failed, exiting" - exit 1 - else - # note this will fail on subsequent runs of lock+vote due to celocli not exposing the locked non-voting balance. - # use explorer to calculate and vote this manually for now [FIXME] - echo "Checking locked CELO balance of COLD_ADDRESS" - COLD_ADDRESS_LOCKED_CELO_BALANCE=$(npx celocli account:balance $COLD_ADDRESS | grep "lockedCELO" | cut -d " " -f 2) - echo "COLD_ADDRESS Locked CELO Balance: $COLD_ADDRESS_LOCKED_CELO_BALANCE" - echo "Voting $COLD_ADDRESS_LOCKED_CELO_BALANCE for group $GROUP" - npx celocli election:vote --for $GROUP --from $COLD_ADDRESS --value $COLD_ADDRESS_LOCKED_CELO_BALANCE $LEDGER_CMD - echo "do not forget to activate these votes after the start of the next epoch" - echo "use the following command to activate your votes" - echo "npx celocli election:activate --from $COLD_ADDRESS $LEDGER_CMD" - fi -else - echo "COLD_ADDRESS has < $CELO_RESERVE CELO, exiting" - exit 0 -fi - - diff --git a/packages/terraform-modules-public/gcp/example/scripts/generate_attestation_pop.sh b/packages/terraform-modules-public/gcp/example/scripts/generate_attestation_pop.sh deleted file mode 100644 index 469e76171b9..00000000000 --- a/packages/terraform-modules-public/gcp/example/scripts/generate_attestation_pop.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -set -x - -###### -# use this script on an attestation signer tx-node to generate a proof of possession, needed for key rotation - -CELO_IMAGE=us.gcr.io/celo-org/celo-node:mainnet -CELO_ATTESTATION_SIGNER_ADDRESS=FIXME -CELO_VALIDATOR_RG_ADDRESS=FIXME - -# On the Attestation machine -docker run -v $PWD:/root/.celo --rm -it $CELO_IMAGE account proof-of-possession $CELO_ATTESTATION_SIGNER_ADDRESS $CELO_VALIDATOR_RG_ADDRESS diff --git a/packages/terraform-modules-public/gcp/example/scripts/generate_pop.sh b/packages/terraform-modules-public/gcp/example/scripts/generate_pop.sh deleted file mode 100644 index 6231a35589f..00000000000 --- a/packages/terraform-modules-public/gcp/example/scripts/generate_pop.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -set -x - -echo "Run this in /root/.celo on the validator" - -###### -# use this script on a validator to generate a proof of possession, needed for key rotation -CELO_IMAGE=us.gcr.io/celo-org/geth:mainnet -SIGNER_TO_AUTHORIZE=FIXME -VALIDATOR_ACCOUNT_ADDRESS=FIXME - -# With $SIGNER_TO_AUTHORIZE as the new validator signer: -# On the new validator node which contains the new $SIGNER_TO_AUTHORIZE key -docker run -v $PWD:/root/.celo --rm -it $CELO_IMAGE --nousb account proof-of-possession $SIGNER_TO_AUTHORIZE $VALIDATOR_ACCOUNT_ADDRESS -docker run -v $PWD:/root/.celo --rm -it $CELO_IMAGE --nousb account proof-of-possession $SIGNER_TO_AUTHORIZE $VALIDATOR_ACCOUNT_ADDRESS --bls diff --git a/packages/terraform-modules-public/gcp/example/scripts/metadata.sh b/packages/terraform-modules-public/gcp/example/scripts/metadata.sh deleted file mode 100644 index 8539de98a55..00000000000 --- a/packages/terraform-modules-public/gcp/example/scripts/metadata.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/bash -set -x - -DOMAIN="EXAMPLE.COM" - -#metadata process from scratch -#create validator metadata -celocli account:create-metadata ./validator_metadata.json --from $CELO_VALIDATOR_RG_ADDRESS - -# On your local machine -# requires that the $CELO_ATTESTATION_SIGNER_ADDRESS account be unlocked, and that $CELO_ATTESTATION_SERVICE_URL be defined -celocli account:claim-attestation-service-url ./validator_metadata.json --url $CELO_ATTESTATION_SERVICE_URL --from $CELO_ATTESTATION_SIGNER_ADDRESS - - -#now create group metadata -celocli account:create-metadata ./group_metadata.json --from $CELO_VALIDATOR_GROUP_RG_ADDRESS - -#set the group name -# On your local machine -celocli releasegold:set-account --contract $CELO_VALIDATOR_GROUP_RG_ADDRESS --property name --value $DOMAIN - - - -#Now we can generate a claim for the domain associated with this name -celocli account:claim-domain ./group_metadata.json --domain $DOMAIN --from $CELO_VALIDATOR_GROUP_SIGNER_ADDRESS --useLedger --ledgerCustomAddresses=[2] - -#put the TXT record this spits out into DNS - -#make sure it worked -celocli account:show-metadata ./group_metadata.json - -#First lets claim the validator address from the group account -# On your local machine -celocli account:claim-account ./group_metadata.json --address $CELO_VALIDATOR_RG_ADDRESS --from $CELO_VALIDATOR_GROUP_SIGNER_ADDRESS --useLedger --ledgerCustomAddresses=[2] - -#Now let's submit the corresponding claim from the validator account on the group account -#(note: if you followed the directions to set up the attestation service, you may have already -#registered metadata for your validator. If that is the case, skip the steps to create the validator's metadata -#and just add the account claim.) - -# Requires that validator vote signer account be unlocked -celocli account:claim-account ./validator_metadata.json --address $CELO_VALIDATOR_GROUP_RG_ADDRESS --from $CELO_VALIDATOR_VOTE_SIGNER_ADDRESS - -#push these to s3 -s3cmd put validator_metadata.json s3://$DOMAIN/metadata/ -s3cmd put group_metadata.json s3://$DOMAIN/metadata/ - - -# On your local machine -celocli releasegold:set-account --contract $CELO_VALIDATOR_GROUP_RG_ADDRESS --property metaURL --value 'https://www.$DOMAIN/metadata/group_metadata.json' --useLedger --ledgerCustomAddresses=[0] -celocli releasegold:set-account --contract $CELO_VALIDATOR_RG_ADDRESS --property metaURL --value 'https://www.$DOMAIN/metadata/validator_metadata.json' --useLedger --ledgerCustomAddresses=[1] - - -#verify everything worked -celocli account:get-metadata $CELO_VALIDATOR_GROUP_RG_ADDRESS -celocli account:get-metadata $CELO_VALIDATOR_RG_ADDRESS diff --git a/packages/terraform-modules-public/gcp/example/scripts/regen_gcp_creds.sh b/packages/terraform-modules-public/gcp/example/scripts/regen_gcp_creds.sh deleted file mode 100755 index f5e2784bf86..00000000000 --- a/packages/terraform-modules-public/gcp/example/scripts/regen_gcp_creds.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -set -x - -GCLOUD_ENV_FILE="gcloud.env" - -echo "Sourcing gcloud env vars from gcloud.env." -if [ -f gcloud.env ]; then - source gcloud.env -else -echo "Please set gcloud environment variables in $GCLOUD_ENV_FILE before running $0" -exit 1 -fi - -echo "Creating gcloud keys on filesystem for terraform" -gcloud iam service-accounts keys create ${TF_CREDS} \ - --iam-account terraform@${TF_VAR_project}.iam.gserviceaccount.com - -gcloud config set project $GOOGLE_PROJECT \ No newline at end of file diff --git a/packages/terraform-modules-public/gcp/example/scripts/validator transfer.txt b/packages/terraform-modules-public/gcp/example/scripts/validator transfer.txt deleted file mode 100644 index f68b230e630..00000000000 --- a/packages/terraform-modules-public/gcp/example/scripts/validator transfer.txt +++ /dev/null @@ -1,85 +0,0 @@ -validator transfer - -0) transfer gold from beneficiary to RG contract -test small -celocli transfer:gold - -USAGE - $ celocli transfer:gold --from $FROM_GROUP_ADDR --to $CELO_VALIDATOR_GROUP_RG_ADDRESS --value=1e18 --useLedger --ledgerCustomAddresses=[0] - -celocli transfer:gold --from $TO_GROUP_ADDR --to $CELO_VALIDATOR_GROUP_RG_ADDRESS --value=9999e18 --useLedger --ledgerCustomAddresses=[0] - - -export CELO_VALIDATOR_GROUP_RG_ADDRESS='FIXME' - -OPTIONS - --from=0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d (required) Address of the sender - --ledgerAddresses=ledgerAddresses [default: 1] If --useLedger is set, this will get the first N addresses for local signing - --ledgerConfirmAddress Set it to ask confirmation for the address of the transaction from the ledger - --ledgerCustomAddresses=ledgerCustomAddresses [default: [0]] If --useLedger is set, this will get the array of index addresses for local signing. Example --ledgerCustomAddresses "[4,99]" - --to=0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d (required) Address of the receiver - --useLedger Set it to use a ledger wallet - --value=value (required) Amount to transfer (in wei) - -EXAMPLE - gold --from 0xa0Af2E71cECc248f4a7fD606F203467B500Dd53B --to 0x5409ed021d9299bf6814279a6a1411a7e866a631 --value 10000000000000000000 - - - -1) lock gold -celocli releasegold:locked-gold --contract $CELO_VALIDATOR_GROUP_RG_ADDRESS --action lock --value 100000e17 --useLedger --ledgerCustomAddresses=[0] - -test lock 1 gold -celocli releasegold:locked-gold --contract $CELO_VALIDATOR_GROUP_RG_ADDRESS --action lock --value 1e18 --useLedger --ledgerCustomAddresses=[0] - -2) check that it's locked -# On your local machine -celocli lockedgold:show $CELO_VALIDATOR_GROUP_RG_ADDRESS - -3) show group info -# On your local machine -celocli validatorgroup:show $CELO_VALIDATOR_GROUP_RG_ADDRESS - -4) john does this -# On the Validator machine -celocli validator:affiliate $CELO_VALIDATOR_GROUP_RG_ADDRESS --from $CELO_VALIDATOR_SIGNER_ADDRESS - - - - -5) I accept the affiliation -# On your local machine -celocli validatorgroup:member --accept 0x1b4A54049a8652c4c3E62C8c6959F06280aCFC6c --from $CELO_VALIDATOR_GROUP_SIGNER_ADDRESS --useLedger --ledgerCustomAddresses=[2] -C6c is John's validator -- will need to claim this in metadata - -6) Verify affiliation worked -# On your local machine -celocli validator:show $CELO_VALIDATOR_RG_ADDRESS -celocli validatorgroup:show $CELO_VALIDATOR_GROUP_RG_ADDRESS - - -7) Vote -# On your local machine -#celocli election:vote --from $CELO_VALIDATOR_VOTE_SIGNER_ADDRESS --for $CELO_VALIDATOR_GROUP_RG_ADDRESS --value 10000e18 -celocli election:vote --from $CELO_VALIDATOR_GROUP_VOTE_SIGNER_ADDRESS --for $CELO_VALIDATOR_GROUP_RG_ADDRESS --value 10000000000000000000000 - - -8) Verify votes -# On your local machine -celocli election:show $CELO_VALIDATOR_GROUP_RG_ADDRESS --group -celocli election:show $CELO_VALIDATOR_GROUP_RG_ADDRESS --voter -celocli election:show $CELO_VALIDATOR_RG_ADDRESS --voter - -9) Activate votes -# On your local machine -# Note that this command will wait for the next epoch transition, which may be up to 24 hours in the future. -celocli election:activate --from $CELO_VALIDATOR_VOTE_SIGNER_ADDRESS --wait && celocli election:activate --from $CELO_VALIDATOR_GROUP_VOTE_SIGNER_ADDRESS --wait - -10) verify vote activation -# On your local machine -celocli election:show $CELO_VALIDATOR_GROUP_RG_ADDRESS --voter -celocli election:show $CELO_VALIDATOR_RG_ADDRESS --voter - - -11) metadata - diff --git a/packages/terraform-modules-public/gcp/example/terraform.tfvars.example b/packages/terraform-modules-public/gcp/example/terraform.tfvars.example deleted file mode 100644 index b009532fa7c..00000000000 --- a/packages/terraform-modules-public/gcp/example/terraform.tfvars.example +++ /dev/null @@ -1,82 +0,0 @@ -# Provide the next vars with a vars-file or modifying the default value -google = { - project = "celo-rc1" - region = "asia-southeast1" - zone = "asia-southeast1-c" -} - -validator_name = "Acme-RC1-Validator" - -proxy_name = "Acme-RC1-Proxy" - -validator_signer_accounts = { - account_addresses = [ - "0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", - ] - private_keys = [ - "cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", - ] - #define your own strong password here - account_passwords = [ - "Iez5lodohzaShap7ohH6ro5ohm9aecaezied4Esii3xeeBo1uxooP6aeluithu0u", - ] - release_gold_addresses = [ - "0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - ] -} - -proxy_accounts = { - - account_addresses = [ - "0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", - ] - private_keys = [ - "10xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", - ] - - #note that complete enode is not revealed from celocli account:new. FIXME - enodes = [ - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - ] - #define your own strong password here - account_passwords = [ - "oi0ahsas8ahghaaxeenoh0fo7ar2EoFa2aloj2chaveelu6Veegh4ahNgeikaegh", - ] -} - -attestation_signer_accounts = { - account_addresses = [ - "0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxf7", - ] - private_keys = [ - "46xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx58", - ] - #define your own strong password here - account_passwords = [ - "el5Lai2ohvex4ohv1ree9Noo2iethoolae6be0aijeishaemiexohtae3meika2u" - ] -} - -attestation_service_db = { - username = "celo" - #define your own strong password here - password = "Yeu4Chaotoh0eiG4xij2oob5phaekaeGeexel5thoo0xahsha2meihahLohk9wai" -} - -attestation_service_credentials = { - sms_providers = "twilio" - nexmo_key = "" - nexmo_secret = "" - nexmo_blacklist = "CU,SY,KP,IR,SD" - nexmo_unsupported_regions = "CU,SY,KP,IR,SD" - twilio_account_sid = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - twilio_messaging_service_sid = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - twilio_verify_service_sid = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - twilio_auth_token = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - twilio_blacklist = "CU,SY,KP,IR,SD" - twilio_unsupported_regions = "CU,SY,KP,IR,SD" - messagebird_api_key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - messagebird_unsupported_regions = "CU,SY,KP,IR,SD" -} - -public_www_fqdn = "www.mysite.org" \ No newline at end of file diff --git a/packages/terraform-modules-public/gcp/example/variables.tf b/packages/terraform-modules-public/gcp/example/variables.tf deleted file mode 100644 index cd51c5fd375..00000000000 --- a/packages/terraform-modules-public/gcp/example/variables.tf +++ /dev/null @@ -1,349 +0,0 @@ -# Provide the next vars with a vars-file or modifying the default value -variable google { - description = "The GCP Data" - type = map(string) - - default = { - #update these in terraform.tfvars - project = "MY_PROJECT_NAME" - region = "MY_REGION" - zone = "MY_ZONE" - } -} - -variable replicas { - description = "The replica number for each component" - type = map(number) - - default = { - validator = 1 # Each validator will create a dedicated proxy that is exposed to the Internet - txnode = 1 - backup_node = 1 - attestation_service = 1 # Attestation service requires >= 1 txnode - } -} - -variable instance_types { - description = "The instance type for each component" - type = map(string) - - default = { - validator = "n1-standard-2" #use n1-standard-2 or better for production - proxy = "n1-standard-2" #use n1-standard-2 or better for production - txnode = "n1-standard-1" - backup_node = "n1-standard-1" - attestation_service = "n1-standard-1" - } -} - -variable network_name { - description = "The name of the new VPC network created" - type = string - - default = "celo-network" -} - -variable celo_env { - description = "The celo network to connect with" - type = string - - default = "mainnet" -} - -variable network_id { - description = "The ethereum network ID" - type = number - default = 42220 -} - -variable ethstats_host { - description = "Ethstats host to report data" - type = string - default = "stats-server.celo.org" -} - -variable geth_node_docker_image { - description = "The Celo Blockchain docker image" - type = map(string) - - default = { - repository = "us.gcr.io/celo-org/geth" - tag = "1.3.2" - } -} - -variable validator_signer_accounts { - description = "The account data for the validator nodes" - type = map - - default = { - account_addresses = [ - "secret in terraform.tfvars", - ] - private_keys = [ - "secret in terraform.tfvars", - ] - account_passwords = [ - "secret in terraform.tfvars", - ] - release_gold_addresses = [ - "secret in terraform.tfvars", - ] - } -} - -variable proxy_accounts { - description = "The account data for the proxy nodes" - type = map - - default = { - - account_addresses = [ - "set in terraform.tfvars", - ] - private_keys = [ - "secret in terraform.tfvars", - ] - enodes = [ - "set in terraform.tfvars", - ] - account_passwords = [ - "set in terraform.tfvars", - ] - } -} - -variable attestation_signer_accounts { - description = "Etherbase address and private key to sign the attestations" - type = map - - default = { - account_addresses = [ - "set in terraform.tfvars", - ] - private_keys = [ - "secret in terraform.tfvars", - ] - account_passwords = [ - "secret in terraform.tfvars" - ] - } -} - -variable validator_name { - type = string - description = "The validator Name for ethstats" - default = "YourValidator" -} - -variable proxy_name { - type = string - description = "The proxy Name for ethstats" - default = "Your-Proxy" -} - -variable reset_geth_data { - type = bool - description = "Specifies if the existing chain data should be removed while creating the instance" - default = true #will restore chaindata from GCS if available -} - -variable geth_verbosity { - description = "Geth log level" - type = number - default = 3 -} - -# Attestation variables -variable attestation_service_db { - description = "Configuration for the Postgres Cloud SQL DB" - type = map(string) - - default = { - username = "celo" - password = "secret in terraform.tfvars" - } -} - -variable attestation_service_docker_image { - description = "The attestation_service docker image" - type = map(string) - - default = { - repository = "us.gcr.io/celo-testnet/celo-monorepo" - tag = "attestation-service-v1.2.0" - } -} - -# SMS provider configuration -variable attestation_service_credentials { - description = "Provider with the credentials for the SMS provider. Provider must be nexmo or twilio" - type = map(string) - - default = { - sms_providers = "twilio" - #sms_providers = "twilio,messagebird" - nexmo_key = "" - nexmo_secret = "" - nexmo_blacklist = "CU,SY,KP,IR,SD" - nexmo_unsupported_regions = "CU,SY,KP,IR,SD" - twilio_account_sid = "secret in terraform.tfvars" - twilio_messaging_service_sid = "secret in terraform.tfvars" - twilio_verify_service_sid = "secret in terraform.tfvars" - twilio_auth_token = "secret in terraform.tfvars" - twilio_blacklist = "CU,SY,KP,IR,SD,BY,TD,CZ,EG,ID,IL,CI,JP,JO,KZ,KE,KW,LB,MW,MX,MA,NP,NG,OM,PK,PS,PH,QA,RU,SA,LK,TZ,TH,TN,TR,AE,UA,VN,ZM,ZW" - twilio_unsupported_regions = "CU,SY,KP,IR,SD,BY,TD,CZ,EG,ID,IL,CI,JP,JO,KZ,KE,KW,LB,MW,MX,MA,NP,NG,OM,PK,PS,PH,QA,RU,SA,LK,TZ,TH,TN,TR,AE,UA,VN,ZM,ZW" - messagebird_api_key = "secret in terraform.tfvars" - messagebird_unsupported_regions = "CU,SY,KP,IR,SD" - } -} - -################## -# The next variables have a default value are not intended to be changed if you do not have a reason for it -variable in_memory_discovery_table { - description = "Geth parameter" - type = bool - default = false -} - -variable block_time { - description = "The ethereum network block time" - type = number - default = 5 -} - -variable istanbul_request_timeout_ms { - description = "The ethereum request timeout" - type = number - default = 10000 -} - -variable geth_exporter_docker_image { - description = "The geth exporter docker image" - type = map(string) - - default = { - repository = "us.gcr.io/celo-testnet/geth-exporter" - tag = "ed7d21bd50592709173368cd697ef73c1774a261" - } -} - -#not yet implemented. intent is to only install the stackdriver agents and inject the log exclusions if 'true' -variable "enable_stackdriver" { - description = "If set to true, enable Stackdriver for monitoring and logging" - type = bool - - default = true -} - -variable "stackdriver_logging_exclusions" { - description = "List of objects that define logs to exclude on stackdriver" - type = map(object({ - description = string - filter = string - })) - - default = { - tf_gcm_infinite = { - description = "Ignore stackdriver agent errors re: infinite values" - filter = "resource.type = gce_instance AND \"write_gcm: can not take infinite value\"" - } - - tf_gcm_swap = { - description = "Ignore stackdriver agent errors re: swap percent/value" - filter = "resource.type = gce_instance AND \"write_gcm: wg_typed_value_create_from_value_t_inline failed for swap/percent/value! Continuing\"" - } - - tf_gcm_invalid_time = { - description = "Ignore stackdriver agent errors related to timing" - filter = "resource.type = gce_instance AND \"write_gcm: Unsuccessful HTTP request 400\" AND \"The start time must be before the end time\"" - } - - tf_gcm_transmit_unique_segments = { - description = "Ignore stackdriver agent errors re: transmit_unique_segments" - filter = "resource.type = gce_instance AND \"write_gcm: wg_transmit_unique_segment\"" - } - - tf_ver_certs = { - description = "Ignore Eth peer flapping warnings caused by peers disconnecting naturally when exceeding max_peers" - filter = "resource.type = gce_instance AND \"Error sending all version certificates\"" - } - - tf_peer_conns = { - description = "Ignore Eth peer connections. Constant flux" - filter = "resource.type = gce_instance AND \"Ethereum peer connected\"" - } - } -} - -variable "stackdriver_logging_metrics" { - description = "List of objects that define COUNT (DELTA) logging metric filters to apply to Stackdriver to graph and alert on useful signals" - type = map(object({ - description = string - filter = string - })) - - default = { - - tf_eth_handshake_failed = { - description = "Ethereum peer handshake failed" - filter = "resource.type=gce_instance AND \"Ethereum handshake failed\"" - } - - tf_eth_genesis_mismatch = { - description = "Client with different genesis block attempted connection" - filter = "resource.type=gce_instance AND \"Genesis mismatch\"" - } - - tf_eth_block_ingested = { - description = "Ethereum block(s) ingested" - filter = "resource.type=gce_instance AND \"blocks\" AND \"Imported new chain segment\"" - } - - # note that this log isn't firing anymore on successfully proposing a block (on 1.1.0) FIXME - tf_eth_block_mined = { - description = "Block mined" - filter = "resource.type=gce_instance AND \"Successfully sealed new block\"" - } - - tf_eth_block_signed = { - description = "Block signed" - filter = "resource.type=gce_instance AND \"Commit new mining work\"" - } - - tf_eth_commit_old_block = { - description = "Committed seal on old block" - filter = "resource.type=gce_instance AND \"Would have sent a commit message for an old block\"" - } - - tf_validator_not_elected = { - description = "Validator failed to be elected" - filter = "resource.type=gce_instance \"Validator Election Results\" AND \"\\\"elected\\\":\\\"false\\\"\" AND NOT \"tx-node\"" - } - - } -} - - -variable "service_account_scopes" { - description = "Scopes to apply to the service account which all nodes in the cluster will inherit" - type = list(string) - - #scope reference: https://cloud.google.com/sdk/gcloud/reference/alpha/compute/instances/set-scopes#--scopes - #verify scopes: curl --silent --connect-timeout 1 -f -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/scopes - default = [ - "https://www.googleapis.com/auth/monitoring.write", - "https://www.googleapis.com/auth/logging.write", - "https://www.googleapis.com/auth/cloud-platform" #this gives r/w to all storage buckets, which is overly broad - ] -} - -variable "GCP_DEFAULT_SERVICE_ACCOUNT" { - description = "gcp default service account for project, $projectid-compute@developer.gserviceaccount.com" - type = string -} - -variable "public_www_fqdn" { - description = "fully qualified domain name for public website" - type = string -} \ No newline at end of file diff --git a/packages/terraform-modules-public/images/Makefile b/packages/terraform-modules-public/images/Makefile deleted file mode 100644 index 075435a5df3..00000000000 --- a/packages/terraform-modules-public/images/Makefile +++ /dev/null @@ -1,31 +0,0 @@ -alfajores-full: - packer build \ - -var 'sync_mode=full' \ - -var 'additional_params="--alfajores"' \ - -var 'network_name=alfajores' \ - -force node.json - -alfajores-lightest: - packer build \ - -var 'sync_mode=lightest' \ - -var 'additional_params="--alfajores"' \ - -var 'network_name=alfajores' \ - -force node.json - -mainnet-full: - packer build \ - -var 'sync_mode=full' \ - -var 'network_name=mainnet' \ - -force node.json - -mainnet-lightest: - packer build \ - -var 'sync_mode=lightest' \ - -var 'network_name=mainnet' \ - -force node.json - -alfajores: alfajores-lightest alfajores-full - -mainnet: mainnet-lightest mainnet-full - -.PHONY: alfajores alfajores-full alfajores-lightest mainnet mainnet-full mainnet-lightest \ No newline at end of file diff --git a/packages/terraform-modules-public/images/node.json b/packages/terraform-modules-public/images/node.json deleted file mode 100644 index 3350d82e6cf..00000000000 --- a/packages/terraform-modules-public/images/node.json +++ /dev/null @@ -1,104 +0,0 @@ -{ - "variables": { - "aws_access_key": "{{env `AWS_ACCESS_KEY_ID`}}", - "aws_secret_key": "{{env `AWS_SECRET_ACCESS_KEY`}}", - "azure_client_id": "{{env `AZURE_CLIENT_ID`}}", - "azure_client_secret": "{{env `AZURE_CLIENT_SECRET`}}", - "azure_tenant_id": "{{env `AZURE_TENANT_ID`}}", - "azure_subscription_id": "{{env `AZURE_SUBSCRIPTION_ID`}}", - "gcp_project_id": "{{env `GCP_PROJECT_ID`}}", - "region": "us-east-1", - "sync_mode": "full", - "network_name": "mainnet", - "additional_params": "" - }, - "builders": [ - { - "name": "aws", - "type": "amazon-ebs", - "access_key": "{{user `aws_access_key`}}", - "secret_key": "{{user `aws_secret_key`}}", - "ami_name": "celo-{{user `network_name`}}-{{user `sync_mode`}}-node-latest", - "instance_type": "t2.medium", - "region": "{{user `region`}}", - "source_ami_filter": { - "filters": { - "virtualization-type": "hvm", - "name": "ubuntu/images/*ubuntu-bionic-18.04-amd64-server-*", - "root-device-type": "ebs" - }, - "owners": ["099720109477"], - "most_recent": true - }, - "ssh_username": "ubuntu" - }, - { - "name": "azure", - "type": "azure-arm", - "client_id": "{{user `azure_client_id`}}", - "client_secret": "{{user `azure_client_secret`}}", - "tenant_id": "{{user `azure_tenant_id`}}", - "subscription_id": "{{user `azure_subscription_id`}}", - "image_publisher": "Canonical", - "image_offer": "UbuntuServer", - "image_sku": "18.04-LTS", - "managed_image_name": "celo-{{user `network_name`}}-{{user `sync_mode`}}-node-latest", - "managed_image_resource_group_name": "CeloNodeImages", - "location": "West Us 2", - "temp_resource_group_name": "CeloNodeImageBuilder", - "os_type": "Linux", - "ssh_username": "ubuntu" - }, - { - "name": "gcp", - "type": "googlecompute", - "project_id": "{{user `gcp_project_id`}}", - "source_image": "ubuntu-1804-bionic-v20200317", - "image_name": "celo-{{user `network_name`}}-{{user `sync_mode`}}-node-latest", - "ssh_username": "ubuntu", - "zone": "us-central1-a" - } - ], - "provisioners": [ - { - "type": "shell", - "execute_command": "echo 'packer' | sudo -S sh -c '{{ .Vars }} {{ .Path }}'", - "script": "../aws/testnet/modules/startup-scripts/install-base.sh" - }, - { - "type": "shell", - "execute_command": "echo 'packer' | sudo -S sh -c '{{ .Vars }} {{ .Path }}'", - "script": "../aws/testnet/modules/startup-scripts/install-docker.sh" - }, - { - "type": "shell", - "inline": [ - "echo {{user `additional_params`}} >> additional_params", - "echo {{user `sync_mode`}} >> sync_mode" - ] - }, - { - "type": "shell", - "execute_command": "echo 'packer' | sudo -S sh -c '{{ .Vars }} {{ .Path }}'", - "script": "scripts/init.sh" - }, - { - "type": "file", - "source": "scripts/start.sh", - "destination": "/tmp/start.sh" - }, - { - "type": "shell", - "execute_command": "echo 'packer' | sudo -S sh -c '{{ .Vars }} {{ .Path }}'", - "inline": [ - "cp /tmp/start.sh /var/lib/cloud/scripts/per-instance/start.sh", - "chmod +x /var/lib/cloud/scripts/per-instance/start.sh" - ] - }, - { - "type": "shell", - "execute_command": "echo 'packer' | sudo -S sh -c '{{ .Vars }} {{ .Path }}'", - "inline": ["find / -name \"authorized_keys\" -exec rm -f {} \\;"] - } - ] -} diff --git a/packages/terraform-modules-public/images/readme.md b/packages/terraform-modules-public/images/readme.md deleted file mode 100644 index 1b3adc39f4e..00000000000 --- a/packages/terraform-modules-public/images/readme.md +++ /dev/null @@ -1,33 +0,0 @@ -# Celo Blockchain Images - -Hashicorp Packer is a tool for building machine images to ease deployment on various cloud platforms. - -As cLabs we provide prebuilt full and lightest Celo blockchain machine images for popular cloud platforms using this Packer script, however anyone is free to use this as inspiration for generating their own. - -## Setup - -To generate a machine image for a cloud provider you'll first need to [install Packer](https://learn.hashicorp.com/tutorials/packer/getting-started-install) and configure your credentials as specified. - -## Building Images - -A Makefile is provided for easy image generation, running `make alfajores` for example, will generate full and lightest nodes for each major cloud provider (AWS, GCP and Azure). - -For more fine grained control you can run Packer manually with `packer build node.json`. One handy flag to note is `-only`, ie. `packer build -only=gcp,aws node.json` will only build images for GCP and AWS. - -See the Makefile for precise examples of how you can generate machine images and pass through additional variables. - -## Making Images Public - -### AWS - -Navigate to the your AMIs tab in AWS (under the EC2 screen) and select the image before clicking `Actions` ->`Modify Image Permissions` -> `Public`. - -### GCP - -Making an image public to the world on GCP requires you to run the following command after you've correctly configured your `gcloud` access. - -```bash -gcloud compute images add-iam-policy-binding - --member='allAuthenticatedUsers' \ - --role='roles/compute.imageUser' -``` diff --git a/packages/terraform-modules-public/images/scripts/init.sh b/packages/terraform-modules-public/images/scripts/init.sh deleted file mode 100644 index 83d56aa75f5..00000000000 --- a/packages/terraform-modules-public/images/scripts/init.sh +++ /dev/null @@ -1,3 +0,0 @@ -NODE_DIRECTORY=/home/ubuntu/celo-data-directory -mkdir -p $NODE_DIRECTORY -cd $NODE_DIRECTORY diff --git a/packages/terraform-modules-public/images/scripts/start.sh b/packages/terraform-modules-public/images/scripts/start.sh deleted file mode 100644 index 7deeea4a3f8..00000000000 --- a/packages/terraform-modules-public/images/scripts/start.sh +++ /dev/null @@ -1,8 +0,0 @@ -#! /bin/bash - - -SYNC_MODE=$(cat /home/ubuntu/sync_mode) -ADDITIONAL_PARAMS=$(cat /home/ubuntu/additional_params) - -cd /home/ubuntu/celo-data-directory -sudo docker run --name celo-$SYNC_MODE-node -d --restart unless-stopped -p 127.0.0.1:8545:8545 -p 127.0.0.1:8546:8546 -p 30303:30303 -p 30303:30303/udp -v $PWD:/root/.celo us.gcr.io/celo-org/geth:mainnet --verbosity 3 --syncmode $SYNC_MODE --rpc --rpcaddr 0.0.0.0 --rpcapi eth,net,web3,debug,admin,personal --light.serve 1 --light.maxpeers 1 --maxpeers 10 --nousb --datadir /root/.celo $ADDITIONAL_PARAMS diff --git a/packages/terraform-modules/.gitignore b/packages/terraform-modules/.gitignore deleted file mode 100644 index 42b565799f5..00000000000 --- a/packages/terraform-modules/.gitignore +++ /dev/null @@ -1 +0,0 @@ -plan/ diff --git a/packages/terraform-modules/README.md b/packages/terraform-modules/README.md deleted file mode 100644 index 91b921d1f57..00000000000 --- a/packages/terraform-modules/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# Terraform Testnets - -## Overview - -Terraform is a tool that allows developers to treat "infrastructure as code." -Infrastructure is defined in modules, and Terraform creates/changes/destroys -when changes are applied. - -## Local Setup - -It's best to use this package with `celotool`, but if you need to -run `terraform` commands locally: - -1. Download Terraform https://www.terraform.io/downloads.html -1. Ensure you have a service account key file at the path shown in the - module's `provider "google"` section. -1. `terraform init` to download anything specific to the module (i.e. GCP specific - binaries) -1. `terraform apply` to initially deploy or upgrade -1. `terraform destroy` to destroy diff --git a/packages/terraform-modules/forno/main.tf b/packages/terraform-modules/forno/main.tf deleted file mode 100644 index 641b7ed2206..00000000000 --- a/packages/terraform-modules/forno/main.tf +++ /dev/null @@ -1,176 +0,0 @@ -# For managing terraform state remotely -terraform { - backend "gcs" { - bucket = "celo_tf_state" - } - required_providers { - google = { - source = "hashicorp/google" - version = "3.69.0" - } - google-beta = { - source = "hashicorp/google-beta" - version = "3.69.0" - } - random = { - source = "hashicorp/random" - version = "3.1.0" - } - } -} - -provider "google" { - credentials = file(var.gcloud_credentials_path) - project = var.gcloud_project - region = "us-west1" - zone = "us-west1-a" -} - -provider "google-beta" { - credentials = file(var.gcloud_credentials_path) - project = var.gcloud_project - region = "us-west1" - zone = "us-west1-a" -} - -data "terraform_remote_state" "state" { - backend = "gcs" - config = { - bucket = "celo_tf_state" - prefix = "${var.celo_env}/forno" - } -} - -module "http_backends" { - source = "./modules/backends" - # variables - backend_max_requests_per_second = var.backend_max_requests_per_second - celo_env = var.celo_env - context_info = var.context_info_http - health_check_destination_port = 6000 - type = "http" - timeout_sec = 60 # 1 minute - security_policy_id = google_compute_security_policy.forno.self_link -} - -module "ws_backends" { - source = "./modules/backends" - # variables - backend_max_requests_per_second = var.backend_max_requests_per_second - celo_env = var.celo_env - context_info = var.context_info_ws - health_check_destination_port = 6001 - type = "ws" - timeout_sec = 1200 # 20 minutes - security_policy_id = google_compute_security_policy.forno.self_link -} - -module "kong" { - source = "./modules/backends" - # variables - backend_max_requests_per_second = var.backend_max_requests_per_second_kong - celo_env = var.celo_env - context_info = var.context_info_kong - health_check_destination_port = 8000 - health_check_request_path = "/kong/status" - type = "kong" - timeout_sec = 60 # 1 minute - security_policy_id = google_compute_security_policy.forno.self_link -} - -resource "google_compute_global_address" "global_address" { - name = "${var.celo_env}-forno-global-address" - - address_type = "EXTERNAL" - ip_version = "IPV4" -} - -resource "google_compute_managed_ssl_certificate" "ssl_cert" { - provider = google-beta - - name = "${var.celo_env}-forno-ssl-cert-${random_id.ssl_random_suffix.hex}" - - managed { - domains = var.ssl_cert_domains - } - - lifecycle { - create_before_destroy = true - } -} - -resource "random_id" "ssl_random_suffix" { - byte_length = 4 - - keepers = { - domains = join(",", var.ssl_cert_domains) - } -} - -resource "google_compute_url_map" "url_map" { - name = "${var.celo_env}-forno-url-map" - default_service = module.kong.backend_service_id - - host_rule { - hosts = ["*"] - path_matcher = "${var.celo_env}-forno-path-matcher" - } - - path_matcher { - name = "${var.celo_env}-forno-path-matcher" - default_service = module.kong.backend_service_id - - path_rule { - paths = ["/ws"] - service = module.ws_backends.backend_service_id - route_action { - url_rewrite { - path_prefix_rewrite = "/" - } - } - } - - path_rule { - paths = ["/kong", "/kong/*"] - service = module.kong.backend_service_id - } - - path_rule { - paths = ["/kong", "/kong/*"] - service = module.kong.backend_service_id - } - } -} - -# This will route ingress traffic to the geographically closest backend -# whose utilization is not full. -# See https://cloud.google.com/load-balancing/docs/https#network-service-tiers_1 -resource "google_compute_target_https_proxy" "target_https_proxy" { - name = "${var.celo_env}-forno-target-https-proxy" - url_map = google_compute_url_map.url_map.id - ssl_certificates = [ - google_compute_managed_ssl_certificate.ssl_cert.id, - ] -} - -resource "google_compute_global_forwarding_rule" "forwarding_rule" { - name = "${var.celo_env}-forno-forwarding-rule" - - target = google_compute_target_https_proxy.target_https_proxy.id - ip_address = google_compute_global_address.global_address.address - port_range = "443" -} - -# This allows GCP health check traffic AND traffic that is being sent from LBs -# to network endpoints -resource "google_compute_firewall" "allow-health-check" { - name = "${var.celo_env}-forno-health-check-firewall" - direction = "INGRESS" - source_ranges = ["130.211.0.0/22", "35.191.0.0/16"] - network = var.vpc_network_name - - allow { - protocol = "tcp" - ports = ["6000", "6001", "8000", "8545", "8546"] - } -} diff --git a/packages/terraform-modules/forno/modules/backends/main.tf b/packages/terraform-modules/forno/modules/backends/main.tf deleted file mode 100644 index 6b99e5bd963..00000000000 --- a/packages/terraform-modules/forno/modules/backends/main.tf +++ /dev/null @@ -1,46 +0,0 @@ -resource "google_compute_health_check" "http_health_check" { - name = "${var.celo_env}-forno-http-health-check-${var.type}" - - http_health_check { - port = var.health_check_destination_port - # For NetworkEndpointGroup, the port specified for each network endpoint is used for health checking - port_specification = "USE_FIXED_PORT" - request_path = var.health_check_request_path - } -} - -# This is a reference to the ClusterIP service inside this region's k8s cluster. -# We get the NEG for each context. -data "google_compute_network_endpoint_group" "service_network_endpoint_group" { - name = each.value.service_network_endpoint_group_name - zone = each.value.zone - - for_each = var.context_info -} - -# A backend that can route traffic to all of the context NEGs. -resource "google_compute_backend_service" "backend_service" { - provider = google-beta - name = "${var.celo_env}-forno-backend-service-${var.type}" - - health_checks = [google_compute_health_check.http_health_check.self_link] - timeout_sec = var.timeout_sec - - custom_response_headers = [ - "Access-Control-Allow-Origin:*", - "Access-Control-Allow-Methods:GET, POST, OPTIONS", - "Access-Control-Allow-Headers:DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range", - "Access-Control-Expose-Headers:Content-Length,Content-Range" - ] - - security_policy = var.security_policy_id - - dynamic "backend" { - for_each = var.context_info - content { - balancing_mode = "RATE" - max_rate_per_endpoint = var.backend_max_requests_per_second - group = data.google_compute_network_endpoint_group.service_network_endpoint_group[backend.key].self_link - } - } -} diff --git a/packages/terraform-modules/forno/modules/backends/outputs.tf b/packages/terraform-modules/forno/modules/backends/outputs.tf deleted file mode 100644 index c26ef7ffbb4..00000000000 --- a/packages/terraform-modules/forno/modules/backends/outputs.tf +++ /dev/null @@ -1,3 +0,0 @@ -output "backend_service_id" { - value = google_compute_backend_service.backend_service.id -} diff --git a/packages/terraform-modules/forno/modules/backends/variables.tf b/packages/terraform-modules/forno/modules/backends/variables.tf deleted file mode 100644 index 3435434abf4..00000000000 --- a/packages/terraform-modules/forno/modules/backends/variables.tf +++ /dev/null @@ -1,46 +0,0 @@ -variable "backend_max_requests_per_second" { - type = number - description = "The max number of requests per second that a backend can receive. In this case, a backend refers to all the nodes in a cluster." -} - -variable "celo_env" { - type = string - description = "Name of the Celo environment" -} - -variable "context_info" { - type = map( - object({ - zone = string - service_network_endpoint_group_name = string - }) - ) - description = "Provides basic information on each context. Keys are contexts and values are the corresponding info" -} - -variable "health_check_destination_port" { - type = number - description = "The destination port the health check will test" -} - -variable "health_check_request_path" { - type = string - description = "The requested path the health check will test" - default = "/" -} - -variable "timeout_sec" { - type = number - description = "The timeout for the backend service in seconds" - default = 30 -} - -variable "type" { - type = string - description = "Type of backends, only used for names" -} - -variable "security_policy_id" { - type = string - description = "Cloud Armon security policy ID applied to the backend" -} diff --git a/packages/terraform-modules/forno/outputs.tf b/packages/terraform-modules/forno/outputs.tf deleted file mode 100644 index 88a414bb6c6..00000000000 --- a/packages/terraform-modules/forno/outputs.tf +++ /dev/null @@ -1,3 +0,0 @@ -output "forno_ip_address" { - value = google_compute_global_address.global_address.address -} diff --git a/packages/terraform-modules/forno/security_policy.tf b/packages/terraform-modules/forno/security_policy.tf deleted file mode 100644 index 85fd61cc824..00000000000 --- a/packages/terraform-modules/forno/security_policy.tf +++ /dev/null @@ -1,27 +0,0 @@ -resource "google_compute_security_policy" "forno" { - name = "${var.celo_env}-forno-security-policy" - - rule { - action = "deny(403)" - priority = "1000" - match { - versioned_expr = "SRC_IPS_V1" - config { - src_ip_ranges = var.banned_cidr - } - } - description = "Deny access to forno due to unfair usage" - } - - rule { - action = "allow" - priority = "2147483647" - match { - versioned_expr = "SRC_IPS_V1" - config { - src_ip_ranges = ["*"] - } - } - description = "default rule" - } -} diff --git a/packages/terraform-modules/forno/variables.tf b/packages/terraform-modules/forno/variables.tf deleted file mode 100644 index ebd7760f413..00000000000 --- a/packages/terraform-modules/forno/variables.tf +++ /dev/null @@ -1,69 +0,0 @@ -variable "backend_max_requests_per_second" { - type = number - description = "The max number of requests per second that a backend can receive. In this case, a backend refers to each endpoint (pod)" -} - -variable "backend_max_requests_per_second_kong" { - type = number - description = "The max number of requests per second that a backend can receive. In this case, a backend refers to each endpoint (pod)" -} - -variable "celo_env" { - type = string - description = "Name of the Celo environment" -} - -variable "context_info_http" { - type = map( - object({ - zone = string - service_network_endpoint_group_name = string - }) - ) - description = "Provides basic information on each context for HTTP. Keys are contexts and values are the corresponding info" -} - -variable "context_info_ws" { - type = map( - object({ - zone = string - service_network_endpoint_group_name = string - }) - ) - description = "Provides basic information on each context for WS. Keys are contexts and values are the corresponding info" -} - -variable "context_info_kong" { - type = map( - object({ - zone = string - service_network_endpoint_group_name = string - }) - ) - description = "Provides basic information on each context for Kong. Keys are contexts and values are the corresponding info" -} - -variable "gcloud_credentials_path" { - type = string - description = "Path to the file containing the Google Cloud credentials to use" -} - -variable "gcloud_project" { - type = string - description = "Name of the Google Cloud project to use" -} - -variable "ssl_cert_domains" { - type = list(string) - description = "Domains to use for the SSL certificate. Each must end with a period." -} - -variable "banned_cidr" { - type = list(string) - description = "Banned CIDR to make request to forno." -} - -variable "vpc_network_name" { - type = string - description = "The name of the VPC network" -} diff --git a/packages/terraform-modules/forno/versions.tf b/packages/terraform-modules/forno/versions.tf deleted file mode 100644 index 6b6318def82..00000000000 --- a/packages/terraform-modules/forno/versions.tf +++ /dev/null @@ -1,3 +0,0 @@ -terraform { - required_version = ">= 0.13" -} diff --git a/packages/terraform-modules/testnet-network/README.md b/packages/terraform-modules/testnet-network/README.md deleted file mode 100644 index 80a457d255e..00000000000 --- a/packages/terraform-modules/testnet-network/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# testnet-network - -This Terraform module exists as a measure of safely creating and destroying -VPC networks. Because `testnet` may sometimes be on the `default` VPC network -that is used by other resources, we need to make sure that `terraform destroy`ing -the testnet module will not result in the default VPC network being destroyed. -Rather than declaring the network as a `resource` in `testnet`, we instead declare -it as a `data` source. This prevents the network from being deleted upon `terraform destroy`, -but also prevents the network from being created. Terraform lacks basic -conditionals to directly implement this logic in the `testnet` module. - -This module is only intended to be used by `celotool`, which only creates/destroys -a network if it is not the `default` VPC. diff --git a/packages/terraform-modules/testnet-network/main.tf b/packages/terraform-modules/testnet-network/main.tf deleted file mode 100644 index fa7a8daaa68..00000000000 --- a/packages/terraform-modules/testnet-network/main.tf +++ /dev/null @@ -1,25 +0,0 @@ -provider "google" { - credentials = file(var.gcloud_credentials_path) - project = var.gcloud_project - region = "us-west1" - zone = "us-west1-a" -} - -# For managing terraform state remotely -terraform { - backend "gcs" { - bucket = "celo_tf_state" - } -} - -data "terraform_remote_state" "state" { - backend = "gcs" - config = { - bucket = "celo_tf_state" - prefix = "${var.celo_env}/testnet-network" - } -} - -resource "google_compute_network" "testnet-network" { - name = var.network_name -} diff --git a/packages/terraform-modules/testnet-network/variables.tf b/packages/terraform-modules/testnet-network/variables.tf deleted file mode 100644 index 17dc776ca3a..00000000000 --- a/packages/terraform-modules/testnet-network/variables.tf +++ /dev/null @@ -1,19 +0,0 @@ -variable celo_env { - type = string - description = "Name of the testnet Celo environment" -} - -variable gcloud_credentials_path { - type = string - description = "Path to the file containing the Google Cloud credentials to use" -} - -variable gcloud_project { - type = string - description = "Name of the Google Cloud project to use" -} - -variable network_name { - type = string - description = "The name of the network to use" -} diff --git a/packages/terraform-modules/testnet/README.md b/packages/terraform-modules/testnet/README.md deleted file mode 100644 index f7df4fd7114..00000000000 --- a/packages/terraform-modules/testnet/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# Testnet - -This creates a VM-based testnet with a bootnode and multiple validators. - -## Overview - -Each type of node (validator/bootnode/tx-node) is in its own module. -A separate module `tx-node-load-balancer` defines an internal TCP load balancer -for ports 8545 and 8546. This is so Blockscout on the same VPC can reach the tx-nodes. -The GCP provider, network, firewall etc declarations are found in the `main.tf` file. - -The script that is run immediately upon the startup of a VM instance is found in -a module's `startup.sh` file. This is where `geth` or `bootnode` is started, -and any setup work is performed. The variables required by these are typically -pulled from a `.env` file by `celotool` and passed to `terraform`. - -Sometimes, if recreating an address right after deleting one, GCP will say -that the resource already exists and `terraform apply` will fail. In this case, -just wait a little bit and try again. - -## Setup - -A few infrastructure assumptions are made by this module and `celotool`: - -1. Google Cloud Platform is used -1. The environment variable `GOOGLE_APPLICATION_CREDENTIALS` is set to the path - of a JSON key file with the credentials that will be used by Terraform. -1. The GCP project used is the value of `TESTNET_PROJECT_NAME` in the environment's `.env` file -1. A bucket called `celo_tf_state` has been manually created. This is where the Terraform - remote backends are. Nothing very sensitive is stored in here, but it should be private. -1. A bucket called `celo-testnet-secrets` has been manually created. This is used to store - sensitive secrets that are uploaded by celotool and downloaded at startup from inside - `startup.sh`. This is to ensure that secrets are not stored in the state files. - -## Google Cloud Permissions Needed - -A service account must be able to create/list/modify/delete networks, -firewalls, instances, objects, addresses, and disks. - -For cLabs employees, a Google Cloud role `Terraform Testnet Admin` has been -created. diff --git a/packages/terraform-modules/testnet/main.tf b/packages/terraform-modules/testnet/main.tf deleted file mode 100644 index 33c035ab94f..00000000000 --- a/packages/terraform-modules/testnet/main.tf +++ /dev/null @@ -1,242 +0,0 @@ -provider "google" { - credentials = file(var.gcloud_credentials_path) - project = var.gcloud_project - region = "us-west1" - zone = "us-west1-a" -} - -provider "acme" { - server_url = "https://acme-v02.api.letsencrypt.org/directory" -} - -# For managing terraform state remotely -terraform { - backend "gcs" { - bucket = "celo_tf_state" - } - required_providers { - google = "~> 2.16.0" - } -} - -data "terraform_remote_state" "state" { - backend = "gcs" - config = { - bucket = "celo_tf_state" - prefix = "${var.celo_env}/testnet" - } -} - -locals { - target_tag_bootnode = "${var.celo_env}-bootnode" - # any geth node (tx nodes & validators) - target_tag_node = "${var.celo_env}-node" - - target_tag_proxy = "${var.celo_env}-proxy" - target_tag_tx_node = "${var.celo_env}-tx-node" - target_tag_tx_node_private = "${var.celo_env}-tx-node-private" - target_tag_validator = "${var.celo_env}-validator" - - target_tag_ssl = "${var.celo_env}-external-ssl" - - target_tags_all = [ - local.target_tag_bootnode, - local.target_tag_node, - local.target_tag_proxy, - local.target_tag_ssl - ] -} - -data "google_compute_network" "network" { - name = var.network_name -} - -resource "google_compute_firewall" "ssh_firewall" { - name = "${var.celo_env}-ssh-firewall" - network = data.google_compute_network.network.name - - target_tags = local.target_tags_all - - allow { - protocol = "tcp" - ports = ["22"] - } -} - -resource "google_compute_firewall" "geth_firewall" { - name = "${var.celo_env}-geth-firewall" - network = data.google_compute_network.network.name - - target_tags = [local.target_tag_node] - - allow { - protocol = "tcp" - ports = ["30303"] - } - - allow { - protocol = "udp" - ports = ["30303"] - } -} - -resource "google_compute_firewall" "geth_metrics_firewall" { - name = "${var.celo_env}-geth-metrics-firewall" - network = data.google_compute_network.network.name - - target_tags = [local.target_tag_node] - - # allow all IPs internal to the VPC - source_ranges = ["10.0.0.0/8"] - - allow { - protocol = "tcp" - ports = ["6060", "9200"] - } -} - -resource "google_compute_firewall" "rpc_firewall_internal" { - name = "${var.celo_env}-rpc-firewall-internal" - network = data.google_compute_network.network.name - - target_tags = [local.target_tag_tx_node_private] - - # allow all IPs internal to the VPC - source_ranges = ["10.0.0.0/8"] - - allow { - protocol = "tcp" - ports = ["8545", "8546"] - } -} - -resource "google_compute_firewall" "rpc_firewall" { - name = "${var.celo_env}-rpc-firewall" - network = data.google_compute_network.network.name - - target_tags = [local.target_tag_tx_node] - - allow { - protocol = "tcp" - ports = ["8545", "8546"] - } -} - -resource "google_compute_firewall" "bootnode_firewall" { - name = "${var.celo_env}-bootnode-firewall" - network = data.google_compute_network.network.name - - target_tags = [local.target_tag_bootnode] - - allow { - protocol = "udp" - ports = ["30301"] - } -} - -module "bootnode" { - source = "./modules/bootnode" - # variables - celo_env = var.celo_env - gcloud_secrets_base_path = var.gcloud_secrets_base_path - gcloud_secrets_bucket = var.gcloud_secrets_bucket - gcloud_vm_service_account_email = var.gcloud_vm_service_account_email - geth_bootnode_docker_image_repository = var.geth_bootnode_docker_image_repository - geth_bootnode_docker_image_tag = var.geth_bootnode_docker_image_tag - network_id = var.network_id - network_name = data.google_compute_network.network.name -} - -module "tx_node" { - source = "./modules/full-node" - # variables - block_time = var.block_time - bootnode_ip_address = module.bootnode.ip_address - celo_env = var.celo_env - ethstats_host = var.ethstats_host - gcloud_secrets_base_path = var.gcloud_secrets_base_path - gcloud_secrets_bucket = var.gcloud_secrets_bucket - gcloud_vm_service_account_email = var.gcloud_vm_service_account_email - genesis_content_base64 = var.genesis_content_base64 - geth_metrics = var.geth_metrics - geth_node_docker_image_repository = var.geth_node_docker_image_repository - geth_node_docker_image_tag = var.geth_node_docker_image_tag - geth_verbosity = var.geth_verbosity - in_memory_discovery_table = var.in_memory_discovery_table - instance_tags = [local.target_tag_tx_node] - max_peers = 500 - name = "tx-node" - network_id = var.network_id - network_name = data.google_compute_network.network.name - gcmode = "full" - node_count = var.tx_node_count - node_disk_size_gb = var.node_disk_size_gb - rpc_apis = "eth,net,web3" -} - -module "tx_node_private" { - source = "./modules/full-node" - # variables - block_time = var.block_time - bootnode_ip_address = module.bootnode.ip_address - celo_env = var.celo_env - ethstats_host = var.ethstats_host - gcloud_secrets_base_path = var.gcloud_secrets_base_path - gcloud_secrets_bucket = var.gcloud_secrets_bucket - gcloud_vm_service_account_email = var.gcloud_vm_service_account_email - genesis_content_base64 = var.genesis_content_base64 - geth_metrics = var.geth_metrics - geth_node_docker_image_repository = var.geth_node_docker_image_repository - geth_node_docker_image_tag = var.geth_node_docker_image_tag - geth_verbosity = var.geth_verbosity - in_memory_discovery_table = var.in_memory_discovery_table - instance_tags = [local.target_tag_tx_node_private] - max_peers = 500 - name = "tx-node-private" - network_id = var.network_id - network_name = data.google_compute_network.network.name - gcmode = "archive" - node_count = var.private_tx_node_count - rpc_apis = "eth,net,web3,debug,txpool" - node_disk_size_gb = var.private_node_disk_size_gb -} - -# used for access by blockscout -module "tx_node_lb" { - source = "./modules/tx-node-load-balancer" - # variables - celo_env = var.celo_env - dns_gcloud_project = var.dns_gcloud_project - dns_zone_name = var.dns_zone_name - forno_host = var.forno_host - gcloud_project = var.gcloud_project - gcloud_vm_service_account_email = var.gcloud_vm_service_account_email - letsencrypt_email = var.letsencrypt_email - network_name = data.google_compute_network.network.name - private_tx_node_self_links = module.tx_node_private.self_links - tx_node_self_links = module.tx_node.self_links -} - -module "validator" { - source = "./modules/validator" - # variables - block_time = var.block_time - bootnode_ip_address = module.bootnode.ip_address - celo_env = var.celo_env - ethstats_host = var.ethstats_host - gcloud_secrets_base_path = var.gcloud_secrets_base_path - gcloud_secrets_bucket = var.gcloud_secrets_bucket - gcloud_vm_service_account_email = var.gcloud_vm_service_account_email - genesis_content_base64 = var.genesis_content_base64 - geth_metrics = var.geth_metrics - geth_node_docker_image_repository = var.geth_node_docker_image_repository - geth_node_docker_image_tag = var.geth_node_docker_image_tag - geth_verbosity = var.geth_verbosity - in_memory_discovery_table = var.in_memory_discovery_table - istanbul_request_timeout_ms = var.istanbul_request_timeout_ms - network_id = var.network_id - network_name = data.google_compute_network.network.name - node_disk_size_gb = var.node_disk_size_gb - proxies_per_validator = var.proxies_per_validator - validator_count = var.validator_count -} diff --git a/packages/terraform-modules/testnet/modules/bootnode/main.tf b/packages/terraform-modules/testnet/modules/bootnode/main.tf deleted file mode 100644 index 777615df7dd..00000000000 --- a/packages/terraform-modules/testnet/modules/bootnode/main.tf +++ /dev/null @@ -1,54 +0,0 @@ -locals { - name_prefix = "${var.celo_env}-bootnode" -} - -resource "google_compute_address" "bootnode" { - name = "${local.name_prefix}-address" - address_type = "EXTERNAL" -} - -resource "google_compute_instance" "bootnode" { - name = local.name_prefix - machine_type = "n1-standard-1" - - tags = [local.name_prefix] - - allow_stopping_for_update = true - - boot_disk { - initialize_params { - image = "debian-cloud/debian-9" - } - } - - scratch_disk { - - } - - network_interface { - network = var.network_name - access_config { - nat_ip = google_compute_address.bootnode.address - } - } - - metadata_startup_script = templatefile( - format("%s/startup.sh", path.module), { - gcloud_secrets_base_path : var.gcloud_secrets_base_path, - gcloud_secrets_bucket : var.gcloud_secrets_bucket, - geth_bootnode_docker_image_repository : var.geth_bootnode_docker_image_repository, - geth_bootnode_docker_image_tag : var.geth_bootnode_docker_image_tag, - ip_address : google_compute_address.bootnode.address, - network_id : var.network_id - } - ) - - service_account { - email = var.gcloud_vm_service_account_email - scopes = [ - "https://www.googleapis.com/auth/devstorage.read_only", - "https://www.googleapis.com/auth/logging.write", - "https://www.googleapis.com/auth/monitoring.write" - ] - } -} diff --git a/packages/terraform-modules/testnet/modules/bootnode/outputs.tf b/packages/terraform-modules/testnet/modules/bootnode/outputs.tf deleted file mode 100644 index edc627de1c3..00000000000 --- a/packages/terraform-modules/testnet/modules/bootnode/outputs.tf +++ /dev/null @@ -1,3 +0,0 @@ -output ip_address { - value = google_compute_address.bootnode.address -} diff --git a/packages/terraform-modules/testnet/modules/bootnode/startup.sh b/packages/terraform-modules/testnet/modules/bootnode/startup.sh deleted file mode 100644 index 2a3a08aa0c5..00000000000 --- a/packages/terraform-modules/testnet/modules/bootnode/startup.sh +++ /dev/null @@ -1,149 +0,0 @@ -#! /bin/bash - -# ---- Set Up Logging ---- - -curl -sSO https://dl.google.com/cloudagents/install-logging-agent.sh -bash install-logging-agent.sh - -echo " -@include config.d/*.conf -# Prometheus monitoring. - - @type prometheus - port 24231 - - - @type prometheus_monitor - - -# Do not collect fluentd's own logs to avoid infinite loops. - - @type null - - -# Add a unique insertId to each log entry that doesn't already have it. -# This helps guarantee the order and prevent log duplication. - -@type add_insert_ids - - -# Configure all sources to output to Google Cloud Logging - - @type google_cloud - buffer_type file - buffer_path /var/log/google-fluentd/buffers - # Set the chunk limit conservatively to avoid exceeding the recommended - # chunk size of 5MB per write request. - buffer_chunk_limit 512KB - # Flush logs every 5 seconds, even if the buffer is not full. - flush_interval 5s - # Enforce some limit on the number of retries. - disable_retry_limit false - # After 3 retries, a given chunk will be discarded. - retry_limit 3 - # Wait 10 seconds before the first retry. The wait interval will be doubled on - # each following retry (20s, 40s...) until it hits the retry limit. - retry_wait 10 - # Never wait longer than 5 minutes between retries. If the wait interval - # reaches this limit, the exponentiation stops. - # Given the default config, this limit should never be reached, but if - # retry_limit and retry_wait are customized, this limit might take effect. - max_retry_wait 300 - # Use multiple threads for processing. - num_threads 8 - # Use the gRPC transport. - use_grpc true - # If a request is a mix of valid log entries and invalid ones, ingest the - # valid ones and drop the invalid ones instead of dropping everything. - partial_success true - # Enable monitoring via Prometheus integration. - enable_monitoring true - monitoring_type opencensus - detect_json true -" > /etc/google-fluentd/google-fluentd.conf - -echo " - - @type rewrite_tag_filter - - key log - pattern ^{ - tag docker_logs_json - - - key log - pattern ^[^{] - tag docker_logs_plain - - - - - @type parser - key_name log - reserve_data false - - @type json - - - - - @type record_transformer - - message $${record["log"]} - - -" > /etc/google-fluentd/config.d/docker.conf -systemctl restart google-fluentd - -# ---- Set Up Monitoring Agent ---- - -curl -sSO https://dl.google.com/cloudagents/install-monitoring-agent.sh -bash install-monitoring-agent.sh - -# ---- Install Docker ---- - -echo "Installing Docker..." - -# TODO(trevor): investigate how to pull this into a separate file so -# other startup scripts can use it -apt update && apt upgrade -apt install -y apt-transport-https ca-certificates curl software-properties-common gnupg2 -curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add - -add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable" -apt update && apt upgrade -apt install -y docker-ce -systemctl start docker - -echo "Configuring Docker..." -gcloud auth configure-docker - -# use GCP logging for Docker containers -echo '{"log-driver":"gcplogs"}' > /etc/docker/daemon.json -systemctl restart docker - -# ---- Set Up and Run Geth ---- - -BOOTNODE_VERBOSITY=1 - -GETH_BOOTNODE_DOCKER_IMAGE=${geth_bootnode_docker_image_repository}:${geth_bootnode_docker_image_tag} - -# download & apply secrets pulled from Cloud Storage as environment vars -echo "Downloading secrets from Google Cloud Storage..." -SECRETS_ENV_PATH=/var/.env.celo.secrets -gsutil cp gs://${gcloud_secrets_bucket}/${gcloud_secrets_base_path}/.env.bootnode $SECRETS_ENV_PATH -# Apply the .env file -. $SECRETS_ENV_PATH - -echo "Pulling bootnode..." -docker pull $GETH_BOOTNODE_DOCKER_IMAGE - -echo "Starting bootnode..." -docker run -p 30301:30301/udp --name bootnode --net=host --restart=always -d $GETH_BOOTNODE_DOCKER_IMAGE /bin/sh -c "\ - set -euo pipefail && \ - mkdir /etc/bootnode && \ - echo $NODE_KEY > /etc/bootnode/node.key && \ - /usr/local/bin/bootnode \ - --nat=extip:${ip_address} \ - --networkid=${network_id} \ - --nodekey=/etc/bootnode/node.key \ - --verbosity=$BOOTNODE_VERBOSITY" diff --git a/packages/terraform-modules/testnet/modules/bootnode/variables.tf b/packages/terraform-modules/testnet/modules/bootnode/variables.tf deleted file mode 100644 index 6493eca4dc2..00000000000 --- a/packages/terraform-modules/testnet/modules/bootnode/variables.tf +++ /dev/null @@ -1,39 +0,0 @@ -variable celo_env { - type = string - description = "Name of the testnet Celo environment" -} - -variable gcloud_secrets_base_path { - type = string - description = "Base path in the secrets bucket of a Google Cloud Storage file containing validator secrets" -} - -variable gcloud_secrets_bucket { - type = string - description = "Name of the Google Cloud Storage bucket where secrets are kept" -} - -variable gcloud_vm_service_account_email { - type = string - description = "The email of the service account to associate virtual machines with" -} - -variable geth_bootnode_docker_image_repository { - type = string - description = "Repository of the geth bootnode docker image" -} - -variable geth_bootnode_docker_image_tag { - type = string - description = "Tag of the geth bootnode docker image" -} - -variable network_id { - type = number - description = "The network ID number" -} - -variable network_name { - type = string - description = "Name of the GCP network" -} diff --git a/packages/terraform-modules/testnet/modules/full-node/main.tf b/packages/terraform-modules/testnet/modules/full-node/main.tf deleted file mode 100644 index 3414589ac8c..00000000000 --- a/packages/terraform-modules/testnet/modules/full-node/main.tf +++ /dev/null @@ -1,116 +0,0 @@ -locals { - attached_disk_name = "celo-data" - name_prefix = "${var.celo_env}-${var.name}" - # generate names using `var.name` if `var.names` isn't set - names = length(var.names) > 0 ? var.names : [for node_index in range(var.node_count) : "${var.name}-${node_index}"] -} - -resource "google_compute_address" "full_node" { - name = "${var.celo_env}-${each.key}-address-${random_id.full_node[each.key].hex}" - address_type = "EXTERNAL" - - for_each = local.names - - lifecycle { - create_before_destroy = true - } -} - -resource "google_compute_instance" "full_node" { - name = "${var.celo_env}-${each.key}-${random_id.full_node[each.key].hex}" - machine_type = "n1-standard-2" - - for_each = local.names - - tags = concat(["${var.celo_env}-node"], var.instance_tags) - - allow_stopping_for_update = true - - boot_disk { - initialize_params { - image = "debian-cloud/debian-9" - } - } - - attached_disk { - source = google_compute_disk.full_node[each.key].self_link - device_name = local.attached_disk_name - } - - network_interface { - network = var.network_name - access_config { - nat_ip = google_compute_address.full_node[each.key].address - } - } - - metadata_startup_script = templatefile( - format("%s/startup.sh", path.module), { - additional_geth_flags : var.additional_geth_flags, - attached_disk_name : local.attached_disk_name, - block_time : var.block_time, - bootnode_ip_address : var.bootnode_ip_address, - ethstats_host : var.ethstats_host, - gcloud_secrets_base_path : var.gcloud_secrets_base_path, - gcloud_secrets_bucket : var.gcloud_secrets_bucket, - genesis_content_base64 : var.genesis_content_base64, - geth_metrics : var.geth_metrics, - geth_node_docker_image_repository : var.geth_node_docker_image_repository, - geth_node_docker_image_tag : var.geth_node_docker_image_tag, - geth_verbosity : var.geth_verbosity, - in_memory_discovery_table : var.in_memory_discovery_table, - ip_address : google_compute_address.full_node[each.key].address, - max_light_peers : var.max_light_peers, - max_peers : var.max_peers, - name : each.key, - network_id : var.network_id, - network_name : var.celo_env, - gcmode: var.gcmode, - node_name : "${var.celo_env}-${each.key}", - proxy : var.proxy, - rid : each.key, - rpc_apis : var.rpc_apis, - } - ) - - service_account { - email = var.gcloud_vm_service_account_email - scopes = [ - "https://www.googleapis.com/auth/devstorage.read_only", - "https://www.googleapis.com/auth/logging.write", - "https://www.googleapis.com/auth/monitoring.write" - ] - } - - lifecycle { - create_before_destroy = true - } -} - -resource "google_compute_disk" "full_node" { - name = "${var.celo_env}-${each.key}-disk-${random_id.full_node_disk[each.key].hex}" - - for_each = local.names - - type = "pd-ssd" - # in GB - size = var.node_disk_size_gb - physical_block_size_bytes = 4096 - - lifecycle { - create_before_destroy = true - } -} - -resource "random_id" "full_node" { - for_each = local.names - - byte_length = 8 -} - -# Separate random id so that updating the ID of the instance doesn't force a new disk -resource "random_id" "full_node_disk" { - for_each = local.names - - byte_length = 8 -} diff --git a/packages/terraform-modules/testnet/modules/full-node/outputs.tf b/packages/terraform-modules/testnet/modules/full-node/outputs.tf deleted file mode 100644 index 7f8141af2de..00000000000 --- a/packages/terraform-modules/testnet/modules/full-node/outputs.tf +++ /dev/null @@ -1,19 +0,0 @@ -output ip_addresses { - value = [for v in google_compute_address.full_node : v.address] -} - -output internal_ip_addresses { - value = [for v in google_compute_instance.full_node : v.network_interface.0.network_ip] -} - -output self_links { - value = [for v in google_compute_instance.full_node : v.self_link] -} - -output ip_addresses_map { - value = { for k, v in google_compute_address.full_node : k => v.address } -} - -output internal_ip_addresses_map { - value = { for k, v in google_compute_instance.full_node : k => v.network_interface.0.network_ip } -} diff --git a/packages/terraform-modules/testnet/modules/full-node/startup.sh b/packages/terraform-modules/testnet/modules/full-node/startup.sh deleted file mode 100644 index 6c661bb4b94..00000000000 --- a/packages/terraform-modules/testnet/modules/full-node/startup.sh +++ /dev/null @@ -1,241 +0,0 @@ -#! /bin/bash - -# ---- Set Up Logging ---- - -curl -sSO https://dl.google.com/cloudagents/install-logging-agent.sh -bash install-logging-agent.sh - -echo " -@include config.d/*.conf -# Prometheus monitoring. - - @type prometheus - port 24231 - - - @type prometheus_monitor - - -# Do not collect fluentd's own logs to avoid infinite loops. - - @type null - - -# Add a unique insertId to each log entry that doesn't already have it. -# This helps guarantee the order and prevent log duplication. - -@type add_insert_ids - - -# Configure all sources to output to Google Cloud Logging - - @type google_cloud - buffer_type file - buffer_path /var/log/google-fluentd/buffers - # Set the chunk limit conservatively to avoid exceeding the recommended - # chunk size of 5MB per write request. - buffer_chunk_limit 512KB - # Flush logs every 5 seconds, even if the buffer is not full. - flush_interval 5s - # Enforce some limit on the number of retries. - disable_retry_limit false - # After 3 retries, a given chunk will be discarded. - retry_limit 3 - # Wait 10 seconds before the first retry. The wait interval will be doubled on - # each following retry (20s, 40s...) until it hits the retry limit. - retry_wait 10 - # Never wait longer than 5 minutes between retries. If the wait interval - # reaches this limit, the exponentiation stops. - # Given the default config, this limit should never be reached, but if - # retry_limit and retry_wait are customized, this limit might take effect. - max_retry_wait 300 - # Use multiple threads for processing. - num_threads 8 - # Use the gRPC transport. - use_grpc true - # If a request is a mix of valid log entries and invalid ones, ingest the - # valid ones and drop the invalid ones instead of dropping everything. - partial_success true - # Enable monitoring via Prometheus integration. - enable_monitoring true - monitoring_type opencensus - detect_json true -" > /etc/google-fluentd/google-fluentd.conf - -echo " - - @type rewrite_tag_filter - - key log - pattern ^{ - tag docker_logs_json - - - key log - pattern ^[^{] - tag docker_logs_plain - - - - - @type parser - key_name log - reserve_data false - - @type json - - - - - @type record_transformer - - message $${record["log"]} - - -" > /etc/google-fluentd/config.d/docker.conf -systemctl restart google-fluentd - -# ---- Set Up Monitoring Agent ---- - -curl -sSO https://dl.google.com/cloudagents/install-monitoring-agent.sh -bash install-monitoring-agent.sh - -# ---- Set Up Persistent Disk ---- - -# gives a path similar to `/dev/sdb` -DISK_PATH=`readlink -f /dev/disk/by-id/google-${attached_disk_name}` -DATA_DIR=/root/.celo - -echo "Setting up persistent disk ${attached_disk_name} at $DISK_PATH..." - -DISK_FORMAT=ext4 -CURRENT_DISK_FORMAT=`lsblk -i -n -o fstype $DISK_PATH` - -echo "Checking if disk $DISK_PATH format $CURRENT_DISK_FORMAT matches desired $DISK_FORMAT..." - -# If the disk has already been formatted previously (this will happen -# if this instance has been recreated with the same disk), we skip formatting -if [[ $CURRENT_DISK_FORMAT == $DISK_FORMAT ]]; then - echo "Disk $DISK_PATH is correctly formatted as $DISK_FORMAT" -else - echo "Disk $DISK_PATH is not formatted correctly, formatting as $DISK_FORMAT..." - mkfs.ext4 -m 0 -F -E lazy_itable_init=0,lazy_journal_init=0,discard $DISK_PATH -fi - -mkdir -p $DATA_DIR -echo "Mounting $DISK_PATH onto $DATA_DIR" -mount -o discard,defaults $DISK_PATH $DATA_DIR - -# ---- Install Docker ---- - -echo "Installing Docker..." -apt update && apt upgrade -apt install -y apt-transport-https ca-certificates curl software-properties-common gnupg2 -curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add - -add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable" -apt update && apt upgrade -apt install -y docker-ce -systemctl start docker - -echo "Configuring Docker..." -gcloud auth configure-docker - -# use GCP logging for Docker containers -echo '{"log-driver":"fluentd","log-opts":{"fluentd-address":"0.0.0.0:24224","tag":"docker_logs"}}' > /etc/docker/daemon.json -systemctl restart docker - -# ---- Set Up and Run Geth ---- - -GETH_NODE_DOCKER_IMAGE=${geth_node_docker_image_repository}:${geth_node_docker_image_tag} - -# download & apply secrets pulled from Cloud Storage as environment vars -echo "Downloading secrets from Google Cloud Storage..." -SECRETS_ENV_PATH=/var/.env.celo.secrets -gsutil cp gs://${gcloud_secrets_bucket}/${gcloud_secrets_base_path}/.env.${name} $SECRETS_ENV_PATH -# Apply the .env file -. $SECRETS_ENV_PATH - -echo "Address: $ACCOUNT_ADDRESS" -echo "Bootnode enode address: $BOOTNODE_ENODE_ADDRESS" - -BOOTNODE_ENODE=$BOOTNODE_ENODE_ADDRESS@${bootnode_ip_address}:30301 -echo "Bootnode enode: $BOOTNODE_ENODE" - -echo "Pulling geth..." -docker pull $GETH_NODE_DOCKER_IMAGE - -IN_MEMORY_DISCOVERY_TABLE_FLAG="" -[[ ${in_memory_discovery_table} == "true" ]] && IN_MEMORY_DISCOVERY_TABLE_FLAG="--use-in-memory-discovery-table" - -RPC_APIS=${rpc_apis} - -if [[ ${proxy} == "true" ]]; then - ADDITIONAL_GETH_FLAGS="--proxy.proxy --proxy.internalendpoint :30503 --proxy.proxiedvalidatoraddress $PROXIED_VALIDATOR_ADDRESS" -fi - -METRICS_FLAGS="" -if [[ ${geth_metrics} == "true" ]]; then - # Valid from celo-blockchain >=1.5.x - METRICS_FLAGS="$METRICS_FLAGS --metrics --pprof --pprof.port 6060 --pprof.addr 127.0.0.1" -fi - -# Using bootnode so their enode url will be published to the discv5 DHT (and light clients can discover them) -BOOTNODE_FLAG="--bootnodes=enode://$BOOTNODE_ENODE" - -DATA_DIR=/root/.celo - -mkdir -p $DATA_DIR/account -echo -n "${genesis_content_base64}" | base64 -d > $DATA_DIR/genesis.json -echo -n "${rid}" > $DATA_DIR/replica_id -echo -n "${ip_address}" > $DATA_DIR/ipAddress -echo -n "$PRIVATE_KEY" > $DATA_DIR/pkey -echo -n "$ACCOUNT_ADDRESS" > $DATA_DIR/address -echo -n "$BOOTNODE_ENODE_ADDRESS" > $DATA_DIR/bootnodeEnodeAddress -echo -n "$BOOTNODE_ENODE" > $DATA_DIR/bootnodeEnode -echo -n "$GETH_ACCOUNT_SECRET" > $DATA_DIR/account/accountSecret - -echo "Starting geth..." -# We need to override the entrypoint in the geth image (which is originally `geth`) -docker run \ - -v $DATA_DIR:$DATA_DIR \ - --name geth \ - --net=host \ - --restart always \ - --entrypoint /bin/sh \ - -d \ - $GETH_NODE_DOCKER_IMAGE -c "\ - ( - set -euo pipefail ; \ - geth init $DATA_DIR/genesis.json \ - ) ; \ - geth account import --password $DATA_DIR/account/accountSecret $DATA_DIR/pkey ; \ - geth \ - --$BOOTNODE_FLAG \ - --datadir $DATA_DIR \ - --light.serve 90 \ - --light.maxpeers ${max_light_peers} \ - --maxpeers=${max_peers} \ - --nousb \ - --rpc \ - --rpcaddr 0.0.0.0 \ - --rpcapi=$RPC_APIS \ - --rpccorsdomain='*' \ - --rpcvhosts=* \ - --ws \ - --wsaddr 0.0.0.0 \ - --wsorigins=* \ - --wsapi=$RPC_APIS \ - --nodekey=$DATA_DIR/pkey \ - --etherbase=$ACCOUNT_ADDRESS \ - --networkid=${network_id} \ - --syncmode=full \ - --gcmode=${gcmode} \ - --consoleformat=json \ - --consoleoutput=stdout \ - --verbosity=${geth_verbosity} \ - --ethstats=${node_name}@${ethstats_host} \ - --metrics \ - --pprof \ - $IN_MEMORY_DISCOVERY_TABLE_FLAG \ - $ADDITIONAL_GETH_FLAGS" - diff --git a/packages/terraform-modules/testnet/modules/full-node/variables.tf b/packages/terraform-modules/testnet/modules/full-node/variables.tf deleted file mode 100644 index 0dda2aca227..00000000000 --- a/packages/terraform-modules/testnet/modules/full-node/variables.tf +++ /dev/null @@ -1,139 +0,0 @@ -variable additional_geth_flags { - type = string - description = "Additional flags to be passed when running geth" - default = "" -} - -variable block_time { - type = number - description = "Number of seconds between each block" -} - -variable bootnode_ip_address { - type = string - description = "The external IP address of the bootnode" -} - -variable celo_env { - type = string - description = "Name of the testnet Celo environment" -} - -variable ethstats_host { - type = string - description = "Ethstats url or IP address" -} - -variable gcloud_secrets_base_path { - type = string - description = "Base path in the secrets bucket of a Google Cloud Storage file containing tx-node secrets" -} - -variable gcloud_secrets_bucket { - type = string - description = "Name of the Google Cloud Storage bucket where secrets are kept" -} - -variable gcloud_vm_service_account_email { - type = string - description = "The email of the service account to associate virtual machines with" -} - -variable genesis_content_base64 { - type = string - description = "Content of the genesis file encoded in base64" -} - -variable geth_metrics { - type = string - description = "Enable Geth metrics (prometheus format) on port 6060" -} - -variable geth_node_docker_image_repository { - type = string - description = "Repository of the geth docker image" -} - -variable geth_node_docker_image_tag { - type = string - description = "Tag of the geth docker image" -} - -variable geth_verbosity { - type = number - description = "Verbosity of the nodes" -} - -variable in_memory_discovery_table { - type = bool - description = "Specifies whether to use an in memory discovery table" -} - -variable instance_tags { - type = list(string) - description = "Tags to set for the instance" - default = [] -} - -variable max_light_peers { - type = number - description = "The maximum number of light client peers" - default = 50 -} - -variable max_peers { - type = number - description = "The maximum number of peers for the node" - default = 100 -} - -variable name { - type = string - description = "Name of the nodes. Should be specified if names is not." - default = "" -} - -variable names { - type = set(string) - description = "Name of each node to create. If not specified, the names will be generated using the name variable and an index." - default = [] -} - -variable network_id { - type = number - description = "The network ID number" -} - -variable network_name { - type = string - description = "Name of the GCP network the node VM is in" -} - -variable node_count { - type = number - description = "Number of nodes to create if names is not specified" - default = 0 -} - -variable node_disk_size_gb { - type = number - description = "The size in GB for each node's disk" -} - -variable gcmode { - type = string - description = "Celo-blockchain --gcmode option" - default = "full" -} - -variable proxy { - type = bool - description = "Whether the node is a proxy for a validator" - default = false -} - -variable rpc_apis { - type = string - description = "Comma separated string including which RPC APIs to expose" - default = "eth,net,web3" -} diff --git a/packages/terraform-modules/testnet/modules/tx-node-load-balancer/main.tf b/packages/terraform-modules/testnet/modules/tx-node-load-balancer/main.tf deleted file mode 100644 index db760f315b7..00000000000 --- a/packages/terraform-modules/testnet/modules/tx-node-load-balancer/main.tf +++ /dev/null @@ -1,244 +0,0 @@ -locals { - name_prefix = "${var.celo_env}-tx-node-lb" - target_https_proxy_name = "${var.celo_env}-tx-node-lb-external-http-proxy" -} - -# We want to maintain websockets (which are not supposed by the HTTPS external -# load balancer) & avoid unnecessary egress costs. -# An internal & external load balancer cannot use the same instance group. To -# get around this, we allocate 1 of the tx-nodes to be for internal load balancing. -# It's still included in `static_nodes.json`, but not included in the forno -# setup. In the future, consider moving this node to live in Kubernetes to be -# along with the services that use it. - -# internal load balancer for cLabs-run infra: - -resource "google_compute_instance_group" "internal" { - name = "${local.name_prefix}-internal-group-${random_id.internal.hex}" - - instances = var.private_tx_node_self_links - - lifecycle { - create_before_destroy = true - } -} - -resource "random_id" "internal" { - byte_length = 8 -} - -data "google_compute_subnetwork" "subnet" { - name = var.network_name -} - -resource "google_compute_address" "internal" { - name = "${local.name_prefix}-internal-address" - address_type = "INTERNAL" - subnetwork = data.google_compute_subnetwork.subnet.self_link -} - -resource "google_compute_forwarding_rule" "internal" { - name = "${local.name_prefix}-internal-fwd-rule" - - backend_service = google_compute_region_backend_service.internal.self_link - ip_address = google_compute_address.internal.address - load_balancing_scheme = "INTERNAL" - network = var.network_name - ports = ["8545", "8546"] -} - -resource "google_compute_region_backend_service" "internal" { - name = "${local.name_prefix}-internal-service" - - # internal HTTP load balancing does not support WebSockets - protocol = "TCP" - - backend { - group = google_compute_instance_group.internal.self_link - } - - health_checks = [ - google_compute_health_check.internal.self_link - ] -} - -resource "google_compute_health_check" "internal" { - name = "${local.name_prefix}-internal-health" - - tcp_health_check { - port = 8545 - } -} - -# external load balancer for forno setup - - -resource "google_compute_instance_group" "external" { - name = "${local.name_prefix}-group-${random_id.external.hex}" - - instances = var.tx_node_self_links - - lifecycle { - create_before_destroy = true - } - - named_port { - name = "http" - port = "8545" - } -} - -resource "random_id" "external" { - byte_length = 8 -} - -resource "google_compute_global_address" "external" { - name = "${local.name_prefix}-external-address" - address_type = "EXTERNAL" -} - -resource "google_compute_global_forwarding_rule" "external" { - name = "${local.name_prefix}-external-fwd-rule" - - ip_address = google_compute_global_address.external.address - load_balancing_scheme = "EXTERNAL" - port_range = "443" - target = google_compute_target_https_proxy.external.self_link -} - -resource "google_compute_target_https_proxy" "external" { - name = "${local.name_prefix}-external-http-proxy" - url_map = google_compute_url_map.external.self_link - ssl_certificates = [google_compute_ssl_certificate.external.self_link] - quic_override = "NONE" -} - -resource "google_compute_url_map" "external" { - name = "${local.name_prefix}-external-url-map" - default_service = google_compute_backend_service.external.self_link - - host_rule { - hosts = [var.forno_host] - path_matcher = "allpaths" - } - - path_matcher { - name = "allpaths" - default_service = google_compute_backend_service.external.self_link - } -} - -resource "google_compute_backend_service" "external" { - name = "${local.name_prefix}-external-service" - port_name = "http" - protocol = "HTTP" - - backend { - group = google_compute_instance_group.external.self_link - } - - health_checks = [ - google_compute_health_check.external.self_link - ] -} - -resource "google_compute_health_check" "external" { - name = "${local.name_prefix}-external-health" - - http_health_check { - port = 8545 - } -} - -resource "google_dns_record_set" "external" { - # google cloud requires the name to end with a "." - name = "${var.forno_host}." - managed_zone = data.google_dns_managed_zone.external.name - type = "A" - ttl = 3600 - - rrdatas = [google_compute_global_address.external.address] - - project = var.dns_gcloud_project -} - -data "google_dns_managed_zone" "external" { - name = var.dns_zone_name - project = var.dns_gcloud_project -} - -# SSL certificate from Let's Encrypt: - -resource "google_compute_instance" "external_ssl" { - name = "${local.name_prefix}-external-ssl" - machine_type = "f1-micro" - - tags = ["${var.celo_env}-external-ssl"] - - allow_stopping_for_update = true - - boot_disk { - initialize_params { - image = "debian-cloud/debian-9" - } - } - - network_interface { - network = var.network_name - access_config { - } - } - - metadata_startup_script = templatefile( - format("%s/ssl-startup.sh", path.module), { - cert_prefix : "${local.name_prefix}-forno-", - forno_host : var.forno_host, - gcloud_project : var.dns_gcloud_project, - letsencrypt_email : var.letsencrypt_email, - target_https_proxy_name : local.target_https_proxy_name - } - ) - - service_account { - email = var.gcloud_vm_service_account_email - scopes = [ - "https://www.googleapis.com/auth/compute", - "https://www.googleapis.com/auth/devstorage.read_only", - "https://www.googleapis.com/auth/logging.write", - "https://www.googleapis.com/auth/ndev.clouddns.readwrite" - ] - } -} - -# temporary self-signed certificate that will be overwritten by -# google_compute_instance.external_ssl - -resource "google_compute_ssl_certificate" "external" { - name_prefix = "${local.name_prefix}-ssl-cert" - private_key = tls_private_key.tmp.private_key_pem - certificate = tls_self_signed_cert.tmp.cert_pem - - lifecycle { - create_before_destroy = true - } -} - -resource "tls_self_signed_cert" "tmp" { - key_algorithm = "RSA" - private_key_pem = tls_private_key.tmp.private_key_pem - - subject { - common_name = var.forno_host - organization = "Temporary self signed cert" - } - - validity_period_hours = 12 - - allowed_uses = [ - "server_auth", - ] -} - -resource "tls_private_key" "tmp" { - algorithm = "RSA" -} diff --git a/packages/terraform-modules/testnet/modules/tx-node-load-balancer/output.tf b/packages/terraform-modules/testnet/modules/tx-node-load-balancer/output.tf deleted file mode 100644 index aba2eac8be9..00000000000 --- a/packages/terraform-modules/testnet/modules/tx-node-load-balancer/output.tf +++ /dev/null @@ -1,3 +0,0 @@ -output internal_ip_address { - value = google_compute_address.internal.address -} diff --git a/packages/terraform-modules/testnet/modules/tx-node-load-balancer/ssl-startup.sh b/packages/terraform-modules/testnet/modules/tx-node-load-balancer/ssl-startup.sh deleted file mode 100644 index e4e674aa3e3..00000000000 --- a/packages/terraform-modules/testnet/modules/tx-node-load-balancer/ssl-startup.sh +++ /dev/null @@ -1,129 +0,0 @@ -#! /bin/bash - -# ---- Set Up Logging ---- - -curl -sSO https://dl.google.com/cloudagents/install-logging-agent.sh -bash install-logging-agent.sh - -echo " -@include config.d/*.conf -# Prometheus monitoring. - - @type prometheus - port 24231 - - - @type prometheus_monitor - - -# Do not collect fluentd's own logs to avoid infinite loops. - - @type null - - -# Add a unique insertId to each log entry that doesn't already have it. -# This helps guarantee the order and prevent log duplication. - -@type add_insert_ids - - -# Configure all sources to output to Google Cloud Logging - - @type google_cloud - buffer_type file - buffer_path /var/log/google-fluentd/buffers - # Set the chunk limit conservatively to avoid exceeding the recommended - # chunk size of 5MB per write request. - buffer_chunk_limit 512KB - # Flush logs every 5 seconds, even if the buffer is not full. - flush_interval 5s - # Enforce some limit on the number of retries. - disable_retry_limit false - # After 3 retries, a given chunk will be discarded. - retry_limit 3 - # Wait 10 seconds before the first retry. The wait interval will be doubled on - # each following retry (20s, 40s...) until it hits the retry limit. - retry_wait 10 - # Never wait longer than 5 minutes between retries. If the wait interval - # reaches this limit, the exponentiation stops. - # Given the default config, this limit should never be reached, but if - # retry_limit and retry_wait are customized, this limit might take effect. - max_retry_wait 300 - # Use multiple threads for processing. - num_threads 8 - # Use the gRPC transport. - use_grpc true - # If a request is a mix of valid log entries and invalid ones, ingest the - # valid ones and drop the invalid ones instead of dropping everything. - partial_success true - # Enable monitoring via Prometheus integration. - enable_monitoring true - monitoring_type opencensus - detect_json true -" > /etc/google-fluentd/google-fluentd.conf - -echo " - - @type rewrite_tag_filter - - key log - pattern ^{ - tag docker_logs_json - - - key log - pattern ^[^{] - tag docker_logs_plain - - - - - @type parser - key_name log - reserve_data false - - @type json - - - - - @type record_transformer - - message $${record["log"]} - - -" > /etc/google-fluentd/config.d/docker.conf -systemctl restart google-fluentd - -# ---- Install Docker ---- - -echo "Installing Docker..." -apt update && apt upgrade -apt install -y apt-transport-https ca-certificates curl software-properties-common gnupg2 -curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add - -add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable" -apt update && apt upgrade -apt install -y docker-ce -systemctl start docker - -echo "Configuring Docker..." -gcloud auth configure-docker - -# use GCP logging for Docker containers -echo '{"log-driver":"gcplogs"}' > /etc/docker/daemon.json -systemctl restart docker - -mkdir -p /home/lego - -# use --env USE_STAGING_SERVER=true to test staging - -/usr/bin/docker run -d \ - -v /home/lego:/root/.lego \ - --restart always \ - --env GCE_PROJECT=${gcloud_project} \ - --env LETSENCRYPT_EMAIL=${letsencrypt_email} \ - --env TARGET_PROXY=${target_https_proxy_name} \ - --env DOMAINS_LIST="-d ${forno_host}" \ - --env CERT_ID_PREFIX=${cert_prefix} \ - --name=ssl-letsencrypt \ - bloomapi/letsencrypt-gcloud-balancer:v1.0.2 diff --git a/packages/terraform-modules/testnet/modules/tx-node-load-balancer/variables.tf b/packages/terraform-modules/testnet/modules/tx-node-load-balancer/variables.tf deleted file mode 100644 index bfe2bb19dbe..00000000000 --- a/packages/terraform-modules/testnet/modules/tx-node-load-balancer/variables.tf +++ /dev/null @@ -1,49 +0,0 @@ -variable celo_env { - type = string - description = "Name of the testnet Celo environment" -} - -variable dns_gcloud_project { - type = string - description = "Name of the Google Cloud project where Cloud DNS is" -} - -variable dns_zone_name { - type = string - description = "Name of the DNS zone for the domain used for the forno setup" -} - -variable forno_host { - type = string - description = "The host name to use for the tx node forno setup" -} - -variable gcloud_project { - type = string - description = "Name of the Google Cloud project to use" -} - -variable gcloud_vm_service_account_email { - type = string - description = "The email of the service account to associate virtual machines with" -} - -variable letsencrypt_email { - type = string - description = "The email to create letsencrypt certificates with" -} - -variable network_name { - type = string - description = "Name of the GCP network the tx-node load balancer is in" -} - -variable private_tx_node_self_links { - type = list(string) - description = "A list including the self_links of each private/internal tx-node" -} - -variable tx_node_self_links { - type = list(string) - description = "A list including the self_links of each public/external tx-node" -} diff --git a/packages/terraform-modules/testnet/modules/validator/main.tf b/packages/terraform-modules/testnet/modules/validator/main.tf deleted file mode 100644 index 36ed6958b35..00000000000 --- a/packages/terraform-modules/testnet/modules/validator/main.tf +++ /dev/null @@ -1,191 +0,0 @@ -# This module creates var.validator_count validators. The first -# local.proxied_validator_count validators are hidden behind externally facing -# proxies, and the rest are exposed to the external internet. - -locals { - attached_disk_name = "celo-data" - name_prefix = "${var.celo_env}-validator" - proxied_validator_count = length(var.proxies_per_validator) -} - -resource "google_compute_address" "validator" { - name = "${local.name_prefix}-address-${count.index}" - address_type = "EXTERNAL" - - # only create external addresses for validators that are not proxied - count = var.validator_count - local.proxied_validator_count -} - -resource "google_compute_address" "validator_internal" { - name = "${local.name_prefix}-internal-address-${count.index}" - subnetwork = google_compute_subnetwork.validator.self_link - address_type = "INTERNAL" - purpose = "GCE_ENDPOINT" - - count = var.validator_count -} - -resource "google_compute_instance" "validator" { - name = "${local.name_prefix}-${count.index}" - machine_type = "n1-standard-2" - - count = var.validator_count - - tags = ["${var.celo_env}-node", "${var.celo_env}-validator"] - - allow_stopping_for_update = true - - boot_disk { - initialize_params { - image = "debian-cloud/debian-9" - } - } - - attached_disk { - source = google_compute_disk.validator[count.index].self_link - device_name = local.attached_disk_name - } - - network_interface { - network = var.network_name - network_ip = google_compute_address.validator_internal[count.index].address - subnetwork = google_compute_subnetwork.validator.name - # We only want an access config for validators that will not be proxied - dynamic "access_config" { - for_each = count.index < local.proxied_validator_count ? [] : [0] - content { - nat_ip = google_compute_address.validator[count.index - local.proxied_validator_count].address - } - } - } - - metadata_startup_script = templatefile( - format("%s/startup.sh", path.module), { - attached_disk_name : local.attached_disk_name, - block_time : var.block_time, - bootnode_ip_address : var.bootnode_ip_address, - ethstats_host : var.ethstats_host, - gcloud_secrets_base_path : var.gcloud_secrets_base_path, - gcloud_secrets_bucket : var.gcloud_secrets_bucket, - genesis_content_base64 : var.genesis_content_base64, - geth_metrics : var.geth_metrics, - geth_node_docker_image_repository : var.geth_node_docker_image_repository, - geth_node_docker_image_tag : var.geth_node_docker_image_tag, - geth_verbosity : var.geth_verbosity, - in_memory_discovery_table : var.in_memory_discovery_table, - ip_address : count.index < local.proxied_validator_count ? "" : google_compute_address.validator[count.index - local.proxied_validator_count].address, - istanbul_request_timeout_ms : var.istanbul_request_timeout_ms, - max_light_peers : 20, - max_peers : 125, - network_id : var.network_id, - network_name : var.celo_env, - proxied : count.index < length(var.proxies_per_validator), - # proxied : var.proxies_per_validator[count.index] > 0 ? true : false, - rid : count.index, - # Searches for all proxies whose map key corresponds to this specific validator - # by finding keys starting with "validator-${this validator index}" - proxy_internal_ip_addresses : compact([for key in keys(module.proxy.internal_ip_addresses_map) : substr(key, 0, length(format("validator-%d", count.index))) == format("validator-%d", count.index) ? module.proxy.internal_ip_addresses_map[key] : ""]), - proxy_external_ip_addresses : compact([for key in keys(module.proxy.ip_addresses_map) : substr(key, 0, length(format("validator-%d", count.index))) == format("validator-%d", count.index) ? module.proxy.ip_addresses_map[key] : ""]), - validator_name : "${local.name_prefix}-${count.index}", - } - ) - - service_account { - email = var.gcloud_vm_service_account_email - scopes = [ - "https://www.googleapis.com/auth/compute", - "https://www.googleapis.com/auth/devstorage.read_only", - "https://www.googleapis.com/auth/logging.write", - "https://www.googleapis.com/auth/monitoring.write" - ] - } -} - -resource "google_compute_disk" "validator" { - name = "${local.name_prefix}-disk-${count.index}" - count = var.validator_count - - type = "pd-ssd" - # in GB - size = var.node_disk_size_gb - physical_block_size_bytes = 4096 -} - -resource "google_compute_subnetwork" "validator" { - name = "${local.name_prefix}-subnet" - network = var.network_name - # Arbitrary IP range. This cannot overlap with existing subnetwork IP ranges - # in the same network, so there can only be at most 1 VM testnet on a VPC network - ip_cidr_range = "10.25.0.0/24" - # to allow an internal instance to reach google API servers (metrics reporting, logs, etc) - private_ip_google_access = true -} - -# proxies - -module "proxy" { - source = "../full-node" - # variables - block_time = var.block_time - bootnode_ip_address = var.bootnode_ip_address - celo_env = var.celo_env - ethstats_host = var.ethstats_host - gcloud_secrets_base_path = var.gcloud_secrets_base_path - gcloud_secrets_bucket = var.gcloud_secrets_bucket - gcloud_vm_service_account_email = var.gcloud_vm_service_account_email - genesis_content_base64 = var.genesis_content_base64 - geth_metrics = var.geth_metrics - geth_node_docker_image_repository = var.geth_node_docker_image_repository - geth_node_docker_image_tag = var.geth_node_docker_image_tag - geth_verbosity = var.geth_verbosity - in_memory_discovery_table = var.in_memory_discovery_table - instance_tags = ["${var.celo_env}-proxy"] - max_peers = 200 - names = flatten([for val_index in range(length(var.proxies_per_validator)) : [for proxy_index in range(var.proxies_per_validator[val_index]) : format("validator-%d-proxy-%d", val_index, proxy_index)]]) - network_id = var.network_id - network_name = var.network_name - node_disk_size_gb = var.node_disk_size_gb - proxy = true -} - -# if there are no proxied validators, we don't have to worry about - -resource "google_compute_firewall" "proxy_internal_ingress" { - count = local.proxied_validator_count > 0 ? 1 : 0 - - name = "${local.name_prefix}-proxy-internal-ingress" - network = var.network_name - - direction = "INGRESS" - source_ranges = ["10.0.0.0/8"] - - allow { - protocol = "tcp" - ports = ["30503"] - } - - allow { - protocol = "udp" - ports = ["30503"] - } -} - -resource "google_compute_firewall" "proxy_internal_egress" { - count = local.proxied_validator_count > 0 ? 1 : 0 - - name = "${local.name_prefix}-proxy-internal-egress" - network = var.network_name - - direction = "EGRESS" - destination_ranges = ["10.0.0.0/8"] - - allow { - protocol = "tcp" - ports = ["30503"] - } - - allow { - protocol = "udp" - ports = ["30503"] - } -} diff --git a/packages/terraform-modules/testnet/modules/validator/outputs.tf b/packages/terraform-modules/testnet/modules/validator/outputs.tf deleted file mode 100644 index 53c502e9e04..00000000000 --- a/packages/terraform-modules/testnet/modules/validator/outputs.tf +++ /dev/null @@ -1,7 +0,0 @@ -output internal_ip_addresses { - value = google_compute_address.validator_internal.*.address -} - -output proxy_internal_ip_addresses { - value = module.proxy.internal_ip_addresses -} diff --git a/packages/terraform-modules/testnet/modules/validator/startup.sh b/packages/terraform-modules/testnet/modules/validator/startup.sh deleted file mode 100644 index b368f759531..00000000000 --- a/packages/terraform-modules/testnet/modules/validator/startup.sh +++ /dev/null @@ -1,300 +0,0 @@ -#! /bin/bash - -GCLOUD_ZONE=`gcloud compute instances list --filter="name=('${validator_name}')" --format 'value(zone)'` - -# If this validator is proxied, it won't have an access config. We need to -# create one for the initial 1 time setup so we can reach the external internet -if [[ ${proxied} == "true" ]]; then - gcloud compute instances add-access-config ${validator_name} --zone=$GCLOUD_ZONE -fi - -# ---- Set Up Logging ---- - -curl -sSO https://dl.google.com/cloudagents/install-logging-agent.sh -bash install-logging-agent.sh - -echo " -@include config.d/*.conf -# Prometheus monitoring. - - @type prometheus - port 24231 - - - @type prometheus_monitor - - -# Do not collect fluentd's own logs to avoid infinite loops. - - @type null - - -# Add a unique insertId to each log entry that doesn't already have it. -# This helps guarantee the order and prevent log duplication. - -@type add_insert_ids - - -# Configure all sources to output to Google Cloud Logging - - @type google_cloud - buffer_type file - buffer_path /var/log/google-fluentd/buffers - # Set the chunk limit conservatively to avoid exceeding the recommended - # chunk size of 5MB per write request. - buffer_chunk_limit 512KB - # Flush logs every 5 seconds, even if the buffer is not full. - flush_interval 5s - # Enforce some limit on the number of retries. - disable_retry_limit false - # After 3 retries, a given chunk will be discarded. - retry_limit 3 - # Wait 10 seconds before the first retry. The wait interval will be doubled on - # each following retry (20s, 40s...) until it hits the retry limit. - retry_wait 10 - # Never wait longer than 5 minutes between retries. If the wait interval - # reaches this limit, the exponentiation stops. - # Given the default config, this limit should never be reached, but if - # retry_limit and retry_wait are customized, this limit might take effect. - max_retry_wait 300 - # Use multiple threads for processing. - num_threads 8 - # Use the gRPC transport. - use_grpc true - # If a request is a mix of valid log entries and invalid ones, ingest the - # valid ones and drop the invalid ones instead of dropping everything. - partial_success true - # Enable monitoring via Prometheus integration. - enable_monitoring true - monitoring_type opencensus - detect_json true -" > /etc/google-fluentd/google-fluentd.conf - -echo " - - @type rewrite_tag_filter - - key log - pattern ^{ - tag docker_logs_json - - - key log - pattern ^[^{] - tag docker_logs_plain - - - - - @type parser - key_name log - reserve_data false - - @type json - - - - - @type record_transformer - - message $${record["log"]} - - -" > /etc/google-fluentd/config.d/docker.conf -systemctl restart google-fluentd - -# ---- Set Up Monitoring Agent ---- - -curl -sSO https://dl.google.com/cloudagents/install-monitoring-agent.sh -bash install-monitoring-agent.sh - -# ---- Set Up Persistent Disk ---- - -# gives a path similar to `/dev/sdb` -DISK_PATH=`readlink -f /dev/disk/by-id/google-${attached_disk_name}` -DATA_DIR=/root/.celo - -echo "Setting up persistent disk ${attached_disk_name} at $DISK_PATH..." - -DISK_FORMAT=ext4 -CURRENT_DISK_FORMAT=`lsblk -i -n -o fstype $DISK_PATH` - -echo "Checking if disk $DISK_PATH format $CURRENT_DISK_FORMAT matches desired $DISK_FORMAT..." - -# If the disk has already been formatted previously (this will happen -# if this instance has been recreated with the same disk), we skip formatting -if [[ $CURRENT_DISK_FORMAT == $DISK_FORMAT ]]; then - echo "Disk $DISK_PATH is correctly formatted as $DISK_FORMAT" -else - echo "Disk $DISK_PATH is not formatted correctly, formatting as $DISK_FORMAT..." - mkfs.ext4 -m 0 -F -E lazy_itable_init=0,lazy_journal_init=0,discard $DISK_PATH -fi - -mkdir -p $DATA_DIR -echo "Mounting $DISK_PATH onto $DATA_DIR" -mount -o discard,defaults $DISK_PATH $DATA_DIR - -# ---- Install Docker ---- - -echo "Installing Docker..." -apt update && apt upgrade -apt install -y apt-transport-https ca-certificates curl software-properties-common gnupg2 -curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add - -add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable" -apt update && apt upgrade -apt install -y docker-ce -systemctl start docker - -echo "Configuring Docker..." -gcloud auth configure-docker - -# use GCP logging for Docker containers -echo '{"log-driver":"fluentd","log-opts":{"fluentd-address":"0.0.0.0:24224","tag":"docker_logs"}}' > /etc/docker/daemon.json -systemctl restart docker - -# ---- Set Up and Run Geth ---- - -GETH_NODE_DOCKER_IMAGE=${geth_node_docker_image_repository}:${geth_node_docker_image_tag} - -# download & apply secrets pulled from Cloud Storage as environment vars -echo "Downloading secrets from Google Cloud Storage..." -SECRETS_ENV_PATH=/var/.env.celo.secrets -gsutil cp gs://${gcloud_secrets_bucket}/${gcloud_secrets_base_path}/.env.validator-${rid} $SECRETS_ENV_PATH -# Apply the .env file -. $SECRETS_ENV_PATH - -echo "Address: $ACCOUNT_ADDRESS" -echo "Bootnode enode address: $BOOTNODE_ENODE_ADDRESS" - -BOOTNODE_ENODE=$BOOTNODE_ENODE_ADDRESS@${bootnode_ip_address}:30301 -echo "Bootnode enode: $BOOTNODE_ENODE" - -echo "Pulling geth..." -docker pull $GETH_NODE_DOCKER_IMAGE - -PROXIED_FLAGS="" -PROXY_ENODE="" -if [[ ${proxied} == "true" ]]; then - - PROXY_COUNT=${length(proxy_internal_ip_addresses)} - PROXY_INTERNAL_IP_ADDRESSES=${join(",", proxy_internal_ip_addresses)} - PROXY_EXTERNAL_IP_ADDRESSES=${join(",", proxy_external_ip_addresses)} - - PROXY_ENODE_URL_PAIRS="" - - PROXY_INDEX=0 - while [ $PROXY_INDEX -lt $PROXY_COUNT ]; do - PROXY_INTERNAL_IP_ADDRESS=`echo -n $PROXY_INTERNAL_IP_ADDRESSES | cut -d ',' -f $(($PROXY_INDEX + 1))` - PROXY_EXTERNAL_IP_ADDRESS=`echo -n $PROXY_EXTERNAL_IP_ADDRESSES | cut -d ',' -f $(($PROXY_INDEX + 1))` - # $PROXY_ENODE_ADDRESSES is from the secrets pulled from google cloud - PROXY_ENODE_ADDRESS=`echo -n $PROXY_ENODE_ADDRESSES | cut -d ',' -f $(($PROXY_INDEX + 1))` - - PROXY_INTERNAL_ENODE="enode://$PROXY_ENODE_ADDRESS@$PROXY_INTERNAL_IP_ADDRESS:30503" - PROXY_EXTERNAL_ENODE="enode://$PROXY_ENODE_ADDRESS@$PROXY_EXTERNAL_IP_ADDRESS:30303" - - echo "Proxy $PROXY_INDEX internal enode: $PROXY_INTERNAL_ENODE" - echo "Proxy $PROXY_INDEX external enode: $PROXY_EXTERNAL_ENODE" - - if [ $PROXY_INDEX -gt 0 ]; then - PROXY_ENODE_URL_PAIRS="$PROXY_ENODE_URL_PAIRS," - fi - PROXY_ENODE_URL_PAIRS="$PROXY_ENODE_URL_PAIRS$PROXY_INTERNAL_ENODE;$PROXY_EXTERNAL_ENODE" - - PROXY_INDEX=$(($PROXY_INDEX + 1)) - done - if docker run --rm --entrypoint=geth $GETH_NODE_DOCKER_IMAGE --help | grep 'proxy.proxyenodeurlpairs' >/dev/null; then - PROXY_FLAG_NAME="--proxy.proxyenodeurlpairs" - else - PROXY_FLAG_NAME="--proxy.proxyenodeurlpair" - fi - PROXIED_FLAGS="--proxy.proxied --nodiscover $PROXY_FLAG_NAME=\"$PROXY_ENODE_URL_PAIRS\"" - - # if this validator is proxied, cut it off from the external internet after - # we've downloaded everything - echo "Deleting access config" - # The command hangs but still succeeds, give it some time - # This is likely because when the access config is actually deleted, this - # instance cannot reach the external internet so the success ack from the server - # is never received - timeout 20 gcloud compute instances delete-access-config ${validator_name} --zone=$GCLOUD_ZONE -fi - -METRICS_FLAGS="" -if [[ ${geth_metrics} == "true" ]]; then - # Valid from celo-blockchain >=1.5.x - METRICS_FLAGS="$METRICS_FLAGS --metrics --pprof --pprof.port 6060 --pprof.addr 127.0.0.1" -fi - -IN_MEMORY_DISCOVERY_TABLE_FLAG="" -[[ ${in_memory_discovery_table} == "true" ]] && IN_MEMORY_DISCOVERY_TABLE_FLAG="--use-in-memory-discovery-table" - -mkdir -p $DATA_DIR/account -echo -n "${genesis_content_base64}" | base64 -d > $DATA_DIR/genesis.json -echo -n "${rid}" > $DATA_DIR/replica_id -echo -n "$ACCOUNT_ADDRESS" > $DATA_DIR/address -echo -n "$BOOTNODE_ENODE_ADDRESS" > $DATA_DIR/bootnodeEnodeAddress -echo -n "$BOOTNODE_ENODE" > $DATA_DIR/bootnodeEnode -echo -n "$GETH_ACCOUNT_SECRET" > $DATA_DIR/account/accountSecret - -NAT_FLAG="" -if [ "${ip_address}" ]; then - echo -n "${ip_address}" > $DATA_DIR/ipAddress - NAT_FLAG="--nat=extip:${ip_address}" -fi - -if [[ "${network_name}" == "alfajores" || "${network_name}" == "baklava" ]]; then - BOOTNODE_FLAG="--${network_name}" -else - BOOTNODE_FLAG="--bootnodes=enode://$BOOTNODE_ENODE" -fi - -echo "Starting geth..." -# We need to override the entrypoint in the geth image (which is originally `geth`). -# `geth account import` fails when the account has already been imported. In -# this case, we do not want to pipefail -docker run \ - -v $DATA_DIR:$DATA_DIR \ - --name geth \ - --net=host \ - --restart always \ - --entrypoint /bin/sh \ - -d \ - $GETH_NODE_DOCKER_IMAGE -c "\ - ( - set -euo pipefail ; \ - geth init $DATA_DIR/genesis.json \ - ) ; \ - TMP_PRIVATE_KEY_FILE=$(mktemp) ; \ - echo -n $PRIVATE_KEY > \$TMP_PRIVATE_KEY_FILE ; \ - geth account import --password $DATA_DIR/account/accountSecret \$TMP_PRIVATE_KEY_FILE ; \ - rm \$TMP_PRIVATE_KEY_FILE ; \ - geth \ - --$BOOTNODE_FLAG \ - --datadir $DATA_DIR \ - --nousb \ - --password=$DATA_DIR/account/accountSecret \ - --unlock=$ACCOUNT_ADDRESS \ - --mine \ - --rpc \ - --rpcaddr 0.0.0.0 \ - --rpcapi=eth,net,web3 \ - --rpccorsdomain='*' \ - --rpcvhosts=* \ - --ws \ - --wsaddr 0.0.0.0 \ - --wsorigins=* \ - --wsapi=eth,net,web3 \ - --etherbase=$ACCOUNT_ADDRESS \ - --networkid=${network_id} \ - --syncmode=full \ - --consoleformat=json \ - --consoleoutput=stdout \ - --verbosity=${geth_verbosity} \ - --ethstats=${validator_name}@${ethstats_host} \ - --maxpeers=${max_peers} \ - --allow-insecure-unlock \ - $METRICS_FLAGS \ - $NAT_FLAG \ - $IN_MEMORY_DISCOVERY_TABLE_FLAG \ - $PROXIED_FLAGS" - diff --git a/packages/terraform-modules/testnet/modules/validator/variables.tf b/packages/terraform-modules/testnet/modules/validator/variables.tf deleted file mode 100644 index 02251abb4c3..00000000000 --- a/packages/terraform-modules/testnet/modules/validator/variables.tf +++ /dev/null @@ -1,94 +0,0 @@ -variable block_time { - type = number - description = "Number of seconds between each block" -} - -variable bootnode_ip_address { - type = string - description = "The external IP address of the bootnode" -} - -variable celo_env { - type = string - description = "Name of the testnet Celo environment" -} - -variable ethstats_host { - type = string - description = "Ethstats url or IP address" -} - -variable gcloud_secrets_base_path { - type = string - description = "Base path in the secrets bucket of a Google Cloud Storage file containing validator secrets" -} - -variable gcloud_secrets_bucket { - type = string - description = "Name of the Google Cloud Storage bucket where secrets are kept" -} - -variable gcloud_vm_service_account_email { - type = string - description = "The email of the service account to associate virtual machines with" -} - -variable genesis_content_base64 { - type = string - description = "Content of the genesis file encoded in base64" -} - -variable geth_metrics { - type = string - description = "Enable Geth metrics (prometheus format) on port 6060" -} - -variable geth_node_docker_image_repository { - type = string - description = "Repository of the geth docker image" -} - -variable geth_node_docker_image_tag { - type = string - description = "Tag of the geth docker image" -} - -variable geth_verbosity { - type = number - description = "Verbosity of the validator nodes" -} - -variable in_memory_discovery_table { - type = bool - description = "Specifies whether to use an in memory discovery table" -} - -variable istanbul_request_timeout_ms { - type = number - description = "The number of ms for the istanbul request timeout" -} - -variable network_id { - type = number - description = "The network ID number" -} - -variable network_name { - type = string - description = "Name of the GCP network the validator VM is in" -} - -variable node_disk_size_gb { - type = number - description = "The size in GB for each node's disk" -} - -variable proxies_per_validator { - type = list(number) - description = "Number of proxies for each validator that is proxied. Does not include validators that aren't proxied. indices correspond to validator indices." -} - -variable validator_count { - type = number - description = "Number of validators to create" -} diff --git a/packages/terraform-modules/testnet/outputs.tf b/packages/terraform-modules/testnet/outputs.tf deleted file mode 100644 index d2d163423c6..00000000000 --- a/packages/terraform-modules/testnet/outputs.tf +++ /dev/null @@ -1,23 +0,0 @@ -output bootnode_ip_address { - value = module.bootnode.ip_address -} - -output tx_node_internal_ip_addresses { - value = module.tx_node.internal_ip_addresses -} - -output tx_node_ip_addresses { - value = module.tx_node.ip_addresses -} - -output tx_node_lb_internal_ip_address { - value = module.tx_node_lb.internal_ip_address -} - -output validator_internal_ip_addresses { - value = module.validator.internal_ip_addresses -} - -output proxy_internal_ip_addresses { - value = module.validator.proxy_internal_ip_addresses -} diff --git a/packages/terraform-modules/testnet/variables.tf b/packages/terraform-modules/testnet/variables.tf deleted file mode 100644 index b4954b0727b..00000000000 --- a/packages/terraform-modules/testnet/variables.tf +++ /dev/null @@ -1,144 +0,0 @@ -variable block_time { - type = number - description = "Number of seconds between each block" -} - -variable celo_env { - type = string - description = "Name of the testnet Celo environment" -} - -variable dns_gcloud_project { - type = string - description = "Name of the Google Cloud project where Cloud DNS is" -} - -variable dns_zone_name { - type = string - description = "Name of the DNS zone for the domain used for the forno setup" -} - -variable ethstats_host { - type = string - description = "Ethstats url or IP address" -} - -variable forno_host { - type = string - description = "The host name to use for the tx node forno setup" -} - -variable gcloud_credentials_path { - type = string - description = "Path to the file containing the Google Cloud credentials to use" -} - -variable gcloud_project { - type = string - description = "Name of the Google Cloud project to use" -} - -variable gcloud_secrets_base_path { - type = string - description = "Base path in the secrets bucket of a Google Cloud Storage file containing node secrets" -} - -variable gcloud_secrets_bucket { - type = string - description = "Name of the Google Cloud Storage bucket where secrets are kept" -} - -variable gcloud_vm_service_account_email { - type = string - description = "The email of the service account to associate virtual machines with" -} - -variable genesis_content_base64 { - type = string - description = "Content of the genesis file encoded in base64" -} - -variable geth_bootnode_docker_image_repository { - type = string - description = "Repository of the bootnode docker image" -} - -variable geth_bootnode_docker_image_tag { - type = string - description = "Tag of the bootnode docker image" -} - -variable geth_metrics { - type = string - description = "Enable Geth metrics (prometheus format) on port 6060" -} - -variable geth_node_docker_image_repository { - type = string - description = "Repository of the geth docker image" -} - -variable geth_node_docker_image_tag { - type = string - description = "Tag of the geth docker image" -} - -variable geth_verbosity { - type = number - description = "Verbosity of all geth nodes" -} - -variable in_memory_discovery_table { - type = bool - description = "Specifies whether to use an in memory discovery table" -} - -variable istanbul_request_timeout_ms { - type = number - description = "The number of ms for the istanbul request timeout" -} - -variable letsencrypt_email { - type = string - description = "The email to create letsencrypt certificates with" -} - -variable network_id { - type = number - description = "The network ID number" -} - -variable network_name { - type = string - description = "The name of the network to use" -} -variable node_disk_size_gb { - type = number - description = "The size in GB of disks for validators, proxies, and txnodes" -} - -variable private_node_disk_size_gb { - type = number - description = "The size in GB of disks for all private txnodes" -} - -variable private_tx_node_count { - type = number - description = "Number of private tx-nodes that are created with RPC ports only internally exposed" - default = 0 -} - -variable proxies_per_validator { - type = list(number) - description = "Number of proxies for each validator that is proxied. Does not include validators that aren't proxied. indices correspond to validator indices." -} - -variable tx_node_count { - type = number - description = "Number of public tx-nodes to create" -} - -variable validator_count { - type = number - description = "Number of validators to create" -} diff --git a/packages/typescript/tslint.json b/packages/typescript/tslint.json index 1a0d6aaf4cc..27167350239 100644 --- a/packages/typescript/tslint.json +++ b/packages/typescript/tslint.json @@ -11,7 +11,10 @@ "no-reference": true, "no-duplicate-super": true, "member-access": false, - "no-implicit-dependencies": false, + "no-implicit-dependencies": [ + true, + "dev" + ], "interface-name": false, "no-submodule-imports": false, "object-literal-sort-keys": false, diff --git a/scripts/deploy-sdks.ts b/scripts/deploy-sdks.ts deleted file mode 100644 index bbb219380b7..00000000000 --- a/scripts/deploy-sdks.ts +++ /dev/null @@ -1,367 +0,0 @@ -#!/usr/local/bin/node - -/* - * deploy-sdks script - * THIS SCRIPT MUST BE RUN WITH NPM TO PUBLISH - `npm run deploy-sdks` - * From the monorepo root run `npm run deploy-sdks` - * You'll first be asked which version to update the sdks to. - * You can pick major, minor, patch, a semantic version, - * or nothing if you don't want to update the versions. - * Then you'll be asked if you want to publish the sdks. - * You can pick Y or N or dry-run for the same behavior as - * `npm publish --dry-run` - * The script will then update all the sdk packages accordingly - * and attempt to publish them accordingly. - * As 2FA is enabled for these packages you'll need to be ready - * to enter 2FA codes as you are prompted. - * (In a dry-run the 2FA codes can be anything) - * If a package fails to update you will be prompted if you - * want to retry. - * You can pick Y or N. - * Any sdk packages that are not published will be saved to - * the `failedSDKs.json` file. - * You will be asked to fix these packages and try again. - * Then the script will exit. - * If you run deploy-sdks and it detects a `failedSDKs.json` - * file it will attempt again to publish those packages - * (using the same version and possibly dry-run option) and - * nothing else. - * Once all packages are successfully deployed the script will - * delete the `failedSDKs.json` file and update all other - * packages in the monorepo that use any of the sdk packages - * to use their `-dev` new version. - */ - -import * as child_process from 'child_process' -import * as colors from 'colors' -import * as fs from 'fs' -import * as path from 'path' -import * as prompt from 'prompt' -import * as semver from 'semver' - -const VERSIONS = ['major', 'minor', 'patch'] -const dontOpen = ['node_modules', 'src', 'lib'] -type PackageJson = { - name: string - version: string - dependencies: { [key: string]: string } - devDependencies: { [key: string]: string } -} - -type Answers = { - packages: string[] - version: string - publish: string -} - -// This is an async IIFE so that we can use `aync/await` -;(async function () { - prompt.start() - - const { confirmNoDanglingDevs } = await prompt.get([ - { - name: 'confirmNoDanglingDevs', - description: colors.yellow( - `Please ensure the @celo/phone-number-privacy-* packages in any sdk/**/package.json are using a published version. Y/N` - ), - }, - ]) - - if (confirmNoDanglingDevs.toString().toUpperCase() !== 'Y') { - process.exit(1) - } - - // `getAnswers` will either prompt the user for a version and whether - // or not to publish or it will use an existing failedSDKs.json file. - const { packages, version, publish } = await getAnswers() - - if (version && !semver.valid(version) && !VERSIONS.includes(version)) { - console.error( - colors.red( - 'Invalid version given. Version must be major, minor, patch, or a semantic version.' - ) - ) - process.exit(1) - } - - const shouldPublish = publish === 'Y' || publish === 'dry-run' - - if (!shouldPublish && !version) { - console.error(colors.red('Either a version or --publish must be given')) - process.exit(1) - } - - let tag = 'latest' - const prerelease = semver.prerelease(version) - if (prerelease) { - tag = (prerelease[0] + '').trim() - - if (!['alpha', 'beta', 'canary', 'rc'].includes(tag)) { - const errorPrompt = [ - { - name: 'confirmTag', - description: colors.red( - `Unknown prerelease keyword given, do you really want to publish ${version} with tag ${tag}? Y/N` - ), - }, - ] - const { confirmTag } = await prompt.get(errorPrompt) - if (confirmTag !== 'Y') { - process.exit(1) - } - } - } - - const sdkPackagePaths = findPackagePaths(path.join(__dirname, '..', 'packages', 'sdk')) - const sdkJsons: PackageJson[] = sdkPackagePaths.map((path) => - JSON.parse(fs.readFileSync(path).toString()) - ) - - // We need all the sdkNames before we go through and update the - // `package.json` dependencies. - const sdkNames = sdkJsons.map(({ name }) => name) - - let newVersion: string - // Here we update the sdk `package.json` objects with updated - // versions and dependencies. - sdkJsons.forEach((json, index) => { - if (!newVersion) { - if (!version) { - newVersion = removeDevSuffix(json.version) - } else { - newVersion = VERSIONS.includes(version) - ? incrementVersion(removeDevSuffix(json.version), version) - : version - } - } - - json.version = newVersion - - if (shouldPublish) { - for (const depName in json.dependencies) { - if (sdkNames.includes(depName)) { - json.dependencies[depName] = newVersion - } - } - for (const depName in json.devDependencies) { - if (sdkNames.includes(depName)) { - json.devDependencies[depName] = newVersion - } - } - } - - writePackageJson(sdkPackagePaths[index], json) - }) - - const otpPrompt = [ - { - name: 'newOtp', - description: colors.green(`Enter 2FA code`), - }, - ] - - let successfulPackages: string[] = [] - let otp = '' - if (shouldPublish) { - // Here we build and publish all the sdk packages - for (let index = 0; index < sdkPackagePaths.length; index++) { - const path = sdkPackagePaths[index] - const packageJson = sdkJsons[index] - if (packages.length && !packages.includes(packageJson.name)) { - console.log(`Skipping ${packageJson.name}`) - successfulPackages.push(packageJson.name) - continue - } - const packageFolderPath = path.replace('package.json', '') - try { - // copy license file from root to sdk folders so it is included in npm package - child_process.execSync(`cp ${__dirname}/../LICENSE ${packageFolderPath}`, { - stdio: 'inherit', - }) - console.log(`Building ${packageJson.name}`) - child_process.execSync('yarn build', { cwd: packageFolderPath, stdio: 'ignore' }) - - console.info(`Publishing ${packageJson.name}@${packageJson.version} tagged as ${tag}...`) - // Here you enter the 2FA code for npm - let { newOtp } = (await prompt.get(otpPrompt)) as { newOtp: string } - if (newOtp) { - otp = newOtp - } else { - newOtp = otp - } - - // Here is the actual publishing - child_process.execSync( - `npm publish --access public --otp ${newOtp} ${ - publish === 'dry-run' ? '--dry-run' : '' - } --tag ${tag}`, - { cwd: packageFolderPath, stdio: 'ignore' } - ) - successfulPackages.push(packageJson.name) - // remove license files from sdks folders - child_process.execSync('rm LICENSE', { cwd: packageFolderPath, stdio: 'inherit' }) - } catch (e) { - const errorPrompt = [ - { - name: 'retry', - description: colors.red( - `${packageJson.name} failed to publish. (Did you run 'yarn deploy-sdks'? must be run as 'npm run deploy-sdks') Error message: ${e.message} Retry? Y/N` - ), - }, - ] - const { retry } = await prompt.get(errorPrompt) - if (retry === 'Y') { - index-- - } - } - } - } - - // This means some packages were not successfully published - // but some were published so we need to track the failed ones - // to keep them in sync. - if (successfulPackages.length && successfulPackages.length !== sdkNames.length) { - const failedPackages = sdkNames.filter((sdkName) => !successfulPackages.includes(sdkName)) - console.error( - colors.red(`The following SDK packages failed to publish ${failedPackages.join(', ')}.`) - ) - console.error(colors.red('Creating failed packages file.')) - fs.writeFileSync( - path.join(__dirname, 'failedSDKs.json'), - JSON.stringify({ packages: failedPackages, version: newVersion, publish }) - ) - console.error(colors.red(`Fix failed packages and try again.`)) - process.exit(1) - } - - const failedJsonPath = path.join(__dirname, 'failedSDKs.json') - if (fs.existsSync(failedJsonPath)) { - fs.unlinkSync(failedJsonPath) - } - - const allPackagePaths = findPackagePaths(path.join(__dirname, '..', 'packages')) - - const newDevVersion = getNewDevVersion(newVersion) - // Finally we update all the packages across the monorepo - // to use the most recent sdk packages. - allPackagePaths.forEach((path) => { - const json: PackageJson = JSON.parse(fs.readFileSync(path).toString()) - let packageChanged = false - const isSdk = sdkNames.includes(json.name) - - if (isSdk) { - json.version = `${newDevVersion}-dev` - packageChanged = true - } - - for (const depName in json.dependencies) { - if (sdkNames.includes(depName)) { - const versionUpdate = - json.dependencies[depName].includes('-dev') || isSdk ? `${newDevVersion}-dev` : newVersion - json.dependencies[depName] = keepCaretOrTilde(json.dependencies[depName], versionUpdate) - packageChanged = true - } - } - for (const depName in json.devDependencies) { - if (sdkNames.includes(depName)) { - const versionUpdate = - json.devDependencies[depName].includes('-dev') || isSdk - ? `${newDevVersion}-dev` - : newVersion - json.devDependencies[depName] = keepCaretOrTilde( - json.devDependencies[depName], - versionUpdate - ) - packageChanged = true - } - } - if (packageChanged) { - writePackageJson(path, json) - } - }) -})() - -async function getAnswers(): Promise { - try { - const json = JSON.parse( - fs.readFileSync(path.join(__dirname, 'failedSDKs.json')).toString() - ) as Answers - console.log(colors.green('Detected failed SDKs file. Attempting to republish failed SDKs.')) - return json - } catch (e) { - const prompts = [ - { - name: 'version', - description: colors.green( - `Specify a version: major, minor, patch, a semantic version number, or nothing` - ), - }, - { - name: 'publish', - description: colors.green(`Should the sdks also be published? Y/N/dry-run`), - }, - ] - const { version, publish } = (await prompt.get(prompts)) as { version: string; publish: string } - return { - version, - publish, - packages: [], - } - } -} - -// Find package paths goes through the given directory and finds the relevant `package.json` files -function findPackagePaths(dir: string): string[] { - return fs.readdirSync(dir, { withFileTypes: true }).reduce((packageJsons, dirent) => { - if (dirent.isDirectory() && !dontOpen.includes(dirent.name)) { - return [...packageJsons, ...findPackagePaths(`${dir}/${dirent.name}`)] - } - if (dirent.name === 'package.json') { - return [...packageJsons, path.join(dir, dirent.name)] - } - return packageJsons - }, []) -} - -function incrementVersion(version: string, command: string) { - const index = VERSIONS.indexOf(command) - return version - .split('.') - .map((v, i) => (i === index ? parseInt(v) + 1 : i > index ? 0 : v)) - .join('.') -} - -function removeDevSuffix(version: string) { - return version.endsWith('-dev') ? version.slice(0, -4) : version -} - -function keepCaretOrTilde(old: string, next: string): string { - return old.startsWith('^') || old.startsWith('~') ? `${old.charAt(0)}${next}` : next -} - -function readPackageJson(filePath: string): PackageJson { - return JSON.parse(fs.readFileSync(filePath).toString()) -} - -function writePackageJson(filePath: string, properties: Partial) { - const packageJson = readPackageJson(filePath) - fs.writeFileSync( - filePath, - JSON.stringify( - { - ...packageJson, - ...properties, - }, - null, - 2 - ) - ) -} - -function getNewDevVersion(version: string) { - const versionArray = version.split('.') - const bump = Number(versionArray[2]) + 1 - if (isNaN(bump)) return version - versionArray[2] = `${bump}` - return versionArray.join('.') -} diff --git a/yarn.lock b/yarn.lock index 0805b9efe34..bf2949f389d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -267,6 +267,11 @@ ethers "~4.0.4" lodash "^4.17.21" +"@adraffy/ens-normalize@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.9.0.tgz#223572538f6bea336750039bb43a4016dcc8182d" + integrity sha512-iowxq3U30sghZotgl4s/oJRci6WPBfNO5YYgk2cIOMCHr3LeGPcsZjCEr+33Q4N+oV3OABDAtA+pyvWjbvBifQ== + "@ampproject/remapping@^2.2.0": version "2.2.1" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" @@ -275,16 +280,6 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@apidevtools/json-schema-ref-parser@^9.0.3": - version "9.1.2" - resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz#8ff5386b365d4c9faa7c8b566ff16a46a577d9b8" - integrity sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg== - dependencies: - "@jsdevtools/ono" "^7.1.3" - "@types/json-schema" "^7.0.6" - call-me-maybe "^1.0.1" - js-yaml "^4.1.0" - "@apollo/protobufjs@1.2.6": version "1.2.6" resolved "https://registry.yarnpkg.com/@apollo/protobufjs/-/protobufjs-1.2.6.tgz#d601e65211e06ae1432bf5993a1a0105f2862f27" @@ -400,7 +395,7 @@ dependencies: tslib "^2.2.0" -"@azure/core-auth@^1.1.4", "@azure/core-auth@^1.3.0", "@azure/core-auth@^1.4.0": +"@azure/core-auth@^1.3.0", "@azure/core-auth@^1.4.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.4.0.tgz#6fa9661c1705857820dbc216df5ba5665ac36a9e" integrity sha512-HFrcTgmuSuukRf/EdPmqBrc5l6Q5Uu+2TbuhaKbgaCpP2TfAeiNaQPAadxO+CYBRHGUzIDteMAjFspFLDLnKVQ== @@ -550,35 +545,6 @@ dependencies: tslib "^2.2.0" -"@azure/ms-rest-azure-env@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@azure/ms-rest-azure-env/-/ms-rest-azure-env-2.0.0.tgz#45809f89763a480924e21d3c620cd40866771625" - integrity sha512-dG76W7ElfLi+fbTjnZVGj+M9e0BIEJmRxU6fHaUQ12bZBe8EJKYb2GV50YWNaP2uJiVQ5+7nXEVj1VN1UQtaEw== - -"@azure/ms-rest-js@^2.0.4": - version "2.6.6" - resolved "https://registry.yarnpkg.com/@azure/ms-rest-js/-/ms-rest-js-2.6.6.tgz#a2ae4a515565ae1b73729b52b25875853bb3240a" - integrity sha512-WYIda8VvrkZE68xHgOxUXvjThxNf1nnGPPe0rAljqK5HJHIZ12Pi3YhEDOn3Ge7UnwaaM3eFO0VtAy4nGVI27Q== - dependencies: - "@azure/core-auth" "^1.1.4" - abort-controller "^3.0.0" - form-data "^2.5.0" - node-fetch "^2.6.7" - tough-cookie "^3.0.1" - tslib "^1.10.0" - tunnel "0.0.6" - uuid "^8.3.2" - xml2js "^0.5.0" - -"@azure/ms-rest-nodeauth@^3.0.10": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@azure/ms-rest-nodeauth/-/ms-rest-nodeauth-3.1.1.tgz#2624222f0685ae580801d6f1abeab20923814693" - integrity sha512-UA/8dgLy3+ZiwJjAZHxL4MUB14fFQPkaAOZ94jsTW/Z6WmoOeny2+cLk0+dyIX/iH6qSrEWKwbStEeB970B9pA== - dependencies: - "@azure/ms-rest-azure-env" "^2.0.0" - "@azure/ms-rest-js" "^2.0.4" - adal-node "^0.2.2" - "@azure/msal-common@^4.0.0": version "4.5.1" resolved "https://registry.yarnpkg.com/@azure/msal-common/-/msal-common-4.5.1.tgz#f35af8b634ae24aebd0906deb237c0db1afa5826" @@ -877,6 +843,13 @@ dependencies: regenerator-runtime "^0.13.11" +"@babel/runtime@^7.20.1": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.2.tgz#062b0ac103261d68a966c4c7baf2ae3e62ec3885" + integrity sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/runtime@^7.6.3", "@babel/runtime@^7.9.2": version "7.21.5" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.5.tgz#8492dddda9644ae3bda3b45eabe87382caee7200" @@ -923,10 +896,10 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@celo/base@1.5.2", "@celo/base@^1.2.0": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@celo/base/-/base-1.5.2.tgz#168ab5e4e30b374079d8d139fafc52ca6bfd4100" - integrity sha512-KGf6Dl9E6D01vAfkgkjL2sG+zqAjspAogILIpWstljWdG5ifyA75jihrnDEHaMCoQS0KxHvTdP1XYS/GS6BEyQ== +"@celo/base@5.0.4": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@celo/base/-/base-5.0.4.tgz#72df2e56aac6cbe33e089e69523826065f53e0a7" + integrity sha512-mzyJXGodTd8HjGgeEfDfq3Tr39/tAKSKiCsJGxI7F2ovPC93NniNq7fyo31Vc7W12SJRgMvejD9+7ReoJrXnKQ== "@celo/bls12377js@0.1.1": version "0.1.1" @@ -936,75 +909,86 @@ "@stablelib/blake2xs" "0.10.4" big-integer "^1.6.44" -"@celo/connect@1.5.2": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@celo/connect/-/connect-1.5.2.tgz#09f0b03bda6f8a6d523fd010492f204cbe82aabd" - integrity sha512-IHsvYp1HizIPfPPeIHyvsmJytIf7HNtNWo9CqCbsqfNfmw53q6dFJu2p5X0qz/fUnR5840cUga8cEyuYZTfp+w== +"@celo/connect@5.0.4": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@celo/connect/-/connect-5.0.4.tgz#9dab3022f2619701b9e9d06e7325d44e33c4ac2c" + integrity sha512-EViV5lxmR0vvtcs+kjazw5d/ITDSD1XbmvHND+oFjNwAKO3DVZ6fsrP1j7Y0ZiWhajP86TWjXfUHuDJTnbJZFQ== dependencies: - "@celo/utils" "1.5.2" + "@celo/base" "^5.0.4" + "@celo/utils" "^5.0.4" "@types/debug" "^4.1.5" "@types/utf8" "^2.1.6" bignumber.js "^9.0.0" debug "^4.1.1" utf8 "3.0.0" -"@celo/contractkit@1.5.2", "@celo/contractkit@^1.2.0": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@celo/contractkit/-/contractkit-1.5.2.tgz#be15d570f3044a190dabb6bbe53d5081c78ea605" - integrity sha512-b0r5TlfYDEscxze1Ai2jyJayiVElA9jvEehMD6aOSNtVhfP8oirjFIIffRe0Wzw1MSDGkw+q1c4m0Yw5sEOlvA== +"@celo/contractkit@5.0.4": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@celo/contractkit/-/contractkit-5.0.4.tgz#00fd583d373761e3b5c46f2006f4bb0372b04f84" + integrity sha512-/IxnngKMBUoA2G0IaZ/Xoq4dKJe140oov1C0qPwHwxVlO80cgwbPZvV/E7GlKNWBi0n6ZTmYkGt5Zs1OiCdyVQ== dependencies: - "@celo/base" "1.5.2" - "@celo/connect" "1.5.2" - "@celo/utils" "1.5.2" - "@celo/wallet-local" "1.5.2" + "@celo/base" "5.0.4" + "@celo/connect" "5.0.4" + "@celo/utils" "5.0.4" + "@celo/wallet-local" "5.0.4" + "@types/bn.js" "^5.1.0" "@types/debug" "^4.1.5" bignumber.js "^9.0.0" - cross-fetch "^3.0.6" + cross-fetch "3.0.6" debug "^4.1.1" fp-ts "2.1.1" io-ts "2.0.1" semver "^7.3.5" - web3 "1.3.6" + web3 "1.10.0" + web3-core-helpers "1.10.0" -"@celo/identity-prev@npm:@celo/identity@1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@celo/identity/-/identity-1.2.0.tgz#822ce8f2237a8498fad6910a2d2707f6b67a6999" - integrity sha512-vpipC8qyEueIKKY0skPXaNwUDIhodGPv9wfFZ5mywzTIz46dbT0VmNSYkvOnoXlqvAjrwdzgvETbVMmpHpj3Xw== +"@celo/identity@^5.0.4": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@celo/identity/-/identity-5.0.4.tgz#77ff3c64d6a9d1e74aa75ec57b331f66504c9411" + integrity sha512-q4onYB78+sWJ5ii5zZjP6s6SA3DzCmxVEchg7ZWd8F94hqufPKGy1ld0tnaUmqlONJJB2QEgjp0c1KqCgmvf+Q== dependencies: - "@celo/base" "^1.2.0" - "@celo/contractkit" "^1.2.0" - "@celo/utils" "^1.2.0" + "@celo/base" "5.0.4" + "@celo/contractkit" "5.0.4" + "@celo/phone-number-privacy-common" "^3.0.3" + "@celo/utils" "5.0.4" "@types/debug" "^4.1.5" bignumber.js "^9.0.0" - blind-threshold-bls "https://github.com/celo-org/blind-threshold-bls-wasm#e1e2f8a" - cross-fetch "3.0.4" + blind-threshold-bls "npm:@celo/blind-threshold-bls@1.0.0-beta" + cross-fetch "3.0.6" debug "^4.1.1" elliptic "^6.5.4" + ethereum-cryptography "1.2.0" fp-ts "2.1.1" io-ts "2.0.1" -"@celo/phone-number-privacy-common@1.0.39": - version "1.0.39" - resolved "https://registry.yarnpkg.com/@celo/phone-number-privacy-common/-/phone-number-privacy-common-1.0.39.tgz#3c9568f70378d24d11afcc4306024c5cf4f8efe9" - integrity sha512-0sbeuoYCN2ZQYO1CryR0Hf9HhOQKuIDZraWFMpUlwrUKk5qKmSMlV16xobG4VL5qUpXHgIRjKPfmcaf0rkrn8A== - dependencies: - "@celo/base" "1.5.2" - "@celo/contractkit" "1.5.2" - "@celo/utils" "1.5.2" +"@celo/phone-number-privacy-common@^3.0.3": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@celo/phone-number-privacy-common/-/phone-number-privacy-common-3.1.0.tgz#df603ce1ff3ccc4297385d28884d1f730ec06743" + integrity sha512-JTnI5tQz7FUN4NR065OXpVDF1p5ByYbf8Xi1KkdnZUidelFmAH7JwjLMTiIhoDYVOv5tv8xB1pffs4lo811E2w== + dependencies: + "@celo/base" "^5.0.4" + "@celo/contractkit" "^5.0.4" + "@celo/phone-utils" "^5.0.4" + "@celo/utils" "^5.0.4" + "@opentelemetry/api" "^1.4.1" + "@opentelemetry/auto-instrumentations-node" "^0.38.0" + "@opentelemetry/propagator-ot-trace" "^0.27.0" + "@opentelemetry/sdk-metrics" "^1.15.1" + "@opentelemetry/sdk-node" "^0.41.1" + "@opentelemetry/sdk-trace-web" "^1.15.1" + "@opentelemetry/semantic-conventions" "^1.15.1" + "@types/bunyan" "1.8.8" bignumber.js "^9.0.0" - blind-threshold-bls "https://github.com/celo-org/blind-threshold-bls-wasm#e1e2f8a" - btoa "1.2.1" bunyan "1.8.12" bunyan-debug-stream "2.0.0" bunyan-gke-stackdriver "0.1.2" dotenv "^8.2.0" elliptic "^6.5.4" + express "^4.17.6" + fp-ts "2.1.1" + io-ts "2.0.1" is-base64 "^1.1.0" - -"@celo/poprf@^0.1.9": - version "0.1.9" - resolved "https://registry.yarnpkg.com/@celo/poprf/-/poprf-0.1.9.tgz#38c514ce0f572b80edeb9dc280b6cf5e9d7c2a75" - integrity sha512-+993EA/W+TBCZyY5G0B2EVdXnPX6t2AldgRAIMaT9WIqTwZKi/TcdJDUQl8mj7HEHMPHlpgCBOVgaHkUcwo/5A== + node-fetch "^2.6.9" "@celo/typechain-target-web3-v1-celo@0.2.0": version "0.2.0" @@ -1014,65 +998,49 @@ lodash "~4.17.19" typechain "2.0.0" -"@celo/utils@1.5.2", "@celo/utils@^1.2.0": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@celo/utils/-/utils-1.5.2.tgz#ddb7f3b50c801225ab41d2355fbe010976329099" - integrity sha512-JyKjuVMbdkyFOb1TpQw6zqamPQWYg7I9hOnva3MeIcQ3ZrJIaNHx0/I+JXFjuu3YYBc1mG8nXp2uPJJTGrwzCQ== +"@celo/utils@5.0.4": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@celo/utils/-/utils-5.0.4.tgz#e2a4789254e464e86c9e37a793708c8b589ce2d8" + integrity sha512-cvIIB/XrLvs8gQAsU5hoM2RKVdY8iYWjYQDA0xZiqF3NQSBDNslCDiYLZuPBjZqvfF4sHsQp2wMsLvWbVOumVg== dependencies: - "@celo/base" "1.5.2" - "@types/country-data" "^0.0.0" + "@celo/base" "5.0.4" + "@ethereumjs/util" "8.0.5" + "@types/bn.js" "^5.1.0" "@types/elliptic" "^6.4.9" - "@types/ethereumjs-util" "^5.2.0" - "@types/google-libphonenumber" "^7.4.17" - "@types/lodash" "^4.14.170" - "@types/node" "^10.12.18" - "@types/randombytes" "^2.0.0" - bigi "^1.1.0" + "@types/node" "^18.7.16" bignumber.js "^9.0.0" - bip32 "2.0.5" - bip39 "https://github.com/bitcoinjs/bip39#d8ea080a18b40f301d4e2219a2991cd2417e83c2" - bls12377js "https://github.com/celo-org/bls12377js#cb38a4cfb643c778619d79b20ca3e5283a2122a6" - bn.js "4.11.8" - buffer-reverse "^1.0.1" - country-data "^0.0.31" - crypto-js "^3.1.9-1" elliptic "^6.5.4" - ethereumjs-util "^5.2.0" - fp-ts "2.1.1" - google-libphonenumber "^3.2.15" + ethereum-cryptography "1.2.0" io-ts "2.0.1" - keccak256 "^1.0.0" - lodash "^4.17.21" - numeral "^2.0.6" - web3-eth-abi "1.3.6" - web3-utils "1.3.6" + web3-eth-abi "1.10.0" + web3-utils "1.10.0" -"@celo/wallet-base@1.5.2": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@celo/wallet-base/-/wallet-base-1.5.2.tgz#ae8df425bf3c702277bb1b63a761a2ec8429e7aa" - integrity sha512-NYJu7OtSRFpGcvSMl2Wc8zN32S6oTkAzKqhH7rXisQ0I2q4yNwCzoquzPVYB0G2UVUFKuuxgsA5V+Zda/LQCyw== +"@celo/wallet-base@5.0.4": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@celo/wallet-base/-/wallet-base-5.0.4.tgz#0d46b1c9b0ac386c9ac9f04e90d000b856aadb90" + integrity sha512-GtNd4WVUHwI5AZtUV5Ky9c+f6dQ/iZUPvU1Ur8BUa/6q1KXp9C0G2TD3wU3Xvm9VxgB5TzTZEuvxvT7L3nsclA== dependencies: - "@celo/base" "1.5.2" - "@celo/connect" "1.5.2" - "@celo/utils" "1.5.2" + "@celo/base" "5.0.4" + "@celo/connect" "5.0.4" + "@celo/utils" "5.0.4" + "@ethereumjs/util" "8.0.5" "@types/debug" "^4.1.5" - "@types/ethereumjs-util" "^5.2.0" bignumber.js "^9.0.0" debug "^4.1.1" eth-lib "^0.2.8" - ethereumjs-util "^5.2.0" + ethereum-cryptography "^2.1.2" + web3-eth-accounts "1.10.0" -"@celo/wallet-local@1.5.2": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@celo/wallet-local/-/wallet-local-1.5.2.tgz#66ea5fb763e19724309e3d56f312f1a342e12b91" - integrity sha512-Aas4SwqQc8ap0OFAOZc+jBR4cXr20V9AReHNEI8Y93R3g1+RlSEJ1Zmsu4vN+Rriz58YqgMnr+pihorw8QydFQ== +"@celo/wallet-local@5.0.4": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@celo/wallet-local/-/wallet-local-5.0.4.tgz#be487ca4750e4e974e461097197eed17aac91627" + integrity sha512-4c1+0PC/NrTBg6fNZebpjik0T4cp2Rmaf8OD7dbeFxghOC6duJvtXNjgmm47Xle91bJ0rSXUKsyFWELqa2RGxQ== dependencies: - "@celo/connect" "1.5.2" - "@celo/utils" "1.5.2" - "@celo/wallet-base" "1.5.2" - "@types/ethereumjs-util" "^5.2.0" + "@celo/connect" "5.0.4" + "@celo/utils" "5.0.4" + "@celo/wallet-base" "5.0.4" + "@ethereumjs/util" "8.0.5" eth-lib "^0.2.8" - ethereumjs-util "^5.2.0" "@chainsafe/as-sha256@^0.3.1": version "0.3.1" @@ -1095,10 +1063,205 @@ "@chainsafe/persistent-merkle-tree" "^0.4.2" case "^1.6.3" -"@colors/colors@1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" - integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== +"@changesets/apply-release-plan@^6.1.4": + version "6.1.4" + resolved "https://registry.yarnpkg.com/@changesets/apply-release-plan/-/apply-release-plan-6.1.4.tgz#09293256090737ecd2f683842d6d732034a5e3c8" + integrity sha512-FMpKF1fRlJyCZVYHr3CbinpZZ+6MwvOtWUuO8uo+svcATEoc1zRDcj23pAurJ2TZ/uVz1wFHH6K3NlACy0PLew== + dependencies: + "@babel/runtime" "^7.20.1" + "@changesets/config" "^2.3.1" + "@changesets/get-version-range-type" "^0.3.2" + "@changesets/git" "^2.0.0" + "@changesets/types" "^5.2.1" + "@manypkg/get-packages" "^1.1.3" + detect-indent "^6.0.0" + fs-extra "^7.0.1" + lodash.startcase "^4.4.0" + outdent "^0.5.0" + prettier "^2.7.1" + resolve-from "^5.0.0" + semver "^7.5.3" + +"@changesets/assemble-release-plan@^5.2.4": + version "5.2.4" + resolved "https://registry.yarnpkg.com/@changesets/assemble-release-plan/-/assemble-release-plan-5.2.4.tgz#d42fd63f4297a2e630e8e0a49f07d4ff5f5ef7bc" + integrity sha512-xJkWX+1/CUaOUWTguXEbCDTyWJFECEhmdtbkjhn5GVBGxdP/JwaHBIU9sW3FR6gD07UwZ7ovpiPclQZs+j+mvg== + dependencies: + "@babel/runtime" "^7.20.1" + "@changesets/errors" "^0.1.4" + "@changesets/get-dependents-graph" "^1.3.6" + "@changesets/types" "^5.2.1" + "@manypkg/get-packages" "^1.1.3" + semver "^7.5.3" + +"@changesets/changelog-git@^0.1.14": + version "0.1.14" + resolved "https://registry.yarnpkg.com/@changesets/changelog-git/-/changelog-git-0.1.14.tgz#852caa7727dcf91497c131d05bc2cd6248532ada" + integrity sha512-+vRfnKtXVWsDDxGctOfzJsPhaCdXRYoe+KyWYoq5X/GqoISREiat0l3L8B0a453B2B4dfHGcZaGyowHbp9BSaA== + dependencies: + "@changesets/types" "^5.2.1" + +"@changesets/cli@^2.26.2": + version "2.26.2" + resolved "https://registry.yarnpkg.com/@changesets/cli/-/cli-2.26.2.tgz#8914dd6ef3ea425a7d5935f6c35a8b7ccde54e45" + integrity sha512-dnWrJTmRR8bCHikJHl9b9HW3gXACCehz4OasrXpMp7sx97ECuBGGNjJhjPhdZNCvMy9mn4BWdplI323IbqsRig== + dependencies: + "@babel/runtime" "^7.20.1" + "@changesets/apply-release-plan" "^6.1.4" + "@changesets/assemble-release-plan" "^5.2.4" + "@changesets/changelog-git" "^0.1.14" + "@changesets/config" "^2.3.1" + "@changesets/errors" "^0.1.4" + "@changesets/get-dependents-graph" "^1.3.6" + "@changesets/get-release-plan" "^3.0.17" + "@changesets/git" "^2.0.0" + "@changesets/logger" "^0.0.5" + "@changesets/pre" "^1.0.14" + "@changesets/read" "^0.5.9" + "@changesets/types" "^5.2.1" + "@changesets/write" "^0.2.3" + "@manypkg/get-packages" "^1.1.3" + "@types/is-ci" "^3.0.0" + "@types/semver" "^7.5.0" + ansi-colors "^4.1.3" + chalk "^2.1.0" + enquirer "^2.3.0" + external-editor "^3.1.0" + fs-extra "^7.0.1" + human-id "^1.0.2" + is-ci "^3.0.1" + meow "^6.0.0" + outdent "^0.5.0" + p-limit "^2.2.0" + preferred-pm "^3.0.0" + resolve-from "^5.0.0" + semver "^7.5.3" + spawndamnit "^2.0.0" + term-size "^2.1.0" + tty-table "^4.1.5" + +"@changesets/config@^2.3.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@changesets/config/-/config-2.3.1.tgz#3d4a1dc866c3623375180b30f69fccdf0e3efebf" + integrity sha512-PQXaJl82CfIXddUOppj4zWu+987GCw2M+eQcOepxN5s+kvnsZOwjEJO3DH9eVy+OP6Pg/KFEWdsECFEYTtbg6w== + dependencies: + "@changesets/errors" "^0.1.4" + "@changesets/get-dependents-graph" "^1.3.6" + "@changesets/logger" "^0.0.5" + "@changesets/types" "^5.2.1" + "@manypkg/get-packages" "^1.1.3" + fs-extra "^7.0.1" + micromatch "^4.0.2" + +"@changesets/errors@^0.1.4": + version "0.1.4" + resolved "https://registry.yarnpkg.com/@changesets/errors/-/errors-0.1.4.tgz#f79851746c43679a66b383fdff4c012f480f480d" + integrity sha512-HAcqPF7snsUJ/QzkWoKfRfXushHTu+K5KZLJWPb34s4eCZShIf8BFO3fwq6KU8+G7L5KdtN2BzQAXOSXEyiY9Q== + dependencies: + extendable-error "^0.1.5" + +"@changesets/get-dependents-graph@^1.3.6": + version "1.3.6" + resolved "https://registry.yarnpkg.com/@changesets/get-dependents-graph/-/get-dependents-graph-1.3.6.tgz#5e19e7b0bfbc7dc38e1986eaaa7016ff377ed888" + integrity sha512-Q/sLgBANmkvUm09GgRsAvEtY3p1/5OCzgBE5vX3vgb5CvW0j7CEljocx5oPXeQSNph6FXulJlXV3Re/v3K3P3Q== + dependencies: + "@changesets/types" "^5.2.1" + "@manypkg/get-packages" "^1.1.3" + chalk "^2.1.0" + fs-extra "^7.0.1" + semver "^7.5.3" + +"@changesets/get-release-plan@^3.0.17": + version "3.0.17" + resolved "https://registry.yarnpkg.com/@changesets/get-release-plan/-/get-release-plan-3.0.17.tgz#8aabced2795ffeae864696b60ee3031f8a94c5f3" + integrity sha512-6IwKTubNEgoOZwDontYc2x2cWXfr6IKxP3IhKeK+WjyD6y3M4Gl/jdQvBw+m/5zWILSOCAaGLu2ZF6Q+WiPniw== + dependencies: + "@babel/runtime" "^7.20.1" + "@changesets/assemble-release-plan" "^5.2.4" + "@changesets/config" "^2.3.1" + "@changesets/pre" "^1.0.14" + "@changesets/read" "^0.5.9" + "@changesets/types" "^5.2.1" + "@manypkg/get-packages" "^1.1.3" + +"@changesets/get-version-range-type@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@changesets/get-version-range-type/-/get-version-range-type-0.3.2.tgz#8131a99035edd11aa7a44c341cbb05e668618c67" + integrity sha512-SVqwYs5pULYjYT4op21F2pVbcrca4qA/bAA3FmFXKMN7Y+HcO8sbZUTx3TAy2VXulP2FACd1aC7f2nTuqSPbqg== + +"@changesets/git@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@changesets/git/-/git-2.0.0.tgz#8de57649baf13a86eb669a25fa51bcad5cea517f" + integrity sha512-enUVEWbiqUTxqSnmesyJGWfzd51PY4H7mH9yUw0hPVpZBJ6tQZFMU3F3mT/t9OJ/GjyiM4770i+sehAn6ymx6A== + dependencies: + "@babel/runtime" "^7.20.1" + "@changesets/errors" "^0.1.4" + "@changesets/types" "^5.2.1" + "@manypkg/get-packages" "^1.1.3" + is-subdir "^1.1.1" + micromatch "^4.0.2" + spawndamnit "^2.0.0" + +"@changesets/logger@^0.0.5": + version "0.0.5" + resolved "https://registry.yarnpkg.com/@changesets/logger/-/logger-0.0.5.tgz#68305dd5a643e336be16a2369cb17cdd8ed37d4c" + integrity sha512-gJyZHomu8nASHpaANzc6bkQMO9gU/ib20lqew1rVx753FOxffnCrJlGIeQVxNWCqM+o6OOleCo/ivL8UAO5iFw== + dependencies: + chalk "^2.1.0" + +"@changesets/parse@^0.3.16": + version "0.3.16" + resolved "https://registry.yarnpkg.com/@changesets/parse/-/parse-0.3.16.tgz#f8337b70aeb476dc81745ab3294022909bc4a84a" + integrity sha512-127JKNd167ayAuBjUggZBkmDS5fIKsthnr9jr6bdnuUljroiERW7FBTDNnNVyJ4l69PzR57pk6mXQdtJyBCJKg== + dependencies: + "@changesets/types" "^5.2.1" + js-yaml "^3.13.1" + +"@changesets/pre@^1.0.14": + version "1.0.14" + resolved "https://registry.yarnpkg.com/@changesets/pre/-/pre-1.0.14.tgz#9df73999a4d15804da7381358d77bb37b00ddf0f" + integrity sha512-dTsHmxQWEQekHYHbg+M1mDVYFvegDh9j/kySNuDKdylwfMEevTeDouR7IfHNyVodxZXu17sXoJuf2D0vi55FHQ== + dependencies: + "@babel/runtime" "^7.20.1" + "@changesets/errors" "^0.1.4" + "@changesets/types" "^5.2.1" + "@manypkg/get-packages" "^1.1.3" + fs-extra "^7.0.1" + +"@changesets/read@^0.5.9": + version "0.5.9" + resolved "https://registry.yarnpkg.com/@changesets/read/-/read-0.5.9.tgz#a1b63a82b8e9409738d7a0f9cc39b6d7c28cbab0" + integrity sha512-T8BJ6JS6j1gfO1HFq50kU3qawYxa4NTbI/ASNVVCBTsKquy2HYwM9r7ZnzkiMe8IEObAJtUVGSrePCOxAK2haQ== + dependencies: + "@babel/runtime" "^7.20.1" + "@changesets/git" "^2.0.0" + "@changesets/logger" "^0.0.5" + "@changesets/parse" "^0.3.16" + "@changesets/types" "^5.2.1" + chalk "^2.1.0" + fs-extra "^7.0.1" + p-filter "^2.1.0" + +"@changesets/types@^4.0.1": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@changesets/types/-/types-4.1.0.tgz#fb8f7ca2324fd54954824e864f9a61a82cb78fe0" + integrity sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw== + +"@changesets/types@^5.2.1": + version "5.2.1" + resolved "https://registry.yarnpkg.com/@changesets/types/-/types-5.2.1.tgz#a228c48004aa8a93bce4be2d1d31527ef3bf21f6" + integrity sha512-myLfHbVOqaq9UtUKqR/nZA/OY7xFjQMdfgfqeZIBK4d0hA6pgxArvdv8M+6NUzzBsjWLOtvApv8YHr4qM+Kpfg== + +"@changesets/write@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@changesets/write/-/write-0.2.3.tgz#baf6be8ada2a67b9aba608e251bfea4fdc40bc63" + integrity sha512-Dbamr7AIMvslKnNYsLFafaVORx4H0pvCA2MHqgtNCySMe1blImEyAEOzDmcgKAkgz4+uwoLz7demIrX+JBr/Xw== + dependencies: + "@babel/runtime" "^7.20.1" + "@changesets/types" "^5.2.1" + fs-extra "^7.0.1" + human-id "^1.0.2" + prettier "^2.7.1" "@cspotcode/source-map-support@^0.8.0": version "0.8.1" @@ -1107,15 +1270,6 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@dabh/diagnostics@^2.0.2": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.3.tgz#7f7e97ee9a725dffc7808d93668cc984e1dc477a" - integrity sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA== - dependencies: - colorspace "1.1.x" - enabled "2.0.x" - kuler "^2.0.0" - "@ensdomains/address-encoder@^0.1.7": version "0.1.9" resolved "https://registry.yarnpkg.com/@ensdomains/address-encoder/-/address-encoder-0.1.9.tgz#f948c485443d9ef7ed2c0c4790e931c33334d02d" @@ -1246,6 +1400,11 @@ resolved "https://registry.yarnpkg.com/@ethereumjs/rlp/-/rlp-4.0.1.tgz#626fabfd9081baab3d0a3074b0c7ecaf674aaa41" integrity sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw== +"@ethereumjs/rlp@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/rlp/-/rlp-5.0.0.tgz#dd81b32b2237bc32fb1b54534f8ff246a6c89d9b" + integrity sha512-WuS1l7GJmB0n0HsXLozCoEFc9IwYgf3l0gCkKVYgR67puVF1O4OpEaN0hWmm1c+iHUHFCKt1hJrvy5toLg+6ag== + "@ethereumjs/statemanager@1.0.4": version "1.0.4" resolved "https://registry.yarnpkg.com/@ethereumjs/statemanager/-/statemanager-1.0.4.tgz#02fb3f05794a2476a78d73660745e9815b476e02" @@ -1315,6 +1474,15 @@ "@ethereumjs/rlp" "^4.0.1" ethereum-cryptography "^1.1.2" +"@ethereumjs/util@^8.1.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/util/-/util-8.1.0.tgz#299df97fb6b034e0577ce9f94c7d9d1004409ed4" + integrity sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA== + dependencies: + "@ethereumjs/rlp" "^4.0.1" + ethereum-cryptography "^2.0.0" + micro-ftch "^0.3.1" + "@ethereumjs/vm@npm:@soloseng/ethereumjs-vm@6.4.1": version "6.4.1" resolved "https://registry.yarnpkg.com/@soloseng/ethereumjs-vm/-/ethereumjs-vm-6.4.1.tgz#3410be54e4c26452f139aee5c99de277ad0dba2f" @@ -1334,21 +1502,6 @@ mcl-wasm "^0.7.1" rustbn.js "~0.2.0" -"@ethersproject/abi@5.0.7": - version "5.0.7" - resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.0.7.tgz#79e52452bd3ca2956d0e1c964207a58ad1a0ee7b" - integrity sha512-Cqktk+hSIckwP/W8O47Eef60VwmoSC/L3lY0+dIBhQPCNn9E4V7rwmm2aFrNRRDJfFlGuZ1khkQUOc3oBX+niw== - dependencies: - "@ethersproject/address" "^5.0.4" - "@ethersproject/bignumber" "^5.0.7" - "@ethersproject/bytes" "^5.0.4" - "@ethersproject/constants" "^5.0.4" - "@ethersproject/hash" "^5.0.4" - "@ethersproject/keccak256" "^5.0.3" - "@ethersproject/logger" "^5.0.5" - "@ethersproject/properties" "^5.0.3" - "@ethersproject/strings" "^5.0.4" - "@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.0.0-beta.146", "@ethersproject/abi@^5.6.3", "@ethersproject/abi@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" @@ -1388,7 +1541,7 @@ "@ethersproject/logger" "^5.7.0" "@ethersproject/properties" "^5.7.0" -"@ethersproject/address@5.7.0", "@ethersproject/address@^5.0.4", "@ethersproject/address@^5.7.0": +"@ethersproject/address@5.7.0", "@ethersproject/address@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37" integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA== @@ -1414,7 +1567,7 @@ "@ethersproject/bytes" "^5.7.0" "@ethersproject/properties" "^5.7.0" -"@ethersproject/bignumber@5.7.0", "@ethersproject/bignumber@^5.0.7", "@ethersproject/bignumber@^5.7.0": +"@ethersproject/bignumber@5.7.0", "@ethersproject/bignumber@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.7.0.tgz#e2f03837f268ba655ffba03a57853e18a18dc9c2" integrity sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw== @@ -1423,14 +1576,14 @@ "@ethersproject/logger" "^5.7.0" bn.js "^5.2.1" -"@ethersproject/bytes@5.7.0", "@ethersproject/bytes@^5.0.4", "@ethersproject/bytes@^5.7.0": +"@ethersproject/bytes@5.7.0", "@ethersproject/bytes@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d" integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A== dependencies: "@ethersproject/logger" "^5.7.0" -"@ethersproject/constants@5.7.0", "@ethersproject/constants@^5.0.4", "@ethersproject/constants@^5.7.0": +"@ethersproject/constants@5.7.0", "@ethersproject/constants@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.7.0.tgz#df80a9705a7e08984161f09014ea012d1c75295e" integrity sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA== @@ -1453,7 +1606,7 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/transactions" "^5.7.0" -"@ethersproject/hash@5.7.0", "@ethersproject/hash@^5.0.4", "@ethersproject/hash@^5.7.0": +"@ethersproject/hash@5.7.0", "@ethersproject/hash@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7" integrity sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g== @@ -1505,7 +1658,7 @@ aes-js "3.0.0" scrypt-js "3.0.1" -"@ethersproject/keccak256@5.7.0", "@ethersproject/keccak256@^5.0.3", "@ethersproject/keccak256@^5.7.0": +"@ethersproject/keccak256@5.7.0", "@ethersproject/keccak256@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.7.0.tgz#3186350c6e1cd6aba7940384ec7d6d9db01f335a" integrity sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg== @@ -1513,7 +1666,7 @@ "@ethersproject/bytes" "^5.7.0" js-sha3 "0.8.0" -"@ethersproject/logger@5.7.0", "@ethersproject/logger@^5.0.5", "@ethersproject/logger@^5.7.0": +"@ethersproject/logger@5.7.0", "@ethersproject/logger@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892" integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig== @@ -1533,7 +1686,7 @@ "@ethersproject/bytes" "^5.7.0" "@ethersproject/sha2" "^5.7.0" -"@ethersproject/properties@5.7.0", "@ethersproject/properties@^5.0.3", "@ethersproject/properties@^5.7.0": +"@ethersproject/properties@5.7.0", "@ethersproject/properties@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.7.0.tgz#a6e12cb0439b878aaf470f1902a176033067ed30" integrity sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw== @@ -1615,7 +1768,7 @@ "@ethersproject/sha2" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@ethersproject/strings@5.7.0", "@ethersproject/strings@^5.0.4", "@ethersproject/strings@^5.7.0": +"@ethersproject/strings@5.7.0", "@ethersproject/strings@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.7.0.tgz#54c9d2a7c57ae8f1205c88a9d3a56471e14d5ed2" integrity sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg== @@ -1624,7 +1777,7 @@ "@ethersproject/constants" "^5.7.0" "@ethersproject/logger" "^5.7.0" -"@ethersproject/transactions@5.7.0", "@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.6.2", "@ethersproject/transactions@^5.7.0": +"@ethersproject/transactions@5.7.0", "@ethersproject/transactions@^5.6.2", "@ethersproject/transactions@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.7.0.tgz#91318fc24063e057885a6af13fdb703e1f993d3b" integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ== @@ -1691,82 +1844,6 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@firebase/app-types@0.6.3": - version "0.6.3" - resolved "https://registry.yarnpkg.com/@firebase/app-types/-/app-types-0.6.3.tgz#3f10514786aad846d74cd63cb693556309918f4b" - integrity sha512-/M13DPPati7FQHEQ9Minjk1HGLm/4K4gs9bR4rzLCWJg64yGtVC0zNg9gDpkw9yc2cvol/mNFxqTtd4geGrwdw== - -"@firebase/app-types@0.7.0": - version "0.7.0" - resolved "https://registry.yarnpkg.com/@firebase/app-types/-/app-types-0.7.0.tgz#c9e16d1b8bed1a991840b8d2a725fb58d0b5899f" - integrity sha512-6fbHQwDv2jp/v6bXhBw2eSRbNBpxHcd1NBF864UksSMVIqIyri9qpJB1Mn6sGZE+bnDsSQBC5j2TbMxYsJQkQg== - -"@firebase/auth-interop-types@0.1.6": - version "0.1.6" - resolved "https://registry.yarnpkg.com/@firebase/auth-interop-types/-/auth-interop-types-0.1.6.tgz#5ce13fc1c527ad36f1bb1322c4492680a6cf4964" - integrity sha512-etIi92fW3CctsmR9e3sYM3Uqnoq861M0Id9mdOPF6PWIg38BXL5k4upCNBggGUpLIS0H1grMOvy/wn1xymwe2g== - -"@firebase/component@0.5.13": - version "0.5.13" - resolved "https://registry.yarnpkg.com/@firebase/component/-/component-0.5.13.tgz#65a382e83bddd109380c9aa1f280791b1b4567c4" - integrity sha512-hxhJtpD8Ppf/VU2Rlos6KFCEV77TGIGD5bJlkPK1+B/WUe0mC6dTjW7KhZtXTc+qRBp9nFHWcsIORnT8liHP9w== - dependencies: - "@firebase/util" "1.5.2" - tslib "^2.1.0" - -"@firebase/database-compat@^0.1.1": - version "0.1.8" - resolved "https://registry.yarnpkg.com/@firebase/database-compat/-/database-compat-0.1.8.tgz#ab627f2bdbe94367f515d5bded880c86886bbd28" - integrity sha512-dhXr5CSieBuKNdU96HgeewMQCT9EgOIkfF1GNy+iRrdl7BWLxmlKuvLfK319rmIytSs/vnCzcD9uqyxTeU/A3A== - dependencies: - "@firebase/component" "0.5.13" - "@firebase/database" "0.12.8" - "@firebase/database-types" "0.9.7" - "@firebase/logger" "0.3.2" - "@firebase/util" "1.5.2" - tslib "^2.1.0" - -"@firebase/database-types@0.9.7": - version "0.9.7" - resolved "https://registry.yarnpkg.com/@firebase/database-types/-/database-types-0.9.7.tgz#c5ee0ea9bb2703a13c1c47fe880fc577d5ce7f33" - integrity sha512-EFhgL89Fz6DY3kkB8TzdHvdu8XaqqvzcF2DLVOXEnQ3Ms7L755p5EO42LfxXoJqb9jKFvgLpFmKicyJG25WFWw== - dependencies: - "@firebase/app-types" "0.7.0" - "@firebase/util" "1.5.2" - -"@firebase/database-types@^0.7.2": - version "0.7.3" - resolved "https://registry.yarnpkg.com/@firebase/database-types/-/database-types-0.7.3.tgz#819f16dd4c767c864b460004458620f265a3f735" - integrity sha512-dSOJmhKQ0nL8O4EQMRNGpSExWCXeHtH57gGg0BfNAdWcKhC8/4Y+qfKLfWXzyHvrSecpLmO0SmAi/iK2D5fp5A== - dependencies: - "@firebase/app-types" "0.6.3" - -"@firebase/database@0.12.8": - version "0.12.8" - resolved "https://registry.yarnpkg.com/@firebase/database/-/database-0.12.8.tgz#11a1b6752ba0614892af15c71958e00ce16f5824" - integrity sha512-JBQVfFLzfhxlQbl4OU6ov9fdsddkytBQdtSSR49cz48homj38ccltAhK6seum+BI7f28cV2LFHF9672lcN+qxA== - dependencies: - "@firebase/auth-interop-types" "0.1.6" - "@firebase/component" "0.5.13" - "@firebase/logger" "0.3.2" - "@firebase/util" "1.5.2" - faye-websocket "0.11.4" - tslib "^2.1.0" - -"@firebase/logger@0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@firebase/logger/-/logger-0.3.2.tgz#5046ffa8295c577846d54b6ca95645a03809800e" - integrity sha512-lzLrcJp9QBWpo40OcOM9B8QEtBw2Fk1zOZQdvv+rWS6gKmhQBCEMc4SMABQfWdjsylBcDfniD1Q+fUX1dcBTXA== - dependencies: - tslib "^2.1.0" - -"@firebase/util@1.5.2": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@firebase/util/-/util-1.5.2.tgz#bdd2bc11c956a8a6a0fa25fbd752a13e033558bc" - integrity sha512-YvBH2UxFcdWG2HdFnhxZptPl2eVFlpOyTH66iDo13JPEYraWzWToZ5AMTtkyRHVmu7sssUpQlU9igy1KET7TOw== - dependencies: - tslib "^2.1.0" - "@ganache/console.log@0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@ganache/console.log/-/console.log-0.2.0.tgz#32ea0df806ed735d61bd0537d7b7fc350e511479" @@ -1794,7 +1871,7 @@ optionalDependencies: "@trufflesuite/bigint-buffer" "1.1.9" -"@gar/promisify@^1.0.1", "@gar/promisify@^1.1.3": +"@gar/promisify@^1.1.3": version "1.1.3" resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== @@ -1816,16 +1893,6 @@ retry-request "^4.0.0" teeny-request "^3.11.3" -"@google-cloud/firestore@^4.5.0": - version "4.15.1" - resolved "https://registry.yarnpkg.com/@google-cloud/firestore/-/firestore-4.15.1.tgz#ed764fc76823ce120e68fe8c27ef1edd0650cd93" - integrity sha512-2PWsCkEF1W02QbghSeRsNdYKN1qavrHBP3m72gPDMHQSYrGULOaTi7fSJquQmAtc4iPVB2/x6h80rdLHTATQtA== - dependencies: - fast-deep-equal "^3.1.1" - functional-red-black-tree "^1.0.1" - google-gax "^2.24.1" - protobufjs "^6.8.6" - "@google-cloud/kms@~2.9.0": version "2.9.0" resolved "https://registry.yarnpkg.com/@google-cloud/kms/-/kms-2.9.0.tgz#3f7e51a3e30795a69ded4ea4f96609a59b503001" @@ -1851,44 +1918,21 @@ split-array-stream "^2.0.0" stream-events "^1.0.4" -"@google-cloud/paginator@^3.0.6", "@google-cloud/paginator@^3.0.7": - version "3.0.7" - resolved "https://registry.yarnpkg.com/@google-cloud/paginator/-/paginator-3.0.7.tgz#fb6f8e24ec841f99defaebf62c75c2e744dd419b" - integrity sha512-jJNutk0arIQhmpUUQJPJErsojqo834KcyB6X7a1mxuic8i1tKXxde8E69IZxNZawRIlZdIK2QY4WALvlK5MzYQ== - dependencies: - arrify "^2.0.0" - extend "^3.0.2" - "@google-cloud/precise-date@^0.1.0": version "0.1.0" resolved "https://registry.yarnpkg.com/@google-cloud/precise-date/-/precise-date-0.1.0.tgz#02ccda04b4413fa64f098fc93db51e95af5c855a" integrity sha512-nXt4AskYjmDLRIO+nquVVppjiLE5ficFRP3WF1JYtPnSRFRpuMusa1kysPsD/yOxt5NMmvlkUCkaFI4rHYeckQ== -"@google-cloud/precise-date@^2.0.0": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@google-cloud/precise-date/-/precise-date-2.0.4.tgz#930b0cbf557ef3a4bfeeb121cfc6da341212a2cb" - integrity sha512-nOB+mZdevI/1Si0QAfxWfzzIqFdc7wrO+DYePFvgbOoMtvX+XfFTINNt7e9Zg66AbDbWCPRnikU+6f5LTm9Wyg== - "@google-cloud/projectify@^0.3.0", "@google-cloud/projectify@^0.3.3": version "0.3.3" resolved "https://registry.yarnpkg.com/@google-cloud/projectify/-/projectify-0.3.3.tgz#bde9103d50b20a3ea3337df8c6783a766e70d41d" integrity sha512-7522YHQ4IhaafgSunsFF15nG0TGVmxgXidy9cITMe+256RgqfcrfWphiMufW+Ou4kqagW/u3yxwbzVEW3dk2Uw== -"@google-cloud/projectify@^2.0.0": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@google-cloud/projectify/-/projectify-2.1.1.tgz#ae6af4fee02d78d044ae434699a630f8df0084ef" - integrity sha512-+rssMZHnlh0twl122gXY4/aCrk0G1acBqkHFfYddtsqpYXGxA29nj9V5V9SfC+GyOG00l650f6lG9KL+EpFEWQ== - "@google-cloud/promisify@^0.4.0": version "0.4.0" resolved "https://registry.yarnpkg.com/@google-cloud/promisify/-/promisify-0.4.0.tgz#4fbfcf4d85bb6a2e4ccf05aa63d2b10d6c9aad9b" integrity sha512-4yAHDC52TEMCNcMzVC8WlqnKKKq+Ssi2lXoUg9zWWkZ6U6tq9ZBRYLHHCRdfU+EU9YJsVmivwGcKYCjRGjnf4Q== -"@google-cloud/promisify@^2.0.0": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@google-cloud/promisify/-/promisify-2.0.4.tgz#9d8705ecb2baa41b6b2673f3a8e9b7b7e1abc52a" - integrity sha512-j8yRSSqswWi1QqUGKVEKOG03Q7qOoZP6/h2zN2YO+F5h2+DHU0bSrHCK9Y7lo2DI9fBd8qGAw795sf+3Jva4yA== - "@google-cloud/pubsub@^0.28.1": version "0.28.1" resolved "https://registry.yarnpkg.com/@google-cloud/pubsub/-/pubsub-0.28.1.tgz#8d0605e155f5a8c36f7b51363c1e139f534b5fd8" @@ -1913,27 +1957,6 @@ p-defer "^1.0.0" protobufjs "^6.8.1" -"@google-cloud/pubsub@^2.7.0": - version "2.19.4" - resolved "https://registry.yarnpkg.com/@google-cloud/pubsub/-/pubsub-2.19.4.tgz#6a52f2fe7458dc13064863024a4f9941567c0536" - integrity sha512-+aZxq6N5XGarQS3xGXjKSRFy4TB+3PMpI0CBmSrcC59g3TB5nmwps3pv/KkdLa0Cd+CPHDdfrEW1uSrGBMLICw== - dependencies: - "@google-cloud/paginator" "^3.0.6" - "@google-cloud/precise-date" "^2.0.0" - "@google-cloud/projectify" "^2.0.0" - "@google-cloud/promisify" "^2.0.0" - "@opentelemetry/api" "^1.0.0" - "@opentelemetry/semantic-conventions" "^1.0.0" - "@types/duplexify" "^3.6.0" - "@types/long" "^4.0.0" - arrify "^2.0.0" - extend "^3.0.2" - google-auth-library "^7.0.0" - google-gax "2.30.3" - is-stream-ended "^0.1.4" - lodash.snakecase "^4.1.1" - p-defer "^3.0.0" - "@google-cloud/secret-manager@3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@google-cloud/secret-manager/-/secret-manager-3.0.0.tgz#31842287bd0eee380210488ae40cae3732b2b32f" @@ -1968,35 +1991,6 @@ through2 "^3.0.0" xdg-basedir "^3.0.0" -"@google-cloud/storage@^5.3.0": - version "5.20.5" - resolved "https://registry.yarnpkg.com/@google-cloud/storage/-/storage-5.20.5.tgz#1de71fc88d37934a886bc815722c134b162d335d" - integrity sha512-lOs/dCyveVF8TkVFnFSF7IGd0CJrTm91qiK6JLu+Z8qiT+7Ag0RyVhxZIWkhiACqwABo7kSHDm8FdH8p2wxSSw== - dependencies: - "@google-cloud/paginator" "^3.0.7" - "@google-cloud/projectify" "^2.0.0" - "@google-cloud/promisify" "^2.0.0" - abort-controller "^3.0.0" - arrify "^2.0.0" - async-retry "^1.3.3" - compressible "^2.0.12" - configstore "^5.0.0" - duplexify "^4.0.0" - ent "^2.2.0" - extend "^3.0.2" - gaxios "^4.0.0" - google-auth-library "^7.14.1" - hash-stream-validation "^0.2.2" - mime "^3.0.0" - mime-types "^2.0.8" - p-limit "^3.0.1" - pumpify "^2.0.0" - retry-request "^4.2.2" - stream-events "^1.0.4" - teeny-request "^7.1.3" - uuid "^8.0.0" - xdg-basedir "^4.0.0" - "@graphql-tools/batch-execute@8.5.1": version "8.5.1" resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-8.5.1.tgz#fa3321d58c64041650be44250b1ebc3aab0ba7a9" @@ -2092,6 +2086,14 @@ dependencies: semver "^5.5.0" +"@grpc/grpc-js@^1.7.1": + version "1.9.7" + resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.9.7.tgz#7d0e29bc162287bee2523901c9bc9320d8402397" + integrity sha512-yMaA/cIsRhGzW3ymCNpdlPcInXcovztlgu/rirThj2b87u3RzWUszliOqZ/pldy7yhmJPS8uwog+kZSTa4A0PQ== + dependencies: + "@grpc/proto-loader" "^0.7.8" + "@types/node" ">=12.12.47" + "@grpc/grpc-js@~1.6.0": version "1.6.12" resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.6.12.tgz#20f710d8a8c5c396b2ae9530ba6c06b984614fdf" @@ -2100,17 +2102,6 @@ "@grpc/proto-loader" "^0.7.0" "@types/node" ">=12.12.47" -"@grpc/proto-loader@0.6.9": - version "0.6.9" - resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.6.9.tgz#4014eef366da733f8e04a9ddd7376fe8a58547b7" - integrity sha512-UlcCS8VbsU9d3XTXGiEVFonN7hXk+oMXZtoHHG2oSA1/GcDP1q6OUgs20PzHDGizzyi8ufGSUDlk3O2NyY7leg== - dependencies: - "@types/long" "^4.0.1" - lodash.camelcase "^4.3.0" - long "^4.0.0" - protobufjs "^6.10.0" - yargs "^16.2.0" - "@grpc/proto-loader@^0.4.0": version "0.4.0" resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.4.0.tgz#a823a51eb2fde58369bef1deb5445fd808d70901" @@ -2141,6 +2132,87 @@ protobufjs "^7.0.0" yargs "^16.2.0" +"@grpc/proto-loader@^0.7.8": + version "0.7.10" + resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.7.10.tgz#6bf26742b1b54d0a473067743da5d3189d06d720" + integrity sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ== + dependencies: + lodash.camelcase "^4.3.0" + long "^5.0.0" + protobufjs "^7.2.4" + yargs "^17.7.2" + +"@hapi/b64@5.x.x": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@hapi/b64/-/b64-5.0.0.tgz#b8210cbd72f4774985e78569b77e97498d24277d" + integrity sha512-ngu0tSEmrezoiIaNGG6rRvKOUkUuDdf4XTPnONHGYfSGRmDqPZX5oJL6HAdKTo1UQHECbdB4OzhWrfgVppjHUw== + dependencies: + "@hapi/hoek" "9.x.x" + +"@hapi/boom@9.x.x", "@hapi/boom@^9.0.0": + version "9.1.4" + resolved "https://registry.yarnpkg.com/@hapi/boom/-/boom-9.1.4.tgz#1f9dad367c6a7da9f8def24b4a986fc5a7bd9db6" + integrity sha512-Ls1oH8jaN1vNsqcaHVYJrKmgMcKsC1wcp8bujvXrHaAqD2iDYq3HoOwsxwo09Cuda5R5nC0o0IxlrlTuvPuzSw== + dependencies: + "@hapi/hoek" "9.x.x" + +"@hapi/bourne@2.x.x": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@hapi/bourne/-/bourne-2.1.0.tgz#66aff77094dc3080bd5df44ec63881f2676eb020" + integrity sha512-i1BpaNDVLJdRBEKeJWkVO6tYX6DMFBuwMhSuWqLsY4ufeTKGVuV5rBsUhxPayXqnnWHgXUAmWK16H/ykO5Wj4Q== + +"@hapi/cryptiles@5.x.x": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@hapi/cryptiles/-/cryptiles-5.1.0.tgz#655de4cbbc052c947f696148c83b187fc2be8f43" + integrity sha512-fo9+d1Ba5/FIoMySfMqPBR/7Pa29J2RsiPrl7bkwo5W5o+AN1dAYQRi4SPrPwwVxVGKjgLOEWrsvt1BonJSfLA== + dependencies: + "@hapi/boom" "9.x.x" + +"@hapi/hoek@9.x.x", "@hapi/hoek@^9.0.0": + version "9.3.0" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb" + integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ== + +"@hapi/iron@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@hapi/iron/-/iron-6.0.0.tgz#ca3f9136cda655bdd6028de0045da0de3d14436f" + integrity sha512-zvGvWDufiTGpTJPG1Y/McN8UqWBu0k/xs/7l++HVU535NLHXsHhy54cfEMdW7EjwKfbBfM9Xy25FmTiobb7Hvw== + dependencies: + "@hapi/b64" "5.x.x" + "@hapi/boom" "9.x.x" + "@hapi/bourne" "2.x.x" + "@hapi/cryptiles" "5.x.x" + "@hapi/hoek" "9.x.x" + +"@hapi/podium@^4.1.3": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@hapi/podium/-/podium-4.1.3.tgz#91e20838fc2b5437f511d664aabebbb393578a26" + integrity sha512-ljsKGQzLkFqnQxE7qeanvgGj4dejnciErYd30dbrYzUOF/FyS/DOF97qcrT3bhoVwCYmxa6PEMhxfCPlnUcD2g== + dependencies: + "@hapi/hoek" "9.x.x" + "@hapi/teamwork" "5.x.x" + "@hapi/validate" "1.x.x" + +"@hapi/teamwork@5.x.x": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@hapi/teamwork/-/teamwork-5.1.1.tgz#4d2ba3cac19118a36c44bf49a3a47674de52e4e4" + integrity sha512-1oPx9AE5TIv+V6Ih54RP9lTZBso3rP8j4Xhb6iSVwPXtAM+sDopl5TFMv5Paw73UnpZJ9gjcrTE1BXrWt9eQrg== + +"@hapi/topo@^5.0.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" + integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@hapi/validate@1.x.x": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@hapi/validate/-/validate-1.1.3.tgz#f750a07283929e09b51aa16be34affb44e1931ad" + integrity sha512-/XMR0N0wjw0Twzq2pQOzPBZlDzkekGcoCtzO314BpIEsbXdYGthQUbxgkGDf4nhk1+IPDAsXqWjMohRQYO06UA== + dependencies: + "@hapi/hoek" "^9.0.0" + "@hapi/topo" "^5.0.0" + "@hutson/parse-repository-url@^3.0.0": version "3.0.2" resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" @@ -2414,11 +2486,6 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@jsdevtools/ono@^7.1.3": - version "7.1.3" - resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" - integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg== - "@ledgerhq/cryptoassets@^5.53.0": version "5.53.0" resolved "https://registry.yarnpkg.com/@ledgerhq/cryptoassets/-/cryptoassets-5.53.0.tgz#11dcc93211960c6fd6620392e4dd91896aaabe58" @@ -3257,20 +3324,27 @@ npmlog "^6.0.2" write-file-atomic "^4.0.1" -"@mapbox/node-pre-gyp@^1.0.0": - version "1.0.10" - resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz#8e6735ccebbb1581e5a7e652244cadc8a844d03c" - integrity sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA== +"@manypkg/find-root@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@manypkg/find-root/-/find-root-1.1.0.tgz#a62d8ed1cd7e7d4c11d9d52a8397460b5d4ad29f" + integrity sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA== dependencies: - detect-libc "^2.0.0" - https-proxy-agent "^5.0.0" - make-dir "^3.1.0" - node-fetch "^2.6.7" - nopt "^5.0.0" - npmlog "^5.0.1" - rimraf "^3.0.2" - semver "^7.3.5" - tar "^6.1.11" + "@babel/runtime" "^7.5.5" + "@types/node" "^12.7.1" + find-up "^4.1.0" + fs-extra "^8.1.0" + +"@manypkg/get-packages@^1.1.3": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@manypkg/get-packages/-/get-packages-1.1.3.tgz#e184db9bba792fa4693de4658cfb1463ac2c9c47" + integrity sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A== + dependencies: + "@babel/runtime" "^7.5.5" + "@changesets/types" "^4.0.1" + "@manypkg/find-root" "^1.1.0" + fs-extra "^8.1.0" + globby "^11.0.0" + read-yaml-file "^1.1.0" "@metamask/safe-event-emitter@^2.0.0": version "2.0.0" @@ -3285,11 +3359,40 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" +"@noble/curves@1.0.0", "@noble/curves@~1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.0.0.tgz#e40be8c7daf088aaf291887cbc73f43464a92932" + integrity sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw== + dependencies: + "@noble/hashes" "1.3.0" + +"@noble/curves@1.1.0", "@noble/curves@~1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.1.0.tgz#f13fc667c89184bc04cccb9b11e8e7bae27d8c3d" + integrity sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA== + dependencies: + "@noble/hashes" "1.3.1" + "@noble/hashes@1.2.0", "@noble/hashes@~1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12" integrity sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ== +"@noble/hashes@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.0.tgz#085fd70f6d7d9d109671090ccae1d3bec62554a1" + integrity sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg== + +"@noble/hashes@1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.1.tgz#8831ef002114670c603c458ab8b11328406953a9" + integrity sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA== + +"@noble/hashes@~1.3.0", "@noble/hashes@~1.3.1": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" + integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== + "@noble/secp256k1@1.7.1", "@noble/secp256k1@~1.7.0": version "1.7.1" resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" @@ -3361,14 +3464,6 @@ treeverse "^2.0.0" walk-up-path "^1.0.0" -"@npmcli/fs@^1.0.0": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-1.1.1.tgz#72f719fe935e687c56a4faecf3c03d06ba593257" - integrity sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ== - dependencies: - "@gar/promisify" "^1.0.1" - semver "^7.3.5" - "@npmcli/fs@^2.1.0": version "2.1.2" resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-2.1.2.tgz#a9e2541a4a2fec2e69c29b35e6060973da79b865" @@ -3420,14 +3515,6 @@ pacote "^13.0.3" semver "^7.3.5" -"@npmcli/move-file@^1.0.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" - integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg== - dependencies: - mkdirp "^1.0.4" - rimraf "^3.0.2" - "@npmcli/move-file@^2.0.0": version "2.0.1" resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-2.0.1.tgz#26f6bdc379d87f75e55739bab89db525b06100e4" @@ -3683,8 +3770,18 @@ chalk "^4.1.0" tslib "^2.5.0" -"@oclif/plugin-autocomplete@^0.1.5": - version "0.1.5" +"@oclif/parser@^3.8.16": + version "3.8.17" + resolved "https://registry.yarnpkg.com/@oclif/parser/-/parser-3.8.17.tgz#e1ce0f29b22762d752d9da1c7abd57ad81c56188" + integrity sha512-l04iSd0xoh/16TGVpXb81Gg3z7tlQGrEup16BrVLsZBK6SEYpYHRJZnM32BwZrHI97ZSFfuSwVlzoo6HdsaK8A== + dependencies: + "@oclif/errors" "^1.3.6" + "@oclif/linewrap" "^1.0.0" + chalk "^4.1.0" + tslib "^2.6.2" + +"@oclif/plugin-autocomplete@^0.1.5": + version "0.1.5" resolved "https://registry.yarnpkg.com/@oclif/plugin-autocomplete/-/plugin-autocomplete-0.1.5.tgz#b021b50db6d6b0d9e7d2227c58f43906578ae175" integrity sha512-Afchpdd8FNfx9GaU/1D9IzyfiXvjfGybgzQ6G4GTFvPO0/hLdkXX3YyYq+SnxE6/bCrhg4pleiB+GuJACmmkEA== dependencies: @@ -3802,22 +3899,6 @@ resolved "https://registry.yarnpkg.com/@oclif/screen/-/screen-1.0.4.tgz#b740f68609dfae8aa71c3a6cab15d816407ba493" integrity sha512-60CHpq+eqnTxLZQ4PGHYNwUX572hgpMHGPtTWMjdTMsAvlm69lZV/4ly6O3sAYkomo4NggGcomrDpBe34rxUqw== -"@octokit/app@^4.2.0": - version "4.3.0" - resolved "https://registry.yarnpkg.com/@octokit/app/-/app-4.3.0.tgz#67a5380344adeae8b1256b0b29918462b8c55f13" - integrity sha512-TAi6Ju1u1rf7+V1vd2pg70SFwmHmwt5WAaAJ8BPaIHALxKbLpyyKUaVP1DBBmNmgF+fw0dwBR/edrClDMpdDfQ== - dependencies: - "@octokit/request" "^5.0.0" - jsonwebtoken "^8.3.0" - lru-cache "^6.0.0" - -"@octokit/auth-token@^2.4.4": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.5.0.tgz#27c37ea26c205f28443402477ffd261311f21e36" - integrity sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g== - dependencies: - "@octokit/types" "^6.0.3" - "@octokit/auth-token@^3.0.0": version "3.0.3" resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-3.0.3.tgz#ce7e48a3166731f26068d7a7a7996b5da58cbe0c" @@ -3825,19 +3906,6 @@ dependencies: "@octokit/types" "^9.0.0" -"@octokit/core@^3.5.1": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.6.0.tgz#3376cb9f3008d9b3d110370d90e0a1fcd5fe6085" - integrity sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q== - dependencies: - "@octokit/auth-token" "^2.4.4" - "@octokit/graphql" "^4.5.8" - "@octokit/request" "^5.6.3" - "@octokit/request-error" "^2.0.5" - "@octokit/types" "^6.0.3" - before-after-hook "^2.2.0" - universal-user-agent "^6.0.0" - "@octokit/core@^4.1.0": version "4.2.0" resolved "https://registry.yarnpkg.com/@octokit/core/-/core-4.2.0.tgz#8c253ba9605aca605bc46187c34fcccae6a96648" @@ -3851,15 +3919,6 @@ before-after-hook "^2.2.0" universal-user-agent "^6.0.0" -"@octokit/endpoint@^6.0.1": - version "6.0.12" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.12.tgz#3b4d47a4b0e79b1027fb8d75d4221928b2d05658" - integrity sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA== - dependencies: - "@octokit/types" "^6.0.3" - is-plain-object "^5.0.0" - universal-user-agent "^6.0.0" - "@octokit/endpoint@^7.0.0": version "7.0.5" resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-7.0.5.tgz#2bb2a911c12c50f10014183f5d596ce30ac67dd1" @@ -3869,15 +3928,6 @@ is-plain-object "^5.0.0" universal-user-agent "^6.0.0" -"@octokit/graphql@^4.5.8": - version "4.8.0" - resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.8.0.tgz#664d9b11c0e12112cbf78e10f49a05959aa22cc3" - integrity sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg== - dependencies: - "@octokit/request" "^5.6.0" - "@octokit/types" "^6.0.3" - universal-user-agent "^6.0.0" - "@octokit/graphql@^5.0.0": version "5.0.5" resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-5.0.5.tgz#a4cb3ea73f83b861893a6370ee82abb36e81afd2" @@ -3887,11 +3937,6 @@ "@octokit/types" "^9.0.0" universal-user-agent "^6.0.0" -"@octokit/openapi-types@^12.11.0": - version "12.11.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-12.11.0.tgz#da5638d64f2b919bca89ce6602d059f1b52d3ef0" - integrity sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ== - "@octokit/openapi-types@^17.0.0": version "17.0.0" resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-17.0.0.tgz#7356d287f48b20e9a1f497ef8dfaabdff9cf8622" @@ -3902,13 +3947,6 @@ resolved "https://registry.yarnpkg.com/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz#e07896739618dab8da7d4077c658003775f95437" integrity sha512-93uGjlhUD+iNg1iWhUENAtJata6w5nE+V4urXOAlIXdco6xNZtUSfYY8dzp3Udy74aqO/B5UZL80x/YMa5PKRw== -"@octokit/plugin-paginate-rest@^2.16.8": - version "2.21.3" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz#7f12532797775640dbb8224da577da7dc210c87e" - integrity sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw== - dependencies: - "@octokit/types" "^6.40.0" - "@octokit/plugin-paginate-rest@^6.0.0": version "6.0.0" resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.0.0.tgz#f34b5a7d9416019126042cd7d7b811e006c0d561" @@ -3921,14 +3959,6 @@ resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85" integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== -"@octokit/plugin-rest-endpoint-methods@^5.12.0": - version "5.16.2" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz#7ee8bf586df97dd6868cf68f641354e908c25342" - integrity sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw== - dependencies: - "@octokit/types" "^6.39.0" - deprecation "^2.3.1" - "@octokit/plugin-rest-endpoint-methods@^7.0.0": version "7.0.1" resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-7.0.1.tgz#f7ebe18144fd89460f98f35a587b056646e84502" @@ -3937,23 +3967,6 @@ "@octokit/types" "^9.0.0" deprecation "^2.3.1" -"@octokit/plugin-retry@^3.0.3": - version "3.0.9" - resolved "https://registry.yarnpkg.com/@octokit/plugin-retry/-/plugin-retry-3.0.9.tgz#ae625cca1e42b0253049102acd71c1d5134788fe" - integrity sha512-r+fArdP5+TG6l1Rv/C9hVoty6tldw6cE2pRHNGmFPdyfrc696R6JjrQ3d7HdVqGwuzfyrcaLAKD7K8TX8aehUQ== - dependencies: - "@octokit/types" "^6.0.3" - bottleneck "^2.15.3" - -"@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.1.0.tgz#9e150357831bfc788d13a4fd4b1913d60c74d677" - integrity sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg== - dependencies: - "@octokit/types" "^6.0.3" - deprecation "^2.0.0" - once "^1.4.0" - "@octokit/request-error@^3.0.0": version "3.0.3" resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-3.0.3.tgz#ef3dd08b8e964e53e55d471acfe00baa892b9c69" @@ -3963,18 +3976,6 @@ deprecation "^2.0.0" once "^1.4.0" -"@octokit/request@^5.0.0", "@octokit/request@^5.6.0", "@octokit/request@^5.6.3": - version "5.6.3" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.6.3.tgz#19a022515a5bba965ac06c9d1334514eb50c48b0" - integrity sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A== - dependencies: - "@octokit/endpoint" "^6.0.1" - "@octokit/request-error" "^2.1.0" - "@octokit/types" "^6.16.1" - is-plain-object "^5.0.0" - node-fetch "^2.6.7" - universal-user-agent "^6.0.0" - "@octokit/request@^6.0.0": version "6.2.3" resolved "https://registry.yarnpkg.com/@octokit/request/-/request-6.2.3.tgz#76d5d6d44da5c8d406620a4c285d280ae310bdb4" @@ -3987,16 +3988,6 @@ node-fetch "^2.6.7" universal-user-agent "^6.0.0" -"@octokit/rest@^18.0.0": - version "18.12.0" - resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.12.0.tgz#f06bc4952fc87130308d810ca9d00e79f6988881" - integrity sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q== - dependencies: - "@octokit/core" "^3.5.1" - "@octokit/plugin-paginate-rest" "^2.16.8" - "@octokit/plugin-request-log" "^1.0.4" - "@octokit/plugin-rest-endpoint-methods" "^5.12.0" - "@octokit/rest@^19.0.3": version "19.0.7" resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-19.0.7.tgz#d2e21b4995ab96ae5bfae50b4969da7e04e0bb70" @@ -4007,13 +3998,6 @@ "@octokit/plugin-request-log" "^1.0.4" "@octokit/plugin-rest-endpoint-methods" "^7.0.0" -"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.39.0", "@octokit/types@^6.40.0": - version "6.41.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.41.0.tgz#e58ef78d78596d2fb7df9c6259802464b5f84a04" - integrity sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg== - dependencies: - "@octokit/openapi-types" "^12.11.0" - "@octokit/types@^9.0.0": version "9.1.1" resolved "https://registry.yarnpkg.com/@octokit/types/-/types-9.1.1.tgz#8a71f05c4e72fe51aac2c66ed71999abdd7e2777" @@ -4021,16 +4005,724 @@ dependencies: "@octokit/openapi-types" "^17.0.0" +"@opentelemetry/api-logs@0.41.2": + version "0.41.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.41.2.tgz#600c9b3d79018e7421d2ff7189f41b6d2c987d6a" + integrity sha512-JEV2RAqijAFdWeT6HddYymfnkiRu2ASxoTBr4WsnGJhOjWZkEy6vp+Sx9ozr1NaIODOa2HUyckExIqQjn6qywQ== + dependencies: + "@opentelemetry/api" "^1.0.0" + "@opentelemetry/api@^1.0.0": version "1.4.1" resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.4.1.tgz#ff22eb2e5d476fbc2450a196e40dd243cc20c28f" integrity sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA== +"@opentelemetry/api@^1.4.1": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.6.0.tgz#de2c6823203d6f319511898bb5de7e70f5267e19" + integrity sha512-OWlrQAnWn9577PhVgqjUvMr1pg57Bc4jv0iL4w0PRuOSRvq67rvHW9Ie/dZVMvCzhSCB+UxhcY/PmCmFj33Q+g== + +"@opentelemetry/auto-instrumentations-node@^0.38.0": + version "0.38.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.38.0.tgz#9841ebc87d696aff10cf1ad03b19f5b233868bd2" + integrity sha512-lQXiUAGs79+SkaTycwmtamzH0bsXpGOccl2jNFDztZrCvMn2xD4TJkKm5PuoFp9fnRgtY/vEJck+ViefJnSCdA== + dependencies: + "@opentelemetry/instrumentation" "^0.41.0" + "@opentelemetry/instrumentation-amqplib" "^0.33.0" + "@opentelemetry/instrumentation-aws-lambda" "^0.36.0" + "@opentelemetry/instrumentation-aws-sdk" "^0.35.0" + "@opentelemetry/instrumentation-bunyan" "^0.32.0" + "@opentelemetry/instrumentation-cassandra-driver" "^0.33.0" + "@opentelemetry/instrumentation-connect" "^0.32.0" + "@opentelemetry/instrumentation-dataloader" "^0.5.0" + "@opentelemetry/instrumentation-dns" "^0.32.0" + "@opentelemetry/instrumentation-express" "^0.33.0" + "@opentelemetry/instrumentation-fastify" "^0.32.0" + "@opentelemetry/instrumentation-fs" "^0.8.0" + "@opentelemetry/instrumentation-generic-pool" "^0.32.0" + "@opentelemetry/instrumentation-graphql" "^0.35.0" + "@opentelemetry/instrumentation-grpc" "^0.41.0" + "@opentelemetry/instrumentation-hapi" "^0.32.0" + "@opentelemetry/instrumentation-http" "^0.41.0" + "@opentelemetry/instrumentation-ioredis" "^0.35.0" + "@opentelemetry/instrumentation-knex" "^0.32.0" + "@opentelemetry/instrumentation-koa" "^0.35.0" + "@opentelemetry/instrumentation-lru-memoizer" "^0.33.0" + "@opentelemetry/instrumentation-memcached" "^0.32.0" + "@opentelemetry/instrumentation-mongodb" "^0.36.0" + "@opentelemetry/instrumentation-mongoose" "^0.33.0" + "@opentelemetry/instrumentation-mysql" "^0.34.0" + "@opentelemetry/instrumentation-mysql2" "^0.34.0" + "@opentelemetry/instrumentation-nestjs-core" "^0.33.0" + "@opentelemetry/instrumentation-net" "^0.32.0" + "@opentelemetry/instrumentation-pg" "^0.36.0" + "@opentelemetry/instrumentation-pino" "^0.34.0" + "@opentelemetry/instrumentation-redis" "^0.35.0" + "@opentelemetry/instrumentation-redis-4" "^0.35.0" + "@opentelemetry/instrumentation-restify" "^0.33.0" + "@opentelemetry/instrumentation-router" "^0.33.0" + "@opentelemetry/instrumentation-socket.io" "^0.34.0" + "@opentelemetry/instrumentation-tedious" "^0.6.0" + "@opentelemetry/instrumentation-winston" "^0.32.0" + "@opentelemetry/resource-detector-alibaba-cloud" "^0.28.0" + "@opentelemetry/resource-detector-aws" "^1.3.0" + "@opentelemetry/resource-detector-container" "^0.3.0" + "@opentelemetry/resource-detector-gcp" "^0.29.0" + "@opentelemetry/resources" "^1.12.0" + "@opentelemetry/sdk-node" "^0.41.0" + tslib "^2.3.1" + +"@opentelemetry/context-async-hooks@1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/context-async-hooks/-/context-async-hooks-1.15.2.tgz#116bd5fef231137198d5bf551e8c0521fbdfe928" + integrity sha512-VAMHG67srGFQDG/N2ns5AyUT9vUcoKpZ/NpJ5fDQIPfJd7t3ju+aHwvDsMcrYBWuCh03U3Ky6o16+872CZchBg== + +"@opentelemetry/core@1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.15.2.tgz#5b170bf223a2333884bbc2d29d95812cdbda7c9f" + integrity sha512-+gBv15ta96WqkHZaPpcDHiaz0utiiHZVfm2YOYSqFGrUaJpPkMoSuLBB58YFQGi6Rsb9EHos84X6X5+9JspmLw== + dependencies: + "@opentelemetry/semantic-conventions" "1.15.2" + +"@opentelemetry/core@1.17.1", "@opentelemetry/core@^1.0.0", "@opentelemetry/core@^1.1.0", "@opentelemetry/core@^1.8.0": + version "1.17.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.17.1.tgz#10c5e09c63aeb1836b34d80baf7113760fb19d96" + integrity sha512-I6LrZvl1FF97FQXPR0iieWQmKnGxYtMbWA1GrAXnLUR+B1Hn2m8KqQNEIlZAucyv00GBgpWkpllmULmZfG8P3g== + dependencies: + "@opentelemetry/semantic-conventions" "1.17.1" + +"@opentelemetry/exporter-jaeger@1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-jaeger/-/exporter-jaeger-1.15.2.tgz#1ac7020d798ec4e47417bd90e00763e0947e17de" + integrity sha512-BwYd5836GYvuiQcF4l5X0ca09jGJr/F37MMGyz94VH0b1dp0uYBwRJw2CQh56RlVZEdpKv29JyDRVZ/4UrRgLQ== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/sdk-trace-base" "1.15.2" + "@opentelemetry/semantic-conventions" "1.15.2" + jaeger-client "^3.15.0" + +"@opentelemetry/exporter-trace-otlp-grpc@0.41.2": + version "0.41.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.41.2.tgz#445f850f4675e0afc3e326b2663576fa0b5fbee4" + integrity sha512-tRM/mq7PFj7mXCws5ICMVp/rmgU93JvZdoLE0uLj4tugNz231u2ZgeRYXulBjdeHM88ZQSsWTJMu2mvr/3JV1A== + dependencies: + "@grpc/grpc-js" "^1.7.1" + "@opentelemetry/core" "1.15.2" + "@opentelemetry/otlp-grpc-exporter-base" "0.41.2" + "@opentelemetry/otlp-transformer" "0.41.2" + "@opentelemetry/resources" "1.15.2" + "@opentelemetry/sdk-trace-base" "1.15.2" + +"@opentelemetry/exporter-trace-otlp-http@0.41.2": + version "0.41.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.41.2.tgz#4818088c652f2077a55c9c1364d8320e994dc00f" + integrity sha512-Y0fGLipjZXLMelWtlS1/MDtrPxf25oM408KukRdkN31a1MEFo4h/ZkNwS7ZfmqHGUa+4rWRt2bi6JBiqy7Ytgw== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/otlp-exporter-base" "0.41.2" + "@opentelemetry/otlp-transformer" "0.41.2" + "@opentelemetry/resources" "1.15.2" + "@opentelemetry/sdk-trace-base" "1.15.2" + +"@opentelemetry/exporter-trace-otlp-proto@0.41.2": + version "0.41.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.41.2.tgz#8e8f823d5264e34dc7c8b400f9d03871c7bfcbc5" + integrity sha512-IGZga9IIckqYE3IpRE9FO9G5umabObIrChlXUHYpMJtDgx797dsb3qXCvLeuAwB+HoB8NsEZstlzmLnoa6/HmA== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/otlp-exporter-base" "0.41.2" + "@opentelemetry/otlp-proto-exporter-base" "0.41.2" + "@opentelemetry/otlp-transformer" "0.41.2" + "@opentelemetry/resources" "1.15.2" + "@opentelemetry/sdk-trace-base" "1.15.2" + +"@opentelemetry/exporter-zipkin@1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.15.2.tgz#4f72482909fd7a197fb614fc1f285b15ca008a39" + integrity sha512-j9dPe8tyx4KqIqJAfZ/LCYfkF9+ggsT0V1+bVg9ZKTBNcLf5dTsTMdcxUxc/9s599kgcn6UERnti/tozbzwa6Q== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/resources" "1.15.2" + "@opentelemetry/sdk-trace-base" "1.15.2" + "@opentelemetry/semantic-conventions" "1.15.2" + +"@opentelemetry/instrumentation-amqplib@^0.33.0": + version "0.33.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.33.2.tgz#427ad7f417bb3f89d1b051a945c442387d1c6469" + integrity sha512-qAc9DMkyStlc45LgM+krSckYL4O5zi3uu1rk1QQL7Nn6q3LNNZV2c+fUPMf7hDf2OuK6VyVF0rqWYDSu/JDJ6Q== + dependencies: + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.44.0" + "@opentelemetry/semantic-conventions" "^1.0.0" + +"@opentelemetry/instrumentation-aws-lambda@^0.36.0": + version "0.36.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.36.0.tgz#bfd3ffac407a1339fc0bdc9afb4bb9e2dfe2ef39" + integrity sha512-GkehkjN4vHTc5HNIBlKddrm+EVch2cNEfbLcV7tXLu0Hu95kt6PPOwxHDYRxgvu1auFpJY0epUzmPd11zI706A== + dependencies: + "@opentelemetry/instrumentation" "^0.41.0" + "@opentelemetry/propagator-aws-xray" "^1.3.0" + "@opentelemetry/resources" "^1.8.0" + "@opentelemetry/semantic-conventions" "^1.0.0" + "@types/aws-lambda" "8.10.81" + tslib "^2.3.1" + +"@opentelemetry/instrumentation-aws-sdk@^0.35.0": + version "0.35.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.35.0.tgz#359f56c956afbf526d729e1e8f7d1c28347afaef" + integrity sha512-jKf2nuTe3kYhtINGmgaVlw54q5pgX959zK2abGdvoUSdSP3Pv36YwNZk1K+jAKCN4I71R8/Qp1driAuKKj/Kxg== + dependencies: + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.41.0" + "@opentelemetry/propagation-utils" "^0.30.0" + "@opentelemetry/semantic-conventions" "^1.0.0" + tslib "^2.3.1" + +"@opentelemetry/instrumentation-bunyan@^0.32.0": + version "0.32.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-bunyan/-/instrumentation-bunyan-0.32.2.tgz#24d2b1e574f71182a8a2286fb15cbf89bcca03cf" + integrity sha512-/sLChixYxFas7c5SbLLGUq2yKmU8c58IPlYNNqRSiT0epouqeBjxNUwQeizXl6Ba5+T/Yq7cwCVjh72CRb3tlA== + dependencies: + "@opentelemetry/instrumentation" "^0.44.0" + "@types/bunyan" "1.8.9" + +"@opentelemetry/instrumentation-cassandra-driver@^0.33.0": + version "0.33.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-cassandra-driver/-/instrumentation-cassandra-driver-0.33.2.tgz#848566fe2c6602408a733ed254d62f272499babb" + integrity sha512-0tgIdRJk6tw8PIuRKM/pSQRwF1YGEaG+KxfT09Fxw0DBaxyoTgxgNzOHiYLx8zmoCzGTaLd79tHlrYWZRfXEGQ== + dependencies: + "@opentelemetry/instrumentation" "^0.44.0" + "@opentelemetry/semantic-conventions" "^1.0.0" + +"@opentelemetry/instrumentation-connect@^0.32.0": + version "0.32.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.32.2.tgz#c81cacba090bca62c841b6536a7c05bb37b340a7" + integrity sha512-wNbExeTGoDTvMfraByTy7GaX+6Dc5DmQ49lZoWO5EBmrpsKBaL8l9XYCPNKOa3dEp28d9oJWyx+ixHQ2OhDLcA== + dependencies: + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.44.0" + "@opentelemetry/semantic-conventions" "^1.0.0" + "@types/connect" "3.4.36" + +"@opentelemetry/instrumentation-dataloader@^0.5.0": + version "0.5.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.5.2.tgz#c6807afd7115e6e038cc6e54aac225934d9236b6" + integrity sha512-OYzh714M8szDayFP21eSGUZqQQjgLr7d1skjtbhzT4TIBYe2X7EpPM+4Rmkef+eZsBkCmUk/ecfwAQC+nfAJGg== + dependencies: + "@opentelemetry/instrumentation" "^0.44.0" + +"@opentelemetry/instrumentation-dns@^0.32.0": + version "0.32.3" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-dns/-/instrumentation-dns-0.32.3.tgz#62a31fa0201fbe83d5e3277e72bae6c7247d56ee" + integrity sha512-bBZW58MhfoIVCxKSeJexkbt1iclf0mgReJzKtldbU+pvD4OwSNLYmQ20OE+3YUKEX442CrjXI92Yg6ZF+EqKJQ== + dependencies: + "@opentelemetry/instrumentation" "^0.44.0" + "@opentelemetry/semantic-conventions" "^1.0.0" + semver "^7.5.4" + +"@opentelemetry/instrumentation-express@^0.33.0": + version "0.33.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-express/-/instrumentation-express-0.33.2.tgz#e5bd14be5814e24b257cd093220d32d5e9261c5a" + integrity sha512-FR05iNosZL42haYang6vpmcuLfXLngJs/0gAgqXk8vwqGGwilOFak1PjoRdO4PAoso0FI+3zhV3Tz7jyDOmSyA== + dependencies: + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.44.0" + "@opentelemetry/semantic-conventions" "^1.0.0" + "@types/express" "4.17.18" + +"@opentelemetry/instrumentation-fastify@^0.32.0": + version "0.32.3" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-fastify/-/instrumentation-fastify-0.32.3.tgz#2c0640c986018d1a41dfff3d9c3bfe3b5b1cf62d" + integrity sha512-vRFVoEJXcu6nNpJ61H5syDb84PirOd4b3u8yl8Bcorrr6firGYBQH4pEIVB4PkQWlmi3sLOifqS3VAO2VRloEQ== + dependencies: + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.44.0" + "@opentelemetry/semantic-conventions" "^1.0.0" + +"@opentelemetry/instrumentation-fs@^0.8.0": + version "0.8.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.8.2.tgz#32ce79fc1afd415131318730b4ea3710ad56c9cf" + integrity sha512-LTIBkttNoycbo9EVgegnSCXU0V7mu3X7EzJNmceiaibMqcxZ6G3/ZE5uRIUVkqY+FTWOTXY5a7dJ9OTsSrMjag== + dependencies: + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.44.0" + "@opentelemetry/semantic-conventions" "^1.0.0" + +"@opentelemetry/instrumentation-generic-pool@^0.32.0": + version "0.32.3" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.32.3.tgz#4a5055927d1fac776eccf4204218086e4c070458" + integrity sha512-pJmmyKQzeROBtl0W+Gv1BHeVXixq8xdXtPy2IokEj33/sr++RfElixWMXRkar7suBkj9/c09a4fOj3fUrJJaYQ== + dependencies: + "@opentelemetry/instrumentation" "^0.44.0" + "@opentelemetry/semantic-conventions" "^1.0.0" + +"@opentelemetry/instrumentation-graphql@^0.35.0": + version "0.35.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.35.2.tgz#67b0c173cff1191cfa66aa26f67c6752c365edf2" + integrity sha512-lJv7BbHFK0ExwogdQMtVHfnWhCBMDQEz8KYvhShXfRPiSStU5aVwa3TmT0O00KiJFpATSKJNZMv1iZNHbF6z1g== + dependencies: + "@opentelemetry/instrumentation" "^0.44.0" + +"@opentelemetry/instrumentation-grpc@^0.41.0": + version "0.41.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-grpc/-/instrumentation-grpc-0.41.2.tgz#38b51eda1bcb6bf8d422410fa4596b56b03e98ab" + integrity sha512-+fh9GUFv97p25CMreUv4OdP5L21hPgfX3d4fuQ0KIgIZIaX2M6/8cr5Ik+8zWsyhYzfFX3CKq6BXm3UBg7cswQ== + dependencies: + "@opentelemetry/instrumentation" "0.41.2" + "@opentelemetry/semantic-conventions" "1.15.2" + +"@opentelemetry/instrumentation-hapi@^0.32.0": + version "0.32.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.32.0.tgz#59362dab5d2a2d7fdfe47bda0cd75dec923b0ece" + integrity sha512-Wl43lSVqqJZAxhWE1BWlV9yoInEOGiKeGqNhphoGJLqblmlF8Yxob1t2fK/wTj2srmmm1XU70olwhN09uOQxpg== + dependencies: + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.41.0" + "@opentelemetry/semantic-conventions" "^1.0.0" + "@types/hapi__hapi" "20.0.9" + tslib "^2.3.1" + +"@opentelemetry/instrumentation-http@^0.41.0": + version "0.41.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-http/-/instrumentation-http-0.41.2.tgz#dad5a693eaad2113ce7ed089fa46ef98d79f2bfc" + integrity sha512-dzOC6xkfK0LM6Dzo91aInLdSbdIzKA0IgSDnyLi6YZ0Z7c1bfrFncFx/3gZs8vi+KXLALgfMlpzE7IYDW/cM3A== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/instrumentation" "0.41.2" + "@opentelemetry/semantic-conventions" "1.15.2" + semver "^7.5.1" + +"@opentelemetry/instrumentation-ioredis@^0.35.0": + version "0.35.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.35.2.tgz#4873f2e69fa337d9ce010a03548d21f6d210c258" + integrity sha512-D+KODK3ZzS9Be6zAzcoOyVd4Taf87CQ34jAX+FgrjtRlCxTuY6+p1eFhWNHWYS0EOcmdOcFcXxhszwp3/K1B4A== + dependencies: + "@opentelemetry/instrumentation" "^0.44.0" + "@opentelemetry/redis-common" "^0.36.1" + "@opentelemetry/semantic-conventions" "^1.0.0" + "@types/ioredis4" "npm:@types/ioredis@^4.28.10" + +"@opentelemetry/instrumentation-knex@^0.32.0": + version "0.32.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.32.2.tgz#f85aea7dc077c2453bd30d2c7baf9422e63fb001" + integrity sha512-mEapkK4efIsCIGyuLcPpzA+dfPfw7w/kzS0zpm0Zm+tw1zjEGYoVubE0HzjmmexE4TOL6PzBSvF5FXOUXYH9XQ== + dependencies: + "@opentelemetry/instrumentation" "^0.44.0" + "@opentelemetry/semantic-conventions" "^1.0.0" + +"@opentelemetry/instrumentation-koa@^0.35.0": + version "0.35.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.35.0.tgz#499ff61accd398e2444c0e52f008be88eec8fb33" + integrity sha512-Q/KclXdHKE3sGlalxxX43lx4b8eY5lv5LSdG3mY8aBsrmw1Mx6Cv4VAeqA4ecCygeapTmf9jjOLmgro15IJ3AQ== + dependencies: + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.41.0" + "@opentelemetry/semantic-conventions" "^1.0.0" + "@types/koa" "2.13.6" + "@types/koa__router" "8.0.7" + tslib "^2.3.1" + +"@opentelemetry/instrumentation-lru-memoizer@^0.33.0": + version "0.33.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.33.2.tgz#355bd161aa37a0aafb98f9abc1d998017c7c98e7" + integrity sha512-+IUW0QaB1mHrhXSMJfGgRVal0RgqW6CJ+J9uZJHhcQc2bCNnM2V2N4/pyDL2F7/6gb8bS0ku7dc8PF6LGlGCng== + dependencies: + "@opentelemetry/instrumentation" "^0.44.0" + +"@opentelemetry/instrumentation-memcached@^0.32.0": + version "0.32.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-memcached/-/instrumentation-memcached-0.32.2.tgz#c90b0436f8c109e097255e2bc334e30f0661f1b9" + integrity sha512-g3BsNXvxQr1Vz4ZqIboJK2QOow+t9bNdn8NXyaqI1uZFxVhaIYQJmK0Wt76ueHJ48SVzC1gbqNsGFIak5KhQEg== + dependencies: + "@opentelemetry/instrumentation" "^0.44.0" + "@opentelemetry/semantic-conventions" "^1.0.0" + "@types/memcached" "^2.2.6" + +"@opentelemetry/instrumentation-mongodb@^0.36.0": + version "0.36.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.36.1.tgz#a92e48f2cb4e2e2de430d900c96e21911709e137" + integrity sha512-//FdYXGcUO08Y1tVPXlcEvUYCnRU8tlBgYBe3ZMjF7E1GMaFti4Xy6sAHVreyl0ZQjBTaGtHdyORHIOTKUM4ZA== + dependencies: + "@opentelemetry/instrumentation" "^0.41.2" + "@opentelemetry/sdk-metrics" "^1.9.1" + "@opentelemetry/semantic-conventions" "^1.0.0" + +"@opentelemetry/instrumentation-mongoose@^0.33.0": + version "0.33.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.33.2.tgz#99f235df66009e0b73953a58f3f6b9f28e6a31b1" + integrity sha512-JXhhn8vkGKbev6aBPkQ6dL5rDImQfucrub8mU7dknPPpCL850fSQ2qt2qLvyDXfawF5my6KWW0fkKJCeRA+ECw== + dependencies: + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.44.0" + "@opentelemetry/semantic-conventions" "^1.0.0" + +"@opentelemetry/instrumentation-mysql2@^0.34.0": + version "0.34.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.34.2.tgz#f59f03c3135a8b50bad9cb3d5b55403008a8d0ba" + integrity sha512-Ac/KAHHtTz087P7I6JapBs+ofNOM+RPTDGwSe1ddnTj0xTAO0F6ITmRC1firnMdzDidI/wI+vmgnWclCB81xKQ== + dependencies: + "@opentelemetry/instrumentation" "^0.44.0" + "@opentelemetry/semantic-conventions" "^1.0.0" + "@opentelemetry/sql-common" "^0.40.0" + +"@opentelemetry/instrumentation-mysql@^0.34.0": + version "0.34.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.34.2.tgz#3372dc11010dce2f357a89a1e3f32359c4d34079" + integrity sha512-3OEhW1CB7b93PHIbQ5t8Aoj/dCqNWQBDBbyUXGy2zFbhEcJBVcLeBpy3w8VEjzNTfRC6cVwASuHRP0aLBIPNjQ== + dependencies: + "@opentelemetry/instrumentation" "^0.44.0" + "@opentelemetry/semantic-conventions" "^1.0.0" + "@types/mysql" "2.15.22" + +"@opentelemetry/instrumentation-nestjs-core@^0.33.0": + version "0.33.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.33.2.tgz#fb87031097a96c761db0823c2eff8deba452abbf" + integrity sha512-jrX/355K+myc5V/EQFouqQzBfy5qj+SyVMHIKqVymOx/zWFCvz1p9ChNiPOKzl2il3o/P/aOqBUN/qnRaGowlw== + dependencies: + "@opentelemetry/instrumentation" "^0.44.0" + "@opentelemetry/semantic-conventions" "^1.0.0" + +"@opentelemetry/instrumentation-net@^0.32.0": + version "0.32.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-net/-/instrumentation-net-0.32.2.tgz#bfc5d99d7e6805675079347d811e484d5e45eb83" + integrity sha512-dkm5tZ2NP4Pn3LLs8IfizEQfFBJ7qrqvwoHQA20Z4ZjjjfrPd1aHANCYGs0axh/VBT0IACdX6IZZq/0Lb3Ocfw== + dependencies: + "@opentelemetry/instrumentation" "^0.44.0" + "@opentelemetry/semantic-conventions" "^1.0.0" + +"@opentelemetry/instrumentation-pg@^0.36.0": + version "0.36.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.36.2.tgz#45947d19bbafabf5b350a76350ef4523deac13a5" + integrity sha512-KUjI8OGi7kicml2Sd/PR/M8otZoZEdPArMfhznS6OQKit+RxFo0p5x6RVeka/cLQlmoc3eeGBizDeZetssbHgw== + dependencies: + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.44.0" + "@opentelemetry/semantic-conventions" "^1.0.0" + "@opentelemetry/sql-common" "^0.40.0" + "@types/pg" "8.6.1" + "@types/pg-pool" "2.0.4" + +"@opentelemetry/instrumentation-pino@^0.34.0": + version "0.34.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-pino/-/instrumentation-pino-0.34.2.tgz#f0cd996935430e4750c8aa83d23d8f56523e0edd" + integrity sha512-gGGeM2sRSulAtV2nAPMrRBm+nenyrKO3kyrORN+q2V4S8hx+6yze35+QQkU5uWk6N5/s5Y9bxqPcaNUt3jwprw== + dependencies: + "@opentelemetry/instrumentation" "^0.44.0" + +"@opentelemetry/instrumentation-redis-4@^0.35.0": + version "0.35.3" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.35.3.tgz#6862cb3542c22b151b38d259d13225e3f442bc26" + integrity sha512-fWMqJpEGLcE1Z76CTwhZPaFU7EGBZ/pNFKCLdHALddvud/9AxKbxLIn73QGh7sLU8MwhCBkTbuipj5UAy8g8TA== + dependencies: + "@opentelemetry/instrumentation" "^0.44.0" + "@opentelemetry/redis-common" "^0.36.1" + "@opentelemetry/semantic-conventions" "^1.0.0" + +"@opentelemetry/instrumentation-redis@^0.35.0": + version "0.35.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.35.2.tgz#1630f48ab90dc22c8bfd1ec3a7050bc9fea24b5b" + integrity sha512-KBwVMsoiMc2kAffnmG64rJDMEbmK3VT991s7kedipJsBT9jrcx4tXT/fdIwFk+helawXHbiI0ILlxzA8dVYz3g== + dependencies: + "@opentelemetry/instrumentation" "^0.44.0" + "@opentelemetry/redis-common" "^0.36.1" + "@opentelemetry/semantic-conventions" "^1.0.0" + +"@opentelemetry/instrumentation-restify@^0.33.0": + version "0.33.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-restify/-/instrumentation-restify-0.33.0.tgz#4f7fbcda93e428052c07d6edc05c192e868917be" + integrity sha512-evDjcF6M9G+KH/GCjtUx9Vnm/CBZ9CBfmm/RP6Aeo20y6Kset1ZEoPK79JT7JK1sCPqViBPoj4qnFePz9/20lg== + dependencies: + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.41.0" + "@opentelemetry/semantic-conventions" "^1.0.0" + tslib "^2.3.1" + +"@opentelemetry/instrumentation-router@^0.33.0": + version "0.33.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-router/-/instrumentation-router-0.33.2.tgz#4914808c5baf1e47bc655649921de1e79ab1ab58" + integrity sha512-GNQMLkz24vc0ND/Su3/ytyAfVPtBYnbLbM2dscj8h3VHebUZPWcOGk/M6wsqDCpEbP5xUA56afEkWsbGzWtMxA== + dependencies: + "@opentelemetry/instrumentation" "^0.44.0" + "@opentelemetry/semantic-conventions" "^1.0.0" + +"@opentelemetry/instrumentation-socket.io@^0.34.0": + version "0.34.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-socket.io/-/instrumentation-socket.io-0.34.2.tgz#a7f65a008fbd0af275c5934289238ef0955002b4" + integrity sha512-uway54Mx/MtZajA5jsq8TPhRpMK9QBTzjhyIdMGS2XV+gykL/JTFbeis6ewmRnLUAcBrmPcaxQnavEAkJ787AA== + dependencies: + "@opentelemetry/instrumentation" "^0.44.0" + "@opentelemetry/semantic-conventions" "^1.0.0" + +"@opentelemetry/instrumentation-tedious@^0.6.0": + version "0.6.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.6.2.tgz#fe08c0a2e3966bc9c2a47d6c24b53dc596592698" + integrity sha512-8/WwifQNuB/1bRWyqSCJPow/Gd6EubFpKbZJgOEIL1jk0Lk/ikpjI5zxqWbLIT2pUOi0Ihwvu5sTqgdY4oPz9Q== + dependencies: + "@opentelemetry/instrumentation" "^0.44.0" + "@opentelemetry/semantic-conventions" "^1.0.0" + "@types/tedious" "^4.0.10" + +"@opentelemetry/instrumentation-winston@^0.32.0": + version "0.32.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-winston/-/instrumentation-winston-0.32.2.tgz#3e10fabbf7ab5496fec3fc8bcfc0227a5ba0a5ca" + integrity sha512-6+BMhSWaGxgezwOUuMH389V16fMgwFNmXAwh8zk0mjYHmWpyLzoJ+QV8AQ9WZFBQQnkpbbxfiFdiyO+NCvwBCg== + dependencies: + "@opentelemetry/instrumentation" "^0.44.0" + +"@opentelemetry/instrumentation@0.41.2", "@opentelemetry/instrumentation@^0.41.0", "@opentelemetry/instrumentation@^0.41.2": + version "0.41.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.41.2.tgz#cae11fa64485dcf03dae331f35b315b64bc6189f" + integrity sha512-rxU72E0pKNH6ae2w5+xgVYZLzc5mlxAbGzF4shxMVK8YC2QQsfN38B2GPbj0jvrKWWNUElfclQ+YTykkNg/grw== + dependencies: + "@types/shimmer" "^1.0.2" + import-in-the-middle "1.4.2" + require-in-the-middle "^7.1.1" + semver "^7.5.1" + shimmer "^1.2.1" + +"@opentelemetry/instrumentation@^0.44.0": + version "0.44.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.44.0.tgz#194f16fc96671575b6bd73d3fadffb5aa4497e67" + integrity sha512-B6OxJTRRCceAhhnPDBshyQO7K07/ltX3quOLu0icEvPK9QZ7r9P1y0RQX8O5DxB4vTv4URRkxkg+aFU/plNtQw== + dependencies: + "@types/shimmer" "^1.0.2" + import-in-the-middle "1.4.2" + require-in-the-middle "^7.1.1" + semver "^7.5.2" + shimmer "^1.2.1" + +"@opentelemetry/otlp-exporter-base@0.41.2": + version "0.41.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.41.2.tgz#5928dfedb2a70117f03809862cf2cbdf8b7f9bf3" + integrity sha512-pfwa6d+Dax3itZcGWiA0AoXeVaCuZbbqUTsCtOysd2re8C2PWXNxDONUfBWsn+KgxAdi+ljwTjJGiaVLDaIEvQ== + dependencies: + "@opentelemetry/core" "1.15.2" + +"@opentelemetry/otlp-grpc-exporter-base@0.41.2": + version "0.41.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.41.2.tgz#b056915aa274947517ac86b0c78533db274404e8" + integrity sha512-OErK8dYjXG01XIMIpmOV2SzL9ctkZ0Nyhf2UumICOAKtgLvR5dG1JMlsNVp8Jn0RzpsKc6Urv7JpP69wzRXN+A== + dependencies: + "@grpc/grpc-js" "^1.7.1" + "@opentelemetry/core" "1.15.2" + "@opentelemetry/otlp-exporter-base" "0.41.2" + protobufjs "^7.2.3" + +"@opentelemetry/otlp-proto-exporter-base@0.41.2": + version "0.41.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/otlp-proto-exporter-base/-/otlp-proto-exporter-base-0.41.2.tgz#10b1a4bb973bd6e0e741931d90f22387c5a3a151" + integrity sha512-BxmEMiP6tHiFroe5/dTt9BsxCci7BTLtF7A6d4DKHLiLweWWZxQ9l7hON7qt/IhpKrQcAFD1OzZ1Gq2ZkNzhCw== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/otlp-exporter-base" "0.41.2" + protobufjs "^7.2.3" + +"@opentelemetry/otlp-transformer@0.41.2": + version "0.41.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/otlp-transformer/-/otlp-transformer-0.41.2.tgz#cd3a7185ef77fe9b7b4c2d2f9e001fa1d2fa6cf8" + integrity sha512-jJbPwB0tNu2v+Xi0c/v/R3YBLJKLonw1p+v3RVjT2VfzeUyzSp/tBeVdY7RZtL6dzZpA9XSmp8UEfWIFQo33yA== + dependencies: + "@opentelemetry/api-logs" "0.41.2" + "@opentelemetry/core" "1.15.2" + "@opentelemetry/resources" "1.15.2" + "@opentelemetry/sdk-logs" "0.41.2" + "@opentelemetry/sdk-metrics" "1.15.2" + "@opentelemetry/sdk-trace-base" "1.15.2" + +"@opentelemetry/propagation-utils@^0.30.0": + version "0.30.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/propagation-utils/-/propagation-utils-0.30.2.tgz#9a1d2491af3251d6dcd75ee6e90a510142af1f12" + integrity sha512-HMn33DQbehFvPW7PBzIajkRuoCGaSaa/vb8IyaXtRsX4aY8K9XqOiRwunN4bPo7rnh+KnRdKx0tO0In1zvdQ4w== + +"@opentelemetry/propagator-aws-xray@^1.3.0": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/propagator-aws-xray/-/propagator-aws-xray-1.3.1.tgz#7fc77a95fe89c705442b0e5a4218422c2954cc07" + integrity sha512-6fDMzFlt5r6VWv7MUd0eOpglXPFqykW8CnOuUxJ1VZyLy6mV1bzBlzpsqEmhx1bjvZYvH93vhGkQZqrm95mlrQ== + dependencies: + "@opentelemetry/core" "^1.0.0" + +"@opentelemetry/propagator-b3@1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/propagator-b3/-/propagator-b3-1.15.2.tgz#7bcb9fa645042a440922669fbac06a1a91e6704b" + integrity sha512-ZSrL3DpMEDsjD8dPt9Ze3ue53nEXJt512KyxXlLgLWnSNbe1mrWaXWkh7OLDoVJh9LqFw+tlvAhDVt/x3DaFGg== + dependencies: + "@opentelemetry/core" "1.15.2" + +"@opentelemetry/propagator-jaeger@1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.15.2.tgz#90757fc9529da806a1845f502acb6d0eb821e3db" + integrity sha512-6m1yu7PVDIRz6BwA36lacfBZJCfAEHKgu+kSyukNwVdVjsTNeyD9xNPQnkl0WN7Rvhk8/yWJ83tLPEyGhk1wCQ== + dependencies: + "@opentelemetry/core" "1.15.2" + +"@opentelemetry/propagator-ot-trace@^0.27.0": + version "0.27.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/propagator-ot-trace/-/propagator-ot-trace-0.27.1.tgz#6718aefd6d0401726e2232fd7ad84515f2b34ca9" + integrity sha512-+Aeht0+1kv/7KAYxNVQFDczEgMQIhi+yO/QNBTxEUGWEQshxznqT2Knqd5nARY8IF3okFdkP4PJUFubYfjWSzw== + +"@opentelemetry/redis-common@^0.36.1": + version "0.36.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/redis-common/-/redis-common-0.36.1.tgz#79bca902603dd27862223a751be0f4bb0be54c2b" + integrity sha512-YjfNEr7DK1Ymc5H0bzhmqVvMcCs+PUEUerzrpTFdHfZxj3HpnnjZTIFKx/gxiL/sajQ8dxycjlreoYTVYKBXlw== + +"@opentelemetry/resource-detector-alibaba-cloud@^0.28.0": + version "0.28.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/resource-detector-alibaba-cloud/-/resource-detector-alibaba-cloud-0.28.2.tgz#108a92ccef9220a11d4fe921b1039847913cd0e9" + integrity sha512-huG07F7Nu2gPTAkb4tSuA/KrNrJ8TfjmyOIPOL/CqXwZh5QD2876YvqM1tqh7gAhSPTSSpfH0ozxcQNmzzeo7g== + dependencies: + "@opentelemetry/resources" "^1.0.0" + "@opentelemetry/semantic-conventions" "^1.0.0" + +"@opentelemetry/resource-detector-aws@^1.3.0": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/resource-detector-aws/-/resource-detector-aws-1.3.2.tgz#0e1cf985cf75e2325e93eb4ce4b708995766c73b" + integrity sha512-RaBOD3mV69ukI43FZwrnOywAJe7M4405/dqdFRHv65rDLmYMBEUOLdrhqDk8uKJwaHAob2jyDBDCv48RnTZi5g== + dependencies: + "@opentelemetry/core" "^1.0.0" + "@opentelemetry/resources" "^1.0.0" + "@opentelemetry/semantic-conventions" "^1.0.0" + +"@opentelemetry/resource-detector-container@^0.3.0": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/resource-detector-container/-/resource-detector-container-0.3.2.tgz#3d3121b83a1974c246ee940645e39465e57269b5" + integrity sha512-pJXm9RXtQSq6PzXpLFC44sG2VhB/BDd7CRxGLS4ytdweBfdl6h2pIDL4CDQQSBc278JXmfslEFQ9K/fT/piXZw== + dependencies: + "@opentelemetry/resources" "^1.0.0" + "@opentelemetry/semantic-conventions" "^1.0.0" + +"@opentelemetry/resource-detector-gcp@^0.29.0": + version "0.29.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/resource-detector-gcp/-/resource-detector-gcp-0.29.2.tgz#f2c24e788ebae2f41e9acee0630674352f72798a" + integrity sha512-BdboAJA3RMRMs6wdGXMvyTCS/72S72Xs+QtdEqMeHViOoMgHidjQIkbj4cFlteUh5GrWM7aQeWgahd9vGZx3Pw== + dependencies: + "@opentelemetry/core" "^1.0.0" + "@opentelemetry/resources" "^1.0.0" + "@opentelemetry/semantic-conventions" "^1.0.0" + gcp-metadata "^5.0.0" + +"@opentelemetry/resources@1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.15.2.tgz#0c9e26cb65652a1402834a3c030cce6028d6dd9d" + integrity sha512-xmMRLenT9CXmm5HMbzpZ1hWhaUowQf8UB4jMjFlAxx1QzQcsD3KFNAVX/CAWzFPtllTyTplrA4JrQ7sCH3qmYw== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/semantic-conventions" "1.15.2" + +"@opentelemetry/resources@1.17.1", "@opentelemetry/resources@^1.0.0", "@opentelemetry/resources@^1.12.0", "@opentelemetry/resources@^1.8.0": + version "1.17.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.17.1.tgz#932f70f58c0e03fb1d38f0cba12672fd70804d99" + integrity sha512-M2e5emqg5I7qRKqlzKx0ROkcPyF8PbcSaWEdsm72od9txP7Z/Pl8PDYOyu80xWvbHAWk5mDxOF6v3vNdifzclA== + dependencies: + "@opentelemetry/core" "1.17.1" + "@opentelemetry/semantic-conventions" "1.17.1" + +"@opentelemetry/sdk-logs@0.41.2": + version "0.41.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-logs/-/sdk-logs-0.41.2.tgz#c3eeb6793bdfa52351d66e2e66637e433abed672" + integrity sha512-smqKIw0tTW15waj7BAPHFomii5c3aHnSE4LQYTszGoK5P9nZs8tEAIpu15UBxi3aG31ZfsLmm4EUQkjckdlFrw== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/resources" "1.15.2" + +"@opentelemetry/sdk-metrics@1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-metrics/-/sdk-metrics-1.15.2.tgz#eadd0a049de9cd860e1e0d49eea01156469c4b60" + integrity sha512-9aIlcX8GnhcsAHW/Wl8bzk4ZnWTpNlLtud+fxUfBtFATu6OZ6TrGrF4JkT9EVrnoxwtPIDtjHdEsSjOqisY/iA== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/resources" "1.15.2" + lodash.merge "^4.6.2" + +"@opentelemetry/sdk-metrics@^1.15.1", "@opentelemetry/sdk-metrics@^1.9.1": + version "1.17.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-metrics/-/sdk-metrics-1.17.1.tgz#9c4d13d845bcc82be8684050d9db7cce10f61580" + integrity sha512-eHdpsMCKhKhwznxvEfls8Wv3y4ZBWkkXlD3m7vtHIiWBqsMHspWSfie1s07mM45i/bBCf6YBMgz17FUxIXwmZA== + dependencies: + "@opentelemetry/core" "1.17.1" + "@opentelemetry/resources" "1.17.1" + lodash.merge "^4.6.2" + +"@opentelemetry/sdk-node@^0.41.0", "@opentelemetry/sdk-node@^0.41.1": + version "0.41.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-node/-/sdk-node-0.41.2.tgz#7ac2fc149d371a9f17c2adba395a9aa257ed1bf4" + integrity sha512-t3vaB5ajoXLtVFoL8TSoSgaVATmOyUfkIfBE4nvykm0dM2vQjMS/SUUelzR06eiPTbMPsr2UkevWhy2/oXy2vg== + dependencies: + "@opentelemetry/api-logs" "0.41.2" + "@opentelemetry/core" "1.15.2" + "@opentelemetry/exporter-jaeger" "1.15.2" + "@opentelemetry/exporter-trace-otlp-grpc" "0.41.2" + "@opentelemetry/exporter-trace-otlp-http" "0.41.2" + "@opentelemetry/exporter-trace-otlp-proto" "0.41.2" + "@opentelemetry/exporter-zipkin" "1.15.2" + "@opentelemetry/instrumentation" "0.41.2" + "@opentelemetry/resources" "1.15.2" + "@opentelemetry/sdk-logs" "0.41.2" + "@opentelemetry/sdk-metrics" "1.15.2" + "@opentelemetry/sdk-trace-base" "1.15.2" + "@opentelemetry/sdk-trace-node" "1.15.2" + "@opentelemetry/semantic-conventions" "1.15.2" + +"@opentelemetry/sdk-trace-base@1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.15.2.tgz#4821f94033c55a6c8bbd35ae387b715b6108517a" + integrity sha512-BEaxGZbWtvnSPchV98qqqqa96AOcb41pjgvhfzDij10tkBhIu9m0Jd6tZ1tJB5ZHfHbTffqYVYE0AOGobec/EQ== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/resources" "1.15.2" + "@opentelemetry/semantic-conventions" "1.15.2" + +"@opentelemetry/sdk-trace-base@1.17.1": + version "1.17.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.17.1.tgz#8ede213df8b0c957028a869c66964e535193a4fd" + integrity sha512-pfSJJSjZj5jkCJUQZicSpzN8Iz9UKMryPWikZRGObPnJo6cUSoKkjZh6BM3j+D47G4olMBN+YZKYqkFM1L6zNA== + dependencies: + "@opentelemetry/core" "1.17.1" + "@opentelemetry/resources" "1.17.1" + "@opentelemetry/semantic-conventions" "1.17.1" + +"@opentelemetry/sdk-trace-node@1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.15.2.tgz#987c929597ca274995164508f57a15f682d9de2f" + integrity sha512-5deakfKLCbPpKJRCE2GPI8LBE2LezyvR17y3t37ZI3sbaeogtyxmBaFV+slmG9fN8OaIT+EUsm1QAT1+z59gbQ== + dependencies: + "@opentelemetry/context-async-hooks" "1.15.2" + "@opentelemetry/core" "1.15.2" + "@opentelemetry/propagator-b3" "1.15.2" + "@opentelemetry/propagator-jaeger" "1.15.2" + "@opentelemetry/sdk-trace-base" "1.15.2" + semver "^7.5.1" + +"@opentelemetry/sdk-trace-web@^1.15.1": + version "1.17.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-web/-/sdk-trace-web-1.17.1.tgz#1c13e6d8427a33b5e97ac85cb745a084b976c470" + integrity sha512-Nle48xE1eaR6lRqyOvUlIwW/C2Bz6pptHzlHqrd+a6tGSLWEP1ZhPfuJbNeq/tWX5PX2RgeOsLlvPLEEBKeKxg== + dependencies: + "@opentelemetry/core" "1.17.1" + "@opentelemetry/sdk-trace-base" "1.17.1" + "@opentelemetry/semantic-conventions" "1.17.1" + +"@opentelemetry/semantic-conventions@1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.15.2.tgz#3bafb5de3e20e841dff6cb3c66f4d6e9694c4241" + integrity sha512-CjbOKwk2s+3xPIMcd5UNYQzsf+v94RczbdNix9/kQh38WiQkM90sUOi3if8eyHFgiBjBjhwXrA7W3ydiSQP9mw== + +"@opentelemetry/semantic-conventions@1.17.1", "@opentelemetry/semantic-conventions@^1.15.1": + version "1.17.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.17.1.tgz#93d219935e967fbb9aa0592cc96b2c0ec817a56f" + integrity sha512-xbR2U+2YjauIuo42qmE8XyJK6dYeRMLJuOlUP5SO4auET4VtOHOzgkRVOq+Ik18N+Xf3YPcqJs9dZMiDddz1eQ== + "@opentelemetry/semantic-conventions@^1.0.0": version "1.12.0" resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.12.0.tgz#19c959bdb900986e74939d4227e757aa16936b91" integrity sha512-hO+bdeGOlJwqowUBoZF5LyP3ORUFOP1G0GRv8N45W/cztXbT2ZEXaAzfokRS9Xc9FWmYrDj32mF6SzH6wuoIyA== +"@opentelemetry/sql-common@^0.40.0": + version "0.40.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/sql-common/-/sql-common-0.40.0.tgz#8cbed0722354d62997c3b9e1adf0e16257be6b15" + integrity sha512-vSqRJYUPJVjMFQpYkQS3ruexCPSZJ8esne3LazLwtCPaPRvzZ7WG3tX44RouAn7w4wMp8orKguBqtt+ng2UTnw== + dependencies: + "@opentelemetry/core" "^1.1.0" + "@openzeppelin/contracts8@npm:@openzeppelin/contracts@^4.4.2": version "4.9.1" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.9.1.tgz#afa804d2c68398704b0175acc94d91a54f203645" @@ -4057,11 +4749,6 @@ web3-eth-contract "1.2.2" web3-utils "1.2.2" -"@panva/asn1.js@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@panva/asn1.js/-/asn1.js-1.0.0.tgz#dd55ae7b8129e02049f009408b97c61ccf9032f6" - integrity sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw== - "@parcel/watcher@2.0.4": version "2.0.4" resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.0.4.tgz#f300fef4cc38008ff4b8c29d92588eced3ce014b" @@ -4215,6 +4902,24 @@ "@noble/secp256k1" "~1.7.0" "@scure/base" "~1.1.0" +"@scure/bip32@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.0.tgz#6c8d980ef3f290987736acd0ee2e0f0d50068d87" + integrity sha512-bcKpo1oj54hGholplGLpqPHRbIsnbixFtc06nwuNM5/dwSXOq/AAYoIBRsBmnZJSdfeNW5rnff7NTAz3ZCqR9Q== + dependencies: + "@noble/curves" "~1.0.0" + "@noble/hashes" "~1.3.0" + "@scure/base" "~1.1.0" + +"@scure/bip32@1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.1.tgz#7248aea723667f98160f593d621c47e208ccbb10" + integrity sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A== + dependencies: + "@noble/curves" "~1.1.0" + "@noble/hashes" "~1.3.1" + "@scure/base" "~1.1.0" + "@scure/bip39@1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.1.1.tgz#b54557b2e86214319405db819c4b6a370cf340c5" @@ -4223,6 +4928,39 @@ "@noble/hashes" "~1.2.0" "@scure/base" "~1.1.0" +"@scure/bip39@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.0.tgz#a207e2ef96de354de7d0002292ba1503538fc77b" + integrity sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg== + dependencies: + "@noble/hashes" "~1.3.0" + "@scure/base" "~1.1.0" + +"@scure/bip39@1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.1.tgz#5cee8978656b272a917b7871c981e0541ad6ac2a" + integrity sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg== + dependencies: + "@noble/hashes" "~1.3.0" + "@scure/base" "~1.1.0" + +"@sideway/address@^4.1.3": + version "4.1.4" + resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0" + integrity sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@sideway/formula@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f" + integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== + +"@sideway/pinpoint@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" + integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== + "@sinclair/typebox@^0.25.16": version "0.25.24" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" @@ -4311,6 +5049,11 @@ resolved "https://registry.yarnpkg.com/@stablelib/wipe/-/wipe-0.5.0.tgz#a682d5f9448e950e099e537e6f72fc960275d151" integrity sha512-SifvRV0rTTFR1qEF6G1hondGZyrmiM1laR8PPrO6TZwQG03hJduVbUX8uQk+Q6FdkND2Z9B8uLPyUAquQIk3iA== +"@summa-tx/memview.sol@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@summa-tx/memview.sol/-/memview.sol-1.1.0.tgz#f54a09faef46ca52deb94e10d0daacd28efe5be6" + integrity sha512-nLEfdC0ayn+PND5WWGalv+IyQ0NYomz43s1IJWDjawUh4JvW03LeP7ZjuMJPLdyJqYX3p8x1LwYt8Klde7cluw== + "@szmarczak/http-timer@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" @@ -4927,6 +5670,13 @@ resolved "https://registry.yarnpkg.com/@types/abstract-leveldown/-/abstract-leveldown-7.2.1.tgz#bb16403c17754b0c4d5772d71d03b924a03d4c80" integrity sha512-YK8irIC+eMrrmtGx0H4ISn9GgzLd9dojZWJaMbjp1YHLl2VqqNFBNrL5Q3KjGf4VE3sf/4hmq6EhQZ7kZp1NoQ== +"@types/accepts@*": + version "1.3.6" + resolved "https://registry.yarnpkg.com/@types/accepts/-/accepts-1.3.6.tgz#5e33830c8ed7b6025976738c88540df2e85b6d91" + integrity sha512-6+qlUg57yfE9OO63wnsJXLeq9cG3gSHBBIxNMOjNrbDRlDnm/NaR7RctfYcVCPq+j7d+MwOxqVEludH5+FKrlg== + dependencies: + "@types/node" "*" + "@types/accepts@^1.3.5": version "1.3.5" resolved "https://registry.yarnpkg.com/@types/accepts/-/accepts-1.3.5.tgz#c34bec115cfc746e04fe5a059df4ce7e7b391575" @@ -4934,13 +5684,6 @@ dependencies: "@types/node" "*" -"@types/archiver@^5.1.0": - version "5.3.2" - resolved "https://registry.yarnpkg.com/@types/archiver/-/archiver-5.3.2.tgz#a9f0bcb0f0b991400e7766d35f6e19d163bdadcc" - integrity sha512-IctHreBuWE5dvBDz/0WeKtyVKVRs4h75IblxOACL92wU66v+HGAfEYAOyXkOFphvRJMhuXdI9huDXpX0FC6lCw== - dependencies: - "@types/readdir-glob" "*" - "@types/asn1js@^0.0.2": version "0.0.2" resolved "https://registry.yarnpkg.com/@types/asn1js/-/asn1js-0.0.2.tgz#f278a8af81861813d4fc9cd0a86c8b1ac5f16db3" @@ -4948,6 +5691,11 @@ dependencies: "@types/pvutils" "*" +"@types/aws-lambda@8.10.81": + version "8.10.81" + resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.81.tgz#6d405269aad82e05a348687631aa9a587cdbe158" + integrity sha512-C1rFKGVZ8KwqhwBOYlpoybTSRtxu2433ea6JaO3amc6ubEe08yQoFsPa9aU9YqvX7ppeZ25CnCtC4AH9mhtxsQ== + "@types/babel__core@^7.1.14": version "7.20.0" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.0.tgz#61bc5a4cae505ce98e1e36c5445e4bee060d8891" @@ -4981,7 +5729,7 @@ dependencies: "@babel/types" "^7.3.0" -"@types/bn.js@*", "@types/bn.js@4.11.6", "@types/bn.js@^4.11.0", "@types/bn.js@^4.11.3", "@types/bn.js@^4.11.4", "@types/bn.js@^4.11.5", "@types/bn.js@^5.1.0", "@types/bn.js@^5.1.1": +"@types/bn.js@*", "@types/bn.js@4.11.6", "@types/bn.js@^4.11.0", "@types/bn.js@^4.11.3", "@types/bn.js@^4.11.4", "@types/bn.js@^5.1.0", "@types/bn.js@^5.1.1": version "4.11.6" resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== @@ -4996,13 +5744,20 @@ "@types/connect" "*" "@types/node" "*" -"@types/bunyan@1.8.8", "@types/bunyan@^1.8.8": +"@types/bunyan@1.8.8": version "1.8.8" resolved "https://registry.yarnpkg.com/@types/bunyan/-/bunyan-1.8.8.tgz#8d6d33f090f37c07e2a80af30ae728450a101008" integrity sha512-Cblq+Yydg3u+sGiz2mjHjC5MPmdjY+No4qvHrF+BUhblsmSfMvsHLbOG62tPbonsqBj6sbWv1LHcsoe5Jw+/Ow== dependencies: "@types/node" "*" +"@types/bunyan@1.8.9": + version "1.8.9" + resolved "https://registry.yarnpkg.com/@types/bunyan/-/bunyan-1.8.9.tgz#22d4517f3217b7c8f5a69bbc8c9f6df79779dcb5" + integrity sha512-ZqS9JGpBxVOvsawzmVt30sP++gSQMTejCkIAQ3VdadOcRE8izTyW66hufvwLeH+YEGP6Js2AW7Gz+RMyvrEbmw== + dependencies: + "@types/node" "*" + "@types/bytebuffer@^5.0.40": version "5.0.44" resolved "https://registry.yarnpkg.com/@types/bytebuffer/-/bytebuffer-5.0.44.tgz#553015fb34db1fc3eb3f7b232bff91c006c251a1" @@ -5069,23 +5824,33 @@ dependencies: "@types/node" "*" -"@types/cookiejar@*": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@types/cookiejar/-/cookiejar-2.1.2.tgz#66ad9331f63fe8a3d3d9d8c6e3906dd10f6446e8" - integrity sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog== +"@types/connect@3.4.36": + version "3.4.36" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.36.tgz#e511558c15a39cb29bd5357eebb57bd1459cd1ab" + integrity sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w== + dependencies: + "@types/node" "*" + +"@types/content-disposition@*": + version "0.5.7" + resolved "https://registry.yarnpkg.com/@types/content-disposition/-/content-disposition-0.5.7.tgz#3b98d4bf8c80640f93b042511acb5aad18139748" + integrity sha512-V9/5u21RHFR1zfdm3rQ6pJUKV+zSSVQt+yq16i1YhdivVzWgPEoKedc3GdT8aFjsqQbakdxuy3FnEdePUQOamQ== + +"@types/cookies@*": + version "0.7.9" + resolved "https://registry.yarnpkg.com/@types/cookies/-/cookies-0.7.9.tgz#d03e9af454b8e23718c9bf0d522649a64d5e67cf" + integrity sha512-SrGYvhKohd/WSOII0WpflC73RgdJhQoqpwq9q+n/qugNGiDSGYXfHy3QvB4+X+J/gYe27j2fSRnK4B+1A3nvsw== + dependencies: + "@types/connect" "*" + "@types/express" "*" + "@types/keygrip" "*" + "@types/node" "*" "@types/cors@2.8.12": version "2.8.12" resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.12.tgz#6b2c510a7ad7039e98e7b8d3d6598f4359e5c080" integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw== -"@types/cors@^2.8.5": - version "2.8.13" - resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.13.tgz#b8ade22ba455a1b8cb3b5d3f35910fd204f84f94" - integrity sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA== - dependencies: - "@types/node" "*" - "@types/country-data@^0.0.0": version "0.0.0" resolved "https://registry.yarnpkg.com/@types/country-data/-/country-data-0.0.0.tgz#6f5563cae3d148780c5b6539803a29bd93f8f1a1" @@ -5112,7 +5877,7 @@ dependencies: "@types/node" "*" -"@types/elliptic@^6.4.12", "@types/elliptic@^6.4.9": +"@types/elliptic@^6.4.9": version "6.4.14" resolved "https://registry.yarnpkg.com/@types/elliptic/-/elliptic-6.4.14.tgz#7bbaad60567a588c1f08b10893453e6b9b4de48e" integrity sha512-z4OBcDAU0GVwDTuwJzQCiL6188QvZMkvoERgcVjq0/mPM8jCfdwZ3x5zQEVoL9WCAru3aG5wl3Z5Ww5wBWn7ZQ== @@ -5126,23 +5891,6 @@ dependencies: bignumber.js "7.2.1" -"@types/ethereumjs-util@^5.2.0": - version "5.2.0" - resolved "https://registry.yarnpkg.com/@types/ethereumjs-util/-/ethereumjs-util-5.2.0.tgz#f49fe8114789ec0871721392c09318c3eb56671b" - integrity sha512-qwQgQqXXTRv2h2AlJef+tMEszLFkCB9dWnrJYIdAwqjubERXEc/geB+S3apRw0yQyTVnsBf8r6BhlrE8vx+3WQ== - dependencies: - "@types/bn.js" "*" - "@types/node" "*" - -"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.33": - version "4.17.33" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz#de35d30a9d637dc1450ad18dd583d75d5733d543" - integrity sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA== - dependencies: - "@types/node" "*" - "@types/qs" "*" - "@types/range-parser" "*" - "@types/express-serve-static-core@4.17.31": version "4.17.31" resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz#a1139efeab4e7323834bb0226e62ac019f474b2f" @@ -5162,6 +5910,25 @@ "@types/range-parser" "*" "@types/send" "*" +"@types/express-serve-static-core@^4.17.33": + version "4.17.33" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz#de35d30a9d637dc1450ad18dd583d75d5733d543" + integrity sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + +"@types/express@*": + version "4.17.20" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.20.tgz#e7c9b40276d29e38a4e3564d7a3d65911e2aa433" + integrity sha512-rOaqlkgEvOW495xErXMsmyX3WKBInbhG5eqojXYi3cGUaLoRDlXa5d52fkfWZT963AZ3v2eZ4MbKE6WpDAGVsw== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.33" + "@types/qs" "*" + "@types/serve-static" "*" + "@types/express@4.17.14": version "4.17.14" resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.14.tgz#143ea0557249bc1b3b54f15db4c81c3d4eb3569c" @@ -5172,19 +5939,10 @@ "@types/qs" "*" "@types/serve-static" "*" -"@types/express@4.17.3": - version "4.17.3" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.3.tgz#38e4458ce2067873b09a73908df488870c303bd9" - integrity sha512-I8cGRJj3pyOLs/HndoP+25vOqhqWkAZsWMEmq1qXy/b/M3ppufecUwaK2/TVDVxcV61/iSdhykUjQQ2DLSrTdg== - dependencies: - "@types/body-parser" "*" - "@types/express-serve-static-core" "*" - "@types/serve-static" "*" - -"@types/express@^4.17.14", "@types/express@^4.17.6": - version "4.17.17" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.17.tgz#01d5437f6ef9cfa8668e616e13c2f2ac9a491ae4" - integrity sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q== +"@types/express@4.17.18": + version "4.17.18" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.18.tgz#efabf5c4495c1880df1bdffee604b143b29c4a95" + integrity sha512-Sxv8BSLLgsBYmcnGdGjjEjqET2U+AKAdCRODmMiq02FgjwuV75Ut85DRpvFjyw/Mk0vgUOliGRU0UUmuuZHByQ== dependencies: "@types/body-parser" "*" "@types/express-serve-static-core" "^4.17.33" @@ -5226,7 +5984,7 @@ "@types/minimatch" "*" "@types/node" "*" -"@types/google-libphonenumber@^7.4.17", "@types/google-libphonenumber@^7.4.23": +"@types/google-libphonenumber@^7.4.23": version "7.4.23" resolved "https://registry.yarnpkg.com/@types/google-libphonenumber/-/google-libphonenumber-7.4.23.tgz#c44c9125d45f042943694d605fd8d8d796cafc3b" integrity sha512-C3ydakLTQa8HxtYf9ge4q6uT9krDX8smSIxmmW3oACFi5g5vv6T068PRExF7UyWbWpuYiDG8Nm24q2X5XhcZWw== @@ -5238,6 +5996,39 @@ dependencies: "@types/node" "*" +"@types/hapi__catbox@*": + version "10.2.5" + resolved "https://registry.yarnpkg.com/@types/hapi__catbox/-/hapi__catbox-10.2.5.tgz#4eb5e2fbb966acd2a3c2e86f935b7de95aceb7dd" + integrity sha512-vomIMP6dUDSbiasbPglH5LJvnnl8jFmRTjPgPl4l9Vi1L9fto3VXJQZtl8LzyIQUUoocyT5bmvWeYWsVgxAHQg== + +"@types/hapi__hapi@20.0.9": + version "20.0.9" + resolved "https://registry.yarnpkg.com/@types/hapi__hapi/-/hapi__hapi-20.0.9.tgz#9d570846c96268266a14c970c13aeeaccfc8e172" + integrity sha512-fGpKScknCKZityRXdZgpCLGbm41R1ppFgnKHerfZlqOOlCX/jI129S6ghgBqkqCE8m9A0CIu1h7Ch04lD9KOoA== + dependencies: + "@hapi/boom" "^9.0.0" + "@hapi/iron" "^6.0.0" + "@hapi/podium" "^4.1.3" + "@types/hapi__catbox" "*" + "@types/hapi__mimos" "*" + "@types/hapi__shot" "*" + "@types/node" "*" + joi "^17.3.0" + +"@types/hapi__mimos@*": + version "4.1.4" + resolved "https://registry.yarnpkg.com/@types/hapi__mimos/-/hapi__mimos-4.1.4.tgz#4f8a1c58345fc468553708d3cb508724aa081bd9" + integrity sha512-i9hvJpFYTT/qzB5xKWvDYaSXrIiNqi4ephi+5Lo6+DoQdwqPXQgmVVOZR+s3MBiHoFqsCZCX9TmVWG3HczmTEQ== + dependencies: + "@types/mime-db" "*" + +"@types/hapi__shot@*": + version "4.1.4" + resolved "https://registry.yarnpkg.com/@types/hapi__shot/-/hapi__shot-4.1.4.tgz#17d418537bc180ac042361d9a331892a31a61583" + integrity sha512-AhEirOGy2ajtdV9WE/JqPkGeCH8lpgcSEQxn0ZNJkTvxkOv5DfZEXGit3l5J9P1VoQFAAHGNLVGWI5IWCiQf9A== + dependencies: + "@types/node" "*" + "@types/hdkey@^0.7.0": version "0.7.1" resolved "https://registry.yarnpkg.com/@types/hdkey/-/hdkey-0.7.1.tgz#9bc63ebbe96b107b277b65ea7a95442a677d0d61" @@ -5245,15 +6036,25 @@ dependencies: "@types/node" "*" +"@types/http-assert@*": + version "1.5.4" + resolved "https://registry.yarnpkg.com/@types/http-assert/-/http-assert-1.5.4.tgz#2ee41bf930d871a76cdbee5c385ccbb3604302e6" + integrity sha512-/6M9aaVk+avzCsrv1lt39AlFw4faCNI6aGll91Rxj38ZE5JI8AxApyQIRy+i1McjiJiuQ0sfuoMLxqq374ZIbA== + "@types/http-cache-semantics@*": version "4.0.1" resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== -"@types/humanize-duration@^3.18.0": - version "3.27.1" - resolved "https://registry.yarnpkg.com/@types/humanize-duration/-/humanize-duration-3.27.1.tgz#f14740d1f585a0a8e3f46359b62fda8b0eaa31e7" - integrity sha512-K3e+NZlpCKd6Bd/EIdqjFJRFHbrq5TzPPLwREk5Iv/YoIjQrs6ljdAUCo+Lb2xFlGNOjGSE0dqsVD19cZL137w== +"@types/http-errors@*": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.3.tgz#c54e61f79b3947d040f150abd58f71efb422ff62" + integrity sha512-pP0P/9BnCj1OVvQR2lF41EkDG/lWWnDyA203b/4Fmi2eTyORnBtcDoKDwjWQthELrBvWkMOrvSOnZ8OVlW6tXA== + +"@types/humanize-duration@^3.27.0": + version "3.27.2" + resolved "https://registry.yarnpkg.com/@types/humanize-duration/-/humanize-duration-3.27.2.tgz#b07c16ce606cf4d5839ac3bf06d0722dcdd425fe" + integrity sha512-KOGjfVAD8CHMgXL6z96f7eCNRFUENKa2BG87l7JsSg9ZA6lRFsipZ0faF4kKFqnzxirVgXmOnWqTLAKUog1h/g== "@types/inquirer@^6.5.0": version "6.5.0" @@ -5263,10 +6064,19 @@ "@types/through" "*" rxjs "^6.4.0" -"@types/is-base64@^1.1.0": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@types/is-base64/-/is-base64-1.1.1.tgz#a17d2b0075f637f80f9ab5f76f0071a65f6965d4" - integrity sha512-JgnGhP+MeSHEQmvxcobcwPEP4Ew56voiq9/0hmP/41lyQ/3gBw/ZCIRy2v+QkEOdeCl58lRcrf6+Y6WMlJGETA== +"@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" + integrity sha512-69LyhUgrXdgcNDv7ogs1qXZomnfOEnSmrmMFqKgt1XMJxmoOSG/u3wYy13yACIfKuMJ8IhKgHafDO3sx19zVQQ== + dependencies: + "@types/node" "*" + +"@types/is-ci@^3.0.0": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/is-ci/-/is-ci-3.0.3.tgz#6042f2fe12a16b2616deabb11d9a422f46a2ec03" + integrity sha512-FdHbjLiN2e8fk9QYQyVYZrK8svUDJpxSaSWLUga8EZS1RGAvvrqM9zbVARBtQuYPeLgnJxM2xloOswPwj1o2cQ== + dependencies: + ci-info "^3.1.0" "@types/isomorphic-fetch@0.0.31": version "0.0.31" @@ -5300,22 +6110,15 @@ expect "^29.0.0" pretty-format "^29.0.0" -"@types/json-schema@^7.0.6": - version "7.0.11" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" - integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== - "@types/json5@^0.0.29": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== -"@types/jsonwebtoken@^8.5.9": - version "8.5.9" - resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.9.tgz#2c064ecb0b3128d837d2764aa0b117b0ff6e4586" - integrity sha512-272FMnFGzAVMGtu9tkr29hRL6bZj4Zs1KZNeHLnKqAvp06tAIcarTMwOh8/8bz4FmKRcMxZhZNeUAQsNLoiPhg== - dependencies: - "@types/node" "*" +"@types/keygrip@*": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@types/keygrip/-/keygrip-1.0.4.tgz#c4015f09e66633bdea5d26916810001272b1ac6a" + integrity sha512-/tjWYD8StMrINelsrHNmpXceo9s3/Y22AzePH1qCvXIgmz/aQp2YFFr6HqhNQVIOdcvaVyp5GS+yjHGuF7Rwsg== "@types/keyv@^3.1.4": version "3.1.4" @@ -5324,6 +6127,48 @@ dependencies: "@types/node" "*" +"@types/koa-compose@*": + version "3.2.7" + resolved "https://registry.yarnpkg.com/@types/koa-compose/-/koa-compose-3.2.7.tgz#7bbb084acc58836590eb11d3c2600c0ff695d3dc" + integrity sha512-smtvSL/oLICPuenxy73OmxKGh42VVfn2o2eutReH1yjij0LmxADBpGcAJbp4N+yJjPapPN7jAX9p7Ue0JMQ/Ag== + dependencies: + "@types/koa" "*" + +"@types/koa@*": + version "2.13.10" + resolved "https://registry.yarnpkg.com/@types/koa/-/koa-2.13.10.tgz#2c2a1cdf1252d654b05f444194328a3d23a880c4" + integrity sha512-weKc5IBeORLDGwD1FMgPjaZIg0/mtP7KxXAXEzPRCN78k274D9U2acmccDNPL1MwyV40Jj+hQQ5N2eaV6O0z8g== + dependencies: + "@types/accepts" "*" + "@types/content-disposition" "*" + "@types/cookies" "*" + "@types/http-assert" "*" + "@types/http-errors" "*" + "@types/keygrip" "*" + "@types/koa-compose" "*" + "@types/node" "*" + +"@types/koa@2.13.6": + version "2.13.6" + resolved "https://registry.yarnpkg.com/@types/koa/-/koa-2.13.6.tgz#6dc14e727baf397310aa6f414ebe5d144983af42" + integrity sha512-diYUfp/GqfWBAiwxHtYJ/FQYIXhlEhlyaU7lB/bWQrx4Il9lCET5UwpFy3StOAohfsxxvEQ11qIJgT1j2tfBvw== + dependencies: + "@types/accepts" "*" + "@types/content-disposition" "*" + "@types/cookies" "*" + "@types/http-assert" "*" + "@types/http-errors" "*" + "@types/keygrip" "*" + "@types/koa-compose" "*" + "@types/node" "*" + +"@types/koa__router@8.0.7": + version "8.0.7" + resolved "https://registry.yarnpkg.com/@types/koa__router/-/koa__router-8.0.7.tgz#663d69d5ddebff5aaca27c0594430b3ba6ea20be" + integrity sha512-OB3Ax75nmTP+WR9AgdzA42DI7YmBtiNKN2g1Wxl+d5Dyek9SWt740t+ukwXSmv/jMBCUPyV3YEI93vZHgdP7UQ== + dependencies: + "@types/koa" "*" + "@types/ledgerhq__hw-transport-node-hid@^4.22.2": version "4.22.2" resolved "https://registry.yarnpkg.com/@types/ledgerhq__hw-transport-node-hid/-/ledgerhq__hw-transport-node-hid-4.22.2.tgz#f3de58b9b49b461dd96e5bfb646328c242e70aca" @@ -5348,10 +6193,10 @@ "@types/abstract-leveldown" "*" "@types/node" "*" -"@types/lodash@^4.14.104", "@types/lodash@^4.14.170": - version "4.14.194" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.194.tgz#b71eb6f7a0ff11bff59fc987134a093029258a76" - integrity sha512-r22s9tAS7imvBt2lyHC9B8AGwWnXaYb1tY09oyLkXDs4vArpYJzw09nj8MLx5VfciBPGIb+ZwG0ssYnEPJxn/g== +"@types/lodash@^4.14.199": + version "4.14.200" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.200.tgz#435b6035c7eba9cdf1e039af8212c9e9281e7149" + integrity sha512-YI/M/4HRImtNf3pJgbF+W6FrXovqj+T+/HpENLTooK9PnkacBsDpeP3IpHab40CClUfhNmdM2WTNP2sa2dni5Q== "@types/long@^3.0.0": version "3.0.32" @@ -5375,6 +6220,18 @@ dependencies: decimal.js "^10.0.0" +"@types/memcached@^2.2.6": + version "2.2.9" + resolved "https://registry.yarnpkg.com/@types/memcached/-/memcached-2.2.9.tgz#dba55dadbd442573d5a12f1cd2cdd6bd64d86537" + integrity sha512-3WVct68tDdjQ0lkOim8PcULZCoPtpYeh41J6IoAV/cTjh0LXD2DThDGLaWRPLtmLoDQ6sO390hgfrcKwYUD52A== + dependencies: + "@types/node" "*" + +"@types/mime-db@*": + version "1.43.3" + resolved "https://registry.yarnpkg.com/@types/mime-db/-/mime-db-1.43.3.tgz#f7bec64a9a62ddded3371e82862c0516539710e8" + integrity sha512-vg0UsF1p1Qi/8iCARoie7F/Ng92zo7tQlL+sqE15GonkKVl55n/0vB6jSbrYTgDO0PSx9pKfGG1iZg9gJum3wA== + "@types/mime@*": version "3.0.1" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" @@ -5427,6 +6284,13 @@ resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197" integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== +"@types/mysql@2.15.22": + version "2.15.22" + resolved "https://registry.yarnpkg.com/@types/mysql/-/mysql-2.15.22.tgz#8705edb9872bf4aa9dbc004cd494e00334e5cdb4" + integrity sha512-wK1pzsJVVAjYCSZWQoWHziQZbNggXFDUEIGf54g4ZM/ERuP86uGdWeKZWMYlqTPMZfHJJvLPyogXGvCOg87yLQ== + dependencies: + "@types/node" "*" + "@types/node-fetch@^2.5.7": version "2.6.3" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.3.tgz#175d977f5e24d93ad0f57602693c435c57ad7e80" @@ -5447,11 +6311,6 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== -"@types/node@10.12.18": - version "10.12.18" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67" - integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ== - "@types/node@11.11.6": version "11.11.6" resolved "https://registry.yarnpkg.com/@types/node/-/node-11.11.6.tgz#df929d1bb2eee5afdda598a41930fe50b43eaa6a" @@ -5472,7 +6331,7 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== -"@types/node@^12.11.7", "@types/node@^12.12.17", "@types/node@^12.12.6", "@types/node@^12.6.1": +"@types/node@^12.12.6", "@types/node@^12.6.1", "@types/node@^12.7.1": version "12.20.55" resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== @@ -5509,6 +6368,31 @@ dependencies: "@types/node" "*" +"@types/pg-pool@2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/pg-pool/-/pg-pool-2.0.4.tgz#b5c60f678094ff3acf3442628a7f708928fcf263" + integrity sha512-qZAvkv1K3QbmHHFYSNRYPkRjOWRLBYrL4B9c+wG0GSVGBw0NtJwPcgx/DSddeDJvRGMHCEQ4VMEVfuJ/0gZ3XQ== + dependencies: + "@types/pg" "*" + +"@types/pg@*": + version "8.10.7" + resolved "https://registry.yarnpkg.com/@types/pg/-/pg-8.10.7.tgz#2f172598272e581e72cba640026e5152b5a3d5c3" + integrity sha512-ksJqHipwYaSEHz9e1fr6H6erjoEdNNaOxwyJgPx9bNeaqOW3iWBQgVHfpwiSAoqGzchfc+ZyRLwEfeCcyYD3uQ== + dependencies: + "@types/node" "*" + pg-protocol "*" + pg-types "^4.0.1" + +"@types/pg@8.6.1": + version "8.6.1" + resolved "https://registry.yarnpkg.com/@types/pg/-/pg-8.6.1.tgz#099450b8dc977e8197a44f5229cedef95c8747f9" + integrity sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w== + dependencies: + "@types/node" "*" + pg-protocol "*" + pg-types "^2.2.0" + "@types/pg@^7.14.3": version "7.14.11" resolved "https://registry.yarnpkg.com/@types/pg/-/pg-7.14.11.tgz#daf5555504a1f7af4263df265d91f140fece52e3" @@ -5584,7 +6468,7 @@ "@types/scheduler" "*" csstype "^3.0.2" -"@types/readable-stream@^2.3.13", "@types/readable-stream@^2.3.5": +"@types/readable-stream@^2.3.13": version "2.3.15" resolved "https://registry.yarnpkg.com/@types/readable-stream/-/readable-stream-2.3.15.tgz#3d79c9ceb1b6a57d5f6e6976f489b9b5384321ae" integrity sha512-oM5JSKQCcICF1wvGgmecmHldZ48OZamtMxcGGVICOJA8o8cahXC1zEVAif8iwoc5j8etxFaRFnf095+CDsuoFQ== @@ -5592,13 +6476,6 @@ "@types/node" "*" safe-buffer "~5.1.1" -"@types/readdir-glob@*": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@types/readdir-glob/-/readdir-glob-1.1.1.tgz#27ac2db283e6aa3d110b14ff9da44fcd1a5c38b1" - integrity sha512-ImM6TmoF8bgOwvehGviEj3tRdRBbQujr1N+0ypaln/GWjaerOB26jb93vsRHmdMtvVQZQebOlqt2HROark87mQ== - dependencies: - "@types/node" "*" - "@types/request@^2.48.1": version "2.48.8" resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.8.tgz#0b90fde3b655ab50976cb8c5ac00faca22f5a82c" @@ -5658,6 +6535,11 @@ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.8.tgz#508a27995498d7586dcecd77c25e289bfaf90c59" integrity sha512-D/2EJvAlCEtYFEYmmlGwbGXuK886HzyCc3nZX/tkFTQdEU8jZDAgiv08P162yB17y4ZXZoq7yFAnW4GDBb9Now== +"@types/semver@^7.5.0": + version "7.5.4" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.4.tgz#0a41252ad431c473158b22f9bfb9a63df7541cff" + integrity sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ== + "@types/send@*": version "0.17.1" resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.1.tgz#ed4932b8a2a805f1fe362a70f4e62d0ac994e301" @@ -5674,6 +6556,11 @@ "@types/mime" "*" "@types/node" "*" +"@types/shimmer@^1.0.2": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@types/shimmer/-/shimmer-1.0.4.tgz#8fa2317517280ecdd64078afc010809cb67c37b7" + integrity sha512-hsughtxFsdJ9+Gxd/qH8zHE+KT6YEAxx9hJLoSXhxTBKHMQ2NMhN23fRJ75M9RRn2hDMNn13H3gS1EktA9VgDA== + "@types/solidity-parser-antlr@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@types/solidity-parser-antlr/-/solidity-parser-antlr-0.2.3.tgz#bb2d9c6511bf483afe4fc3e2714da8a924e59e3f" @@ -5696,21 +6583,6 @@ resolved "https://registry.yarnpkg.com/@types/string-hash/-/string-hash-1.1.1.tgz#4c336e61d1e13ce2d3efaaa5910005fd080e106b" integrity sha512-ijt3zdHi2DmZxQpQTmozXszzDo78V4R3EdvX0jFMfnMH2ZzQSmCbaWOMPGXFUYSzSIdStv78HDjg32m5dxc+tA== -"@types/superagent@*": - version "4.1.16" - resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-4.1.16.tgz#12c9c16f232f9d89beab91d69368f96ce8e2d881" - integrity sha512-tLfnlJf6A5mB6ddqF159GqcDizfzbMUB1/DeT59/wBNqzRTNNKsaw79A/1TZ84X+f/EwWH8FeuSkjlCLyqS/zQ== - dependencies: - "@types/cookiejar" "*" - "@types/node" "*" - -"@types/supertest@^2.0.12": - version "2.0.12" - resolved "https://registry.yarnpkg.com/@types/supertest/-/supertest-2.0.12.tgz#ddb4a0568597c9aadff8dbec5b2e8fddbe8692fc" - integrity sha512-X3HPWTwXRerBZS7Mo1k6vMVR1Z6zmJcDVn5O/31whe0tnjE4te6ZJSJGq1RiqHPjzPdMTfjCFogDJmwng9xHaQ== - dependencies: - "@types/superagent" "*" - "@types/tar-fs@*": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/tar-fs/-/tar-fs-2.0.1.tgz#6391dcad1b03dea2d79fac07371585ab54472bb1" @@ -5740,6 +6612,13 @@ dependencies: "@types/tar-fs" "*" +"@types/tedious@^4.0.10": + version "4.0.13" + resolved "https://registry.yarnpkg.com/@types/tedious/-/tedious-4.0.13.tgz#31fa5968083218c114b9259b1653ef84a5ddbaa9" + integrity sha512-eCADRqah0uHMUNVHJ/0Yz4drscJ5tZ+IQ/i+nDs7/nR8R6RqLhJaplklvMe3EsMraxOWmp4mTqYi0Xo6ik1DpQ== + dependencies: + "@types/node" "*" + "@types/through@*": version "0.0.30" resolved "https://registry.yarnpkg.com/@types/through/-/through-0.0.30.tgz#e0e42ce77e897bd6aead6f6ea62aeb135b8a3895" @@ -5757,25 +6636,10 @@ resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397" integrity sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw== -"@types/triple-beam@^1.3.2": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.2.tgz#38ecb64f01aa0d02b7c8f4222d7c38af6316fef8" - integrity sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g== - -"@types/underscore@^1.8.8": - version "1.11.4" - resolved "https://registry.yarnpkg.com/@types/underscore/-/underscore-1.11.4.tgz#62e393f8bc4bd8a06154d110c7d042a93751def3" - integrity sha512-uO4CD2ELOjw8tasUrAhvnn2W4A0ZECOvMjCivJr4gA9pGgjv+qxKWY9GLTMVEK8ej85BxQOocUyE7hImmSQYcg== - "@types/utf8@^2.1.6": version "2.1.6" resolved "https://registry.yarnpkg.com/@types/utf8/-/utf8-2.1.6.tgz#430cabb71a42d0a3613cce5621324fe4f5a25753" - integrity sha512-pRs2gYF5yoKYrgSaira0DJqVg2tFuF+Qjp838xS7K+mJyY2jJzjsrl6y17GbIa4uMRogMbxs+ghNCvKg6XyNrA== - -"@types/uuid@^7.0.3": - version "7.0.5" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-7.0.5.tgz#b1d2f772142a301538fae9bdf9cf15b9f2573a29" - integrity sha512-hKB88y3YHL8oPOs/CNlaXtjWn93+Bs48sDQR37ZUqG2tLeCS7EA1cmnkKsuQsub9OKEB/y/Rw9zqJqqNSbqVlQ== + integrity sha512-pRs2gYF5yoKYrgSaira0DJqVg2tFuF+Qjp838xS7K+mJyY2jJzjsrl6y17GbIa4uMRogMbxs+ghNCvKg6XyNrA== "@types/web3-provider-engine@^14.0.0": version "14.0.1" @@ -5791,6 +6655,13 @@ dependencies: web3 "*" +"@types/ws@^8.5.4": + version "8.5.8" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.8.tgz#13efec7bd439d0bdf2af93030804a94f163b1430" + integrity sha512-flUksGIQCnJd6sZ1l5dqCEG/ksaoAg/eUwiLAGTJQcfgvZJKF++Ta4bJA6A5aPSJmsr+xlseHn4KLgVlNnvPTg== + dependencies: + "@types/node" "*" + "@types/yargs-parser@*": version "21.0.0" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" @@ -5815,10 +6686,10 @@ dependencies: "@types/yargs-parser" "*" -"@xmldom/xmldom@^0.8.3": - version "0.8.7" - resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.8.7.tgz#8b1e39c547013941974d83ad5e9cf5042071a9a0" - integrity sha512-sI1Ly2cODlWStkINzqGrZ8K6n+MTSbAeQnAipGyL+KZCXuHaRlj2gyyy8B/9MvsFFqN7XHryQnB2QwhzvJXovg== +"@wagmi/chains@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@wagmi/chains/-/chains-1.6.0.tgz#eb992ad28dbaaab729b5bcab3e5b461e8a035656" + integrity sha512-5FRlVxse5P4ZaHG3GTvxwVANSmYJas1eQrTBHhjxVtqXoorm0aLmCHbhmN8Xo1yu09PaWKlleEvfE98yH4AgIw== "@yarnpkg/lockfile@^1.1.0": version "1.1.0" @@ -5840,7 +6711,7 @@ dependencies: argparse "^2.0.1" -JSONStream@^1.0.4, JSONStream@^1.2.1: +JSONStream@^1.0.4: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== @@ -5874,6 +6745,11 @@ abi-to-sol@^0.6.6: prettier "^2.7.1" prettier-plugin-solidity "^1.0.0-dev.23" +abitype@0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/abitype/-/abitype-0.9.3.tgz#294d25288ee683d72baf4e1fed757034e3c8c277" + integrity sha512-dz4qCQLurx97FQhnb/EIYTk/ldQ+oafEDUqC0VVIeQS1Q48/YWt/9YNfMmp9SLFqN41ktxny3c8aYxHjmFIB/w== + abort-controller@3.0.0, abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" @@ -5961,7 +6837,7 @@ abstract-leveldown@~6.2.1, abstract-leveldown@~6.2.3: level-supports "~1.0.0" xtend "~4.0.0" -accepts@^1.3.5, accepts@~1.3.5, accepts@~1.3.8: +accepts@^1.3.5, accepts@~1.3.8: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== @@ -5969,12 +6845,17 @@ accepts@^1.3.5, accepts@~1.3.5, accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" +acorn-import-assertions@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" + integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== + acorn-jsx@^5.0.0: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn-walk@^8.1.1, acorn-walk@^8.2.0: +acorn-walk@^8.1.1: version "8.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== @@ -5984,24 +6865,15 @@ acorn@^6.0.7: resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== -acorn@^8.4.1, acorn@^8.7.0: +acorn@^8.4.1: version "8.8.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== -adal-node@^0.2.2: - version "0.2.4" - resolved "https://registry.yarnpkg.com/adal-node/-/adal-node-0.2.4.tgz#881beed9d493b76a86706ad5c8dc6f60eff04520" - integrity sha512-zIcvbwQFKMUtKxxj8YMHeTT1o/TPXfVNsTXVgXD8sxwV6h4AFQgK77dRciGhuEF9/Sdm3UQPJVPc/6XxrccSeA== - dependencies: - "@xmldom/xmldom" "^0.8.3" - async "^2.6.3" - axios "^0.21.1" - date-utils "*" - jws "3.x.x" - underscore ">= 1.3.1" - uuid "^3.1.0" - xpath.js "~1.1.0" +acorn@^8.8.2: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== add-stream@^1.0.0: version "1.0.0" @@ -6018,7 +6890,7 @@ aes-js@^3.1.1, aes-js@^3.1.2: resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.1.2.tgz#db9aabde85d5caabbfc0d4f2a4446960f627146a" integrity sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ== -agent-base@6, agent-base@^6.0.0, agent-base@^6.0.2: +agent-base@6, agent-base@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== @@ -6032,7 +6904,7 @@ agent-base@^4.3.0: dependencies: es6-promisify "^5.0.0" -agentkeepalive@^4.1.3, agentkeepalive@^4.2.1: +agentkeepalive@^4.2.1: version "4.3.0" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.3.0.tgz#bb999ff07412653c1803b3ced35e50729830a255" integrity sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg== @@ -6056,7 +6928,7 @@ ajv-formats@^2.1.1: dependencies: ajv "^8.0.0" -ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.3, ajv@^6.12.5, ajv@^6.12.6, ajv@^6.6.1, ajv@^6.9.1: +ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.5, ajv@^6.6.1, ajv@^6.9.1: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -6088,12 +6960,10 @@ amdefine@>=0.0.4: resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" integrity sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg== -ansi-align@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" - integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== - dependencies: - string-width "^4.1.0" +ansi-color@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/ansi-color/-/ansi-color-0.2.1.tgz#3e75c037475217544ed763a8db5709fa9ae5bf9a" + integrity sha512-bF6xLaZBLpOQzgYUtYEhJx090nPSZk1BQ/q2oyBK9aMMcJHzx9uXGCjI2Y+LebsN4Jwoykr0V9whbPiogdyHoQ== ansi-colors@3.2.3: version "3.2.3" @@ -6105,7 +6975,7 @@ ansi-colors@4.1.1: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== -ansi-colors@^4.1.1: +ansi-colors@^4.1.1, ansi-colors@^4.1.3: version "4.1.3" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== @@ -6122,7 +6992,7 @@ ansi-escapes@^4.2.1, ansi-escapes@^4.3.0: dependencies: type-fest "^0.21.3" -ansi-regex@^2.0.0, ansi-regex@^2.1.1: +ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA== @@ -6142,11 +7012,6 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA== - ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -6321,43 +7186,6 @@ aproba@^1.0.3: resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== -archiver-utils@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-2.1.0.tgz#e8a460e94b693c3e3da182a098ca6285ba9249e2" - integrity sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw== - dependencies: - glob "^7.1.4" - graceful-fs "^4.2.0" - lazystream "^1.0.0" - lodash.defaults "^4.2.0" - lodash.difference "^4.5.0" - lodash.flatten "^4.4.0" - lodash.isplainobject "^4.0.6" - lodash.union "^4.6.0" - normalize-path "^3.0.0" - readable-stream "^2.0.0" - -archiver@^5.0.0: - version "5.3.1" - resolved "https://registry.yarnpkg.com/archiver/-/archiver-5.3.1.tgz#21e92811d6f09ecfce649fbefefe8c79e57cbbb6" - integrity sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w== - dependencies: - archiver-utils "^2.1.0" - async "^3.2.3" - buffer-crc32 "^0.2.1" - readable-stream "^3.6.0" - readdir-glob "^1.0.0" - tar-stream "^2.2.0" - zip-stream "^4.1.0" - -are-we-there-yet@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" - integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== - dependencies: - delegates "^1.0.0" - readable-stream "^3.6.0" - are-we-there-yet@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz#679df222b278c64f2cdba1175cdc00b0d96164bd" @@ -6469,16 +7297,11 @@ array-differ@^3.0.0: resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b" integrity sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg== -array-flatten@1.1.1, array-flatten@^1.0.0: +array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== -array-flatten@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-3.0.0.tgz#6428ca2ee52c7b823192ec600fa3ed2f157cd541" - integrity sha512-zPMVc3ZYlGLNk4mpK1NzP2wg0ml9t7fUgDsayR5Y5rSzxQilzR9FGu/EH2jQOcKSAeAfWeylyW8juy3OkWRvNA== - array-ify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" @@ -6516,7 +7339,17 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ== -array.prototype.map@^1.0.1, array.prototype.map@^1.0.5: +array.prototype.flat@^1.2.3: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18" + integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +array.prototype.map@^1.0.1: version "1.0.5" resolved "https://registry.yarnpkg.com/array.prototype.map/-/array.prototype.map-1.0.5.tgz#6e43c2fee6c0fb5e4806da2dc92eb00970809e55" integrity sha512-gfaKntvwqYIuC7mLLyv2wzZIJqrRhn5PZ9EfFejSx6a78sV7iDsGpG9P+3oUPtm1Rerqm6nrKS4FYuTIvWfo3g== @@ -6538,6 +7371,19 @@ array.prototype.reduce@^1.0.5: es-array-method-boxes-properly "^1.0.0" is-string "^1.0.7" +arraybuffer.prototype.slice@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz#98bd561953e3e74bb34938e77647179dfe6e9f12" + integrity sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + is-array-buffer "^3.0.2" + is-shared-array-buffer "^1.0.2" + arrify@^1.0.0, arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -6548,20 +7394,6 @@ arrify@^2.0.0, arrify@^2.0.1: resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== -as-array@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/as-array/-/as-array-1.0.0.tgz#28a6eeeaa5729f1f4eca2047df5e9de1abda0ed1" - integrity sha512-yTEVeqmnVlLJV0j8IAz/mcMGbr88+yX9SqTxyFc1HJwmW8Zy347jEmWFIg34MRqCUS8CXRKy8a8B/9BaoYDW2w== - dependencies: - lodash.isarguments "2.4.x" - lodash.isobject "^2.4.1" - lodash.values "^2.4.1" - -as-array@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/as-array/-/as-array-2.0.0.tgz#4f04805d87f8fce8e511bc2108f8e5e3a287d547" - integrity sha512-1Sd1LrodN0XYxYeZcN1J4xYZvmvTwD5tDWaPUGPIzH1mFsmzsPnVtd2exWhecMjtZk/wYWjNZJiD3b1SLCeJqg== - asap@^2.0.0, asap@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" @@ -6614,13 +7446,6 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw== -ast-types@^0.13.2: - version "0.13.4" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782" - integrity sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w== - dependencies: - tslib "^2.0.1" - astral-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" @@ -6650,7 +7475,7 @@ async-mutex@^0.2.6: dependencies: tslib "^2.0.0" -async-retry@^1.2.1, async-retry@^1.3.3: +async-retry@^1.2.1: version "1.3.3" resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.3.tgz#0e7f36c04d8478e7a58bdbed80cedf977785f280" integrity sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw== @@ -6669,7 +7494,7 @@ async@^2.0.1, async@^2.1.2, async@^2.4.0, async@^2.5.0, async@^2.6.1: dependencies: lodash "^4.17.14" -async@^2.6.3, async@^2.6.4: +async@^2.6.4: version "2.6.4" resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== @@ -6914,18 +7739,6 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" -basic-auth-connect@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/basic-auth-connect/-/basic-auth-connect-1.0.0.tgz#fdb0b43962ca7b40456a7c2bb48fe173da2d2122" - integrity sha512-kiV+/DTgVro4aZifY/hwRwALBISViL5NP4aReaR2EVJEObpbUBHIkdJh/YpcoEiYt7nBodZ6U2ajZeZvSxUCCg== - -basic-auth@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" - integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== - dependencies: - safe-buffer "5.1.2" - bcrypt-pbkdf@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" @@ -6956,12 +7769,19 @@ better-ajv-errors@^0.8.2: jsonpointer "^5.0.0" leven "^3.1.0" +better-path-resolve@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/better-path-resolve/-/better-path-resolve-1.0.0.tgz#13a35a1104cdd48a7b74bf8758f96a1ee613f99d" + integrity sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g== + dependencies: + is-windows "^1.0.0" + big-integer@1.6.36: version "1.6.36" resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.36.tgz#78631076265d4ae3555c04f85e7d9d2f3a071a36" integrity sha512-t70bfa7HYEA1D9idDbmuv7YbsbVkQ+Hp+8KFSul4aE5e/i1bjCNIRYJZlA8Q8p0r9T8cF/RVvwUgRA//FydEyg== -big-integer@^1.6.15, big-integer@^1.6.17, big-integer@^1.6.44: +big-integer@^1.6.15, big-integer@^1.6.44: version "1.6.51" resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== @@ -7003,39 +7823,13 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -binary@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/binary/-/binary-0.3.0.tgz#9f60553bc5ce8c3386f3b553cff47462adecaa79" - integrity sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg== - dependencies: - buffers "~0.1.1" - chainsaw "~0.1.0" - -bindings@^1.2.1, bindings@^1.3.0, bindings@^1.5.0: +bindings@^1.2.1, bindings@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== dependencies: file-uri-to-path "1.0.0" -bintrees@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bintrees/-/bintrees-1.0.2.tgz#49f896d6e858a4a499df85c38fb399b9aff840f8" - integrity sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw== - -bip32@2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/bip32/-/bip32-2.0.5.tgz#e3808a9e97a880dbafd0f5f09ca4a1e14ee275d2" - integrity sha512-zVY4VvJV+b2fS0/dcap/5XLlpqtgwyN8oRkuGgAS1uLOeEp0Yo6Tw2yUTozTtlrMJO3G8n4g/KX/XGFHW6Pq3g== - dependencies: - "@types/node" "10.12.18" - bs58check "^2.1.1" - create-hash "^1.2.0" - create-hmac "^1.1.7" - tiny-secp256k1 "^1.1.3" - typeforce "^1.11.5" - wif "^2.0.6" - bip32@3.1.0, bip32@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/bip32/-/bip32-3.1.0.tgz#ce90e020d0e6b41e891a0122ff053efabcce1ccc" @@ -7048,29 +7842,7 @@ bip32@3.1.0, bip32@^3.1.0: typeforce "^1.11.5" wif "^2.0.6" -bip39@2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/bip39/-/bip39-2.5.0.tgz#51cbd5179460504a63ea3c000db3f787ca051235" - integrity sha512-xwIx/8JKoT2+IPJpFEfXoWdYwP7UVAoUxxLNfGCfVowaJE7yg1Y5B1BVPqlUNsBq5/nGwmFkwRJ8xDW4sX8OdA== - dependencies: - create-hash "^1.1.0" - pbkdf2 "^3.0.9" - randombytes "^2.0.1" - safe-buffer "^5.0.1" - unorm "^1.3.3" - -bip39@^2.2.0, bip39@^2.5.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/bip39/-/bip39-2.6.0.tgz#9e3a720b42ec8b3fbe4038f1e445317b6a99321c" - integrity sha512-RrnQRG2EgEoqO24ea+Q/fftuPUZLmrEM3qNhhGsA3PbaXaCW791LTzPuVyx/VprXQcTbPJ3K3UeTna8ZnVl2sg== - dependencies: - create-hash "^1.1.0" - pbkdf2 "^3.0.9" - randombytes "^2.0.1" - safe-buffer "^5.0.1" - unorm "^1.3.3" - -"bip39@https://github.com/bitcoinjs/bip39#d8ea080a18b40f301d4e2219a2991cd2417e83c2": +bip39@2.5.0, bip39@^2.2.0, bip39@^2.5.0, "bip39@https://github.com/bitcoinjs/bip39#d8ea080a18b40f301d4e2219a2991cd2417e83c2": version "3.0.3" resolved "https://github.com/bitcoinjs/bip39#d8ea080a18b40f301d4e2219a2991cd2417e83c2" dependencies: @@ -7094,13 +7866,6 @@ bl@^1.0.0: readable-stream "^2.3.5" safe-buffer "^5.1.1" -bl@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/bl/-/bl-3.0.1.tgz#1cbb439299609e419b5a74d7fce2f8b37d8e5c6f" - integrity sha512-jrCW5ZhfQ/Vt07WX1Ngs+yn9BDqPL/gw28S7s9H6QK/gupnizNzJAss5akW20ISgOrbLTlXOOCTJeNUQqruAWQ== - dependencies: - readable-stream "^3.0.1" - bl@^4.0.3, bl@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" @@ -7115,37 +7880,21 @@ 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@https://github.com/celo-org/blind-threshold-bls-wasm#e1e2f8a": - version "0.1.0" - resolved "https://github.com/celo-org/blind-threshold-bls-wasm#e1e2f8a1ab5154c2f0b1c55cb0d61fbb1d907208" - -"bls12377js@https://github.com/celo-org/bls12377js#cb38a4cfb643c778619d79b20ca3e5283a2122a6": - version "0.1.0" - resolved "https://github.com/celo-org/bls12377js#cb38a4cfb643c778619d79b20ca3e5283a2122a6" - dependencies: - "@stablelib/blake2xs" "0.10.4" - "@types/node" "^12.11.7" - big-integer "^1.6.44" - chai "^4.2.0" - mocha "^6.2.2" - ts-node "^8.4.1" - typescript "^3.6.4" +"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== bluebird@^2.9.33: version "2.11.0" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1" integrity sha512-UfFSr22dmHPQqPP9XWHRhq+gWnHCYguQGkXQlbyPtW5qTnhFWA8/iXg765tH0cAjy7l/zPJ1aBTO0g5XgA7kvQ== -bluebird@^3.5.0, bluebird@^3.5.2, bluebird@^3.5.4: +bluebird@^3.5.0, bluebird@^3.5.2: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== -bluebird@~3.4.1: - version "3.4.7" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.7.tgz#f72d760be09b7f76d08ed8fae98b289a8d05fab3" - integrity sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA== - bn.js@4.11.6: version "4.11.6" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" @@ -7166,7 +7915,7 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.0, bn.js@^4.11.6, bn.js@^4.11.8, bn.js@^ resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== -bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2, bn.js@^5.1.3, bn.js@^5.2.0, bn.js@^5.2.1: +bn.js@^5.0.0, bn.js@^5.1.0, bn.js@^5.1.1, bn.js@^5.1.2, bn.js@^5.1.3, bn.js@^5.2.0, bn.js@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== @@ -7189,7 +7938,7 @@ body-parser@1.20.1: type-is "~1.6.18" unpipe "1.0.0" -body-parser@^1.16.0, body-parser@^1.18.3, body-parser@^1.19.0: +body-parser@^1.16.0, body-parser@^1.19.0: version "1.20.2" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== @@ -7212,39 +7961,6 @@ boolbase@^1.0.0: resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== -bottleneck@^2.15.3: - version "2.19.5" - resolved "https://registry.yarnpkg.com/bottleneck/-/bottleneck-2.19.5.tgz#5df0b90f59fd47656ebe63c78a98419205cadd91" - integrity sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw== - -boxen@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64" - integrity sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ== - dependencies: - ansi-align "^3.0.0" - camelcase "^5.3.1" - chalk "^3.0.0" - cli-boxes "^2.2.0" - string-width "^4.1.0" - term-size "^2.1.0" - type-fest "^0.8.1" - widest-line "^3.1.0" - -boxen@^5.0.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" - integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ== - dependencies: - ansi-align "^3.0.0" - camelcase "^6.2.0" - chalk "^4.1.0" - cli-boxes "^2.2.1" - string-width "^4.2.2" - type-fest "^0.20.2" - widest-line "^3.1.0" - wrap-ansi "^7.0.0" - brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -7292,6 +8008,13 @@ braces@^3.0.2, braces@~3.0.2: dependencies: fill-range "^7.0.1" +breakword@^1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/breakword/-/breakword-1.0.6.tgz#242506e7b871b7fad1bce8dc05cb0f2a129c12bd" + integrity sha512-yjxDAYyK/pBvws9H4xKYpLDpYKEH6CzrBPAuXq3x18I+c/2MkVtT3qAr7Oloi6Dss9qNhPVueAAVU1CSeNDIXw== + dependencies: + wcwidth "^1.0.1" + brorand@^1.0.1, brorand@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" @@ -7411,7 +8134,7 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" -btoa@1.2.1, btoa@^1.2.1: +btoa@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/btoa/-/btoa-1.2.1.tgz#01a9909f8b2c93f6bf680ba26131eb30f7fa3d73" integrity sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g== @@ -7429,7 +8152,7 @@ buffer-alloc@^1.2.0: buffer-alloc-unsafe "^1.1.0" buffer-fill "^1.0.0" -buffer-crc32@^0.2.1, buffer-crc32@^0.2.13, buffer-crc32@~0.2.3: +buffer-crc32@~0.2.3: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== @@ -7449,11 +8172,6 @@ buffer-from@1.1.2, buffer-from@^1.0.0, buffer-from@^1.1.1: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -buffer-indexof-polyfill@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz#d2732135c5999c64b277fcf9b1abe3498254729c" - integrity sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A== - buffer-reverse@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/buffer-reverse/-/buffer-reverse-1.0.1.tgz#49283c8efa6f901bc01fa3304d06027971ae2f60" @@ -7506,11 +8224,6 @@ buffer@^5.0.5, buffer@^5.2.1, buffer@^5.4.3, buffer@^5.5.0, buffer@^5.6.0: base64-js "^1.3.1" ieee754 "^1.1.13" -buffers@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb" - integrity sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ== - bufferutil@4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.5.tgz#da9ea8166911cc276bf677b8aed2d02d31f59028" @@ -7525,6 +8238,16 @@ bufferutil@^4.0.1: dependencies: node-gyp-build "^4.3.0" +bufrw@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/bufrw/-/bufrw-1.3.0.tgz#28d6cfdaf34300376836310f5c31d57eeb40c8fa" + integrity sha512-jzQnSbdJqhIltU9O5KUiTtljP9ccw2u5ix59McQy4pV2xGhVLhRZIndY8GIrgh5HjXa6+QJ9AQhOd2QWQizJFQ== + dependencies: + ansi-color "^0.2.1" + error "^7.0.0" + hexer "^1.5.0" + xtend "^4.0.0" + builtin-modules@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" @@ -7550,14 +8273,6 @@ bunyan-debug-stream@2.0.0: colors "^1.0.3" exception-formatter "^1.0.4" -bunyan-debug-stream@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/bunyan-debug-stream/-/bunyan-debug-stream-1.1.2.tgz#3d09a788a8ddf37a23b6840e7e19cf46239bc7b4" - integrity sha512-mNU4QelBu9tUyE6VA0+AQdyillEMefx/2h7xkNL1Uvhw5w9JWtwGWAb7Rdnmj9opmwEaPrRvnJSy2+c1q47+sA== - dependencies: - colors "1.4.0" - exception-formatter "^1.0.4" - bunyan-debug-stream@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/bunyan-debug-stream/-/bunyan-debug-stream-2.0.1.tgz#9bd7c7e30c7b2cf711317e9d37529b0464c3b164" @@ -7581,16 +8296,6 @@ bunyan@1.8.12: mv "~2" safe-json-stringify "~1" -bunyan@^1.8.12: - version "1.8.15" - resolved "https://registry.yarnpkg.com/bunyan/-/bunyan-1.8.15.tgz#8ce34ca908a17d0776576ca1b2f6cbd916e93b46" - integrity sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig== - optionalDependencies: - dtrace-provider "~0.8" - moment "^2.19.3" - mv "~2" - safe-json-stringify "~1" - byte-size@^7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-7.0.1.tgz#b1daf3386de7ab9d706b941a748dbfc71130dee3" @@ -7603,40 +8308,11 @@ bytebuffer@~5: dependencies: long "~3" -bytes@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" - integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== - bytes@3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== -cacache@^15.2.0: - version "15.3.0" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.3.0.tgz#dc85380fb2f556fe3dda4c719bfa0ec875a7f1eb" - integrity sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ== - dependencies: - "@npmcli/fs" "^1.0.0" - "@npmcli/move-file" "^1.0.1" - chownr "^2.0.0" - fs-minipass "^2.0.0" - glob "^7.1.4" - infer-owner "^1.0.4" - lru-cache "^6.0.0" - minipass "^3.1.1" - minipass-collect "^1.0.2" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.2" - mkdirp "^1.0.3" - p-map "^4.0.0" - promise-inflight "^1.0.1" - rimraf "^3.0.2" - ssri "^8.0.1" - tar "^6.0.2" - unique-filename "^1.1.1" - cacache@^16.0.0, cacache@^16.0.6, cacache@^16.1.0: version "16.1.3" resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.1.3.tgz#a02b9f34ecfaf9a78c9f4bc16fceb94d5d67a38e" @@ -7720,6 +8396,15 @@ call-bind@^1.0.0, call-bind@^1.0.2, call-bind@~1.0.2: function-bind "^1.1.1" get-intrinsic "^1.0.2" +call-bind@^1.0.4, call-bind@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" + integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== + dependencies: + function-bind "^1.1.2" + get-intrinsic "^1.2.1" + set-function-length "^1.1.1" + call-me-maybe@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.2.tgz#03f964f19522ba643b1b0693acb9152fe2074baa" @@ -7864,7 +8549,7 @@ chai-subset@^1.6.0: resolved "https://registry.yarnpkg.com/chai-subset/-/chai-subset-1.6.0.tgz#a5d0ca14e329a79596ed70058b6646bd6988cfe9" integrity sha512-K3d+KmqdS5XKW5DWPd5sgNffL3uxdDe+6GdnJh3AYPhwnBGRY5urfvfcbRtWIvvpz+KxkL9FeBB6MZewLUNwug== -chai@^4.0.1, chai@^4.2.0, chai@^4.3.6, chai@^4.3.7: +chai@^4.0.1, chai@^4.3.6, chai@^4.3.7: version "4.3.7" resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.7.tgz#ec63f6df01829088e8bf55fca839bcd464a8ec51" integrity sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A== @@ -7877,24 +8562,6 @@ chai@^4.0.1, chai@^4.2.0, chai@^4.3.6, chai@^4.3.7: pathval "^1.1.1" type-detect "^4.0.5" -chainsaw@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98" - integrity sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ== - dependencies: - traverse ">=0.3.0 <0.4" - -chalk@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A== - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -8003,15 +8670,6 @@ chevrotain@6.5.0: dependencies: regexp-to-ast "0.4.0" -child-process-promise@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/child-process-promise/-/child-process-promise-2.2.1.tgz#4730a11ef610fad450b8f223c79d31d7bdad8074" - integrity sha512-Fi4aNdqBsr0mv+jgWxcZ/7rAIC2mgihrptyVI4foh/rrjY/3BNjfP9+oaiFx/fzim+1ZyCNBae0DlyfQhSugog== - dependencies: - cross-spawn "^4.0.2" - node-version "^1.0.0" - promise-polyfill "^6.0.1" - chokidar@3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.0.tgz#12c0714668c55800f659e262d4962a97faf554a6" @@ -8072,6 +8730,11 @@ ci-info@^2.0.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== +ci-info@^3.1.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== + ci-info@^3.2.0: version "3.8.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" @@ -8101,12 +8764,10 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== -cjson@^0.3.1: - version "0.3.3" - resolved "https://registry.yarnpkg.com/cjson/-/cjson-0.3.3.tgz#a92d9c786e5bf9b930806329ee05d5d3261b4afa" - integrity sha512-yKNcXi/Mvi5kb1uK0sahubYiyfUO2EUgOp4NcY9+8NX5Xmc+4yeNogZuLFkpLBBj7/QI9MjRUIuXrV9XOw5kVg== - dependencies: - json-parse-helpfulerror "^1.0.3" +cjs-module-lexer@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" + integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== class-is@^1.1.0: version "1.1.0" @@ -8146,23 +8807,6 @@ clean-stack@^3.0.0: dependencies: escape-string-regexp "4.0.0" -cli-boxes@^2.2.0, cli-boxes@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" - integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== - -cli-color@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/cli-color/-/cli-color-1.4.0.tgz#7d10738f48526824f8fe7da51857cb0f572fe01f" - integrity sha512-xu6RvQqqrWEo6MPR1eixqGPywhYBHRs653F9jfXB2Hx4jdM/3WxiNE1vppRmxtMIfl16SFYTpYlrnqH/HsK/2w== - dependencies: - ansi-regex "^2.1.1" - d "1" - es5-ext "^0.10.46" - es6-iterator "^2.0.3" - memoizee "^0.4.14" - timers-ext "^0.1.5" - cli-cursor@3.1.0, cli-cursor@^3.0.0, cli-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" @@ -8339,6 +8983,15 @@ cliui@^5.0.0: strip-ansi "^5.2.0" wrap-ansi "^5.1.0" +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" @@ -8383,7 +9036,7 @@ clone@^1.0.2: resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== -clone@^2.0.0, clone@^2.1.1, clone@^2.1.2: +clone@^2.0.0, clone@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w== @@ -8442,7 +9095,7 @@ collection-visit@^1.0.0: map-visit "^1.0.0" object-visit "^1.0.0" -color-convert@^1.9.0, color-convert@^1.9.3: +color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== @@ -8461,37 +9114,16 @@ color-name@1.1.3: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== -color-name@^1.0.0, color-name@~1.1.4: +color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-string@^1.6.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" - integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== - dependencies: - color-name "^1.0.0" - simple-swizzle "^0.2.2" - -color-support@^1.1.2, color-support@^1.1.3: +color-support@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== -color@^3.1.3: - version "3.2.1" - resolved "https://registry.yarnpkg.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164" - integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA== - dependencies: - color-convert "^1.9.3" - color-string "^1.6.0" - -colorette@2.0.19: - version "2.0.19" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" - integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== - colors@1.0.3, colors@1.0.x: version "1.0.3" resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" @@ -8502,14 +9134,6 @@ colors@1.4.0, colors@^1.0.3, colors@^1.1.2: resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== -colorspace@1.1.x: - version "1.1.4" - resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.4.tgz#8d442d1186152f60453bf8070cd66eb364e59243" - integrity sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w== - dependencies: - color "^3.1.3" - text-hex "1.0.x" - colour@~0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/colour/-/colour-0.7.1.tgz#9cb169917ec5d12c0736d3e8685746df1cadf778" @@ -8564,21 +9188,11 @@ commander@^2.12.1, commander@^2.20.3, commander@^2.8.1: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -commander@^4.0.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" - integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== - commander@^8.1.0: version "8.3.0" resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== -commander@^9.1.0: - version "9.5.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" - integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== - common-ancestor-path@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz#4f7d2d1394d91b7abdf51871c62f71eadb0182a7" @@ -8597,13 +9211,6 @@ compare-func@^2.0.0: array-ify "^1.0.0" dot-prop "^5.1.0" -compare-semver@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/compare-semver/-/compare-semver-1.1.0.tgz#7c0a79a27bb80b6c6994445f82958259d3d02153" - integrity sha512-AENcdfhxsMCzzl+QRdOwMQeA8tZBEEacAmA4pGPoyco27G9sIaM98WNYkcToC9O0wIx1vE+1ErmaM4t0/fXhMw== - dependencies: - semver "^5.0.1" - compare-versions@^3.5.1: version "3.6.0" resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62" @@ -8614,41 +9221,18 @@ complex.js@2.0.11: resolved "https://registry.yarnpkg.com/complex.js/-/complex.js-2.0.11.tgz#09a873fbf15ffd8c18c9c2201ccef425c32b8bf1" integrity sha512-6IArJLApNtdg1P1dFtn3dnyzoZBEF0MwMnrfF1exSBRpZYoy4yieMkpZhQDC0uwctw48vii0CFVyHfpgZ/DfGw== -component-emitter@^1.2.0, component-emitter@^1.2.1, component-emitter@^1.3.0: +component-emitter@^1.2.0, component-emitter@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== -compress-commons@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-4.1.1.tgz#df2a09a7ed17447642bad10a85cc9a19e5c42a7d" - integrity sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ== - dependencies: - buffer-crc32 "^0.2.13" - crc32-stream "^4.0.2" - normalize-path "^3.0.0" - readable-stream "^3.6.0" - -compressible@^2.0.12, compressible@~2.0.16: +compressible@^2.0.12: version "2.0.18" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== dependencies: mime-db ">= 1.43.0 < 2" -compression@^1.7.0: - version "1.7.4" - resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" - integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== - dependencies: - accepts "~1.3.5" - bytes "3.0.0" - compressible "~2.0.16" - debug "2.6.9" - on-headers "~1.0.2" - safe-buffer "5.1.2" - vary "~1.1.2" - concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -8710,28 +9294,6 @@ configstore@^4.0.0: write-file-atomic "^2.0.0" xdg-basedir "^3.0.0" -configstore@^5.0.0, configstore@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" - integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA== - dependencies: - dot-prop "^5.2.0" - graceful-fs "^4.1.2" - make-dir "^3.0.0" - unique-string "^2.0.0" - write-file-atomic "^3.0.0" - xdg-basedir "^4.0.0" - -connect@^3.6.2: - version "3.7.0" - resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" - integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== - dependencies: - debug "2.6.9" - finalhandler "1.1.2" - parseurl "~1.3.3" - utils-merge "1.0.1" - console-control-strings@^1.0.0, console-control-strings@^1.1.0, console-control-strings@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" @@ -8868,7 +9430,7 @@ cookie@0.5.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== -cookiejar@^2.1.1, cookiejar@^2.1.4: +cookiejar@^2.1.1: version "2.1.4" resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.4.tgz#ee669c1fea2cf42dc31585469d193fef0d65771b" integrity sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw== @@ -8952,14 +9514,6 @@ crc-32@^1.2.0: resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== -crc32-stream@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-4.0.2.tgz#c922ad22b38395abe9d3870f02fa8134ed709007" - integrity sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w== - dependencies: - crc-32 "^1.2.0" - readable-stream "^3.4.0" - create-ecdh@^4.0.0: version "4.0.4" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" @@ -8996,21 +9550,13 @@ create-require@^1.1.0: resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== -cross-env@^5.1.3, cross-env@^5.1.6: +cross-env@^5.1.6: version "5.2.1" resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-5.2.1.tgz#b2c76c1ca7add66dc874d11798466094f551b34d" integrity sha512-1yHhtcfAd1r4nwQgknowuUNfIT9E8dOMMspC36g45dN+iD1blloi7xp8X/xAIDnjHWyt1uQ8PHk2fkNaym7soQ== dependencies: cross-spawn "^6.0.5" -cross-fetch@3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.0.4.tgz#7bef7020207e684a7638ef5f2f698e24d9eb283c" - integrity sha512-MSHgpjQqgbT/94D4CyADeNoYh52zMkCX4pcJvPP5WqPsLFMKjr2TCMg381ox5qI0ii2dPwaLx/00477knXqXVw== - dependencies: - node-fetch "2.6.0" - whatwg-fetch "3.0.0" - cross-fetch@3.0.6: version "3.0.6" resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.0.6.tgz#3a4040bc8941e653e0e9cf17f29ebcd177d3365c" @@ -9018,19 +9564,20 @@ cross-fetch@3.0.6: dependencies: node-fetch "2.6.1" -cross-fetch@^3.0.6, cross-fetch@^3.1.4: +cross-fetch@^3.1.4: version "3.1.5" resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== dependencies: node-fetch "2.6.7" -cross-spawn@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41" - integrity sha512-yAXz/pA1tD8Gtg2S98Ekf/sewp3Lcp3YoFKJ4Hkp5h5yLWnKVTDU0kwjKJ8NDCYcfTLfyGkzTikst+jWypT1iA== +cross-spawn@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + integrity sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A== dependencies: lru-cache "^4.0.1" + shebang-command "^1.2.0" which "^1.2.9" cross-spawn@^6.0.0, cross-spawn@^6.0.5: @@ -9044,7 +9591,7 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.3: +cross-spawn@^7.0.0, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -9088,7 +9635,7 @@ crypto-browserify@3.12.0: randombytes "^2.0.0" randomfill "^1.0.3" -crypto-js@^3.1.4, crypto-js@^3.1.9-1: +crypto-js@^3.1.4: version "3.3.0" resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.3.0.tgz#846dd1cce2f68aacfa156c8578f926a609b7976b" integrity sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q== @@ -9098,11 +9645,6 @@ crypto-random-string@^1.0.0: resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" integrity sha512-GsVpkFPlycH7/fRR7Dhcmnoii54gV1nz7y4CWyeFS14N+JVBBhY+r8amRHE4BwSYal7BPTDp8isvAlCxyFt3Hg== -crypto-random-string@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" - integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== - css-select@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6" @@ -9129,6 +9671,16 @@ csstype@^3.0.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== +csv-generate@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/csv-generate/-/csv-generate-3.4.3.tgz#bc42d943b45aea52afa896874291da4b9108ffff" + integrity sha512-w/T+rqR0vwvHqWs/1ZyMDWtHHSJaN06klRqJXBEpDJaM/+dZkso0OKh1VcuuYvK3XM53KysVNq8Ko/epCK8wOw== + +csv-parse@^4.16.3: + version "4.16.3" + resolved "https://registry.yarnpkg.com/csv-parse/-/csv-parse-4.16.3.tgz#7ca624d517212ebc520a36873c3478fa66efbaf7" + integrity sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg== + csv-parser@^2.0.0: version "2.3.5" resolved "https://registry.yarnpkg.com/csv-parser/-/csv-parser-2.3.5.tgz#6b3bf0907684914ff2c5abfbadab111a69eae5db" @@ -9137,13 +9689,6 @@ csv-parser@^2.0.0: minimist "^1.2.0" through2 "^3.0.1" -csv-streamify@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/csv-streamify/-/csv-streamify-3.0.4.tgz#4cb614c57e3f299cca17b63fdcb4ad167777f47a" - integrity sha512-IQkxN0zu0gym8/5CHrSyReeRewbw+aRDrMrGI5WmIY/LmEcNpAcPOyETBHREKgsWHeEQWEihiBmx5EcKAsKWZw== - dependencies: - through2 "2.0.1" - csv-stringify@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/csv-stringify/-/csv-stringify-4.3.1.tgz#7bee36f746ef555dd481a735a9e2938965f8478b" @@ -9151,6 +9696,21 @@ csv-stringify@^4.3.1: dependencies: lodash.get "~4.4.2" +csv-stringify@^5.6.5: + version "5.6.5" + resolved "https://registry.yarnpkg.com/csv-stringify/-/csv-stringify-5.6.5.tgz#c6d74badda4b49a79bf4e72f91cce1e33b94de00" + integrity sha512-PjiQ659aQ+fUTQqSrd1XEDnOr52jh30RBurfzkscaE2tPaFsDH5wOAHJiw8XAHphRknCwMUE9KRayc4K/NbO8A== + +csv@^5.5.3: + version "5.5.3" + resolved "https://registry.yarnpkg.com/csv/-/csv-5.5.3.tgz#cd26c1e45eae00ce6a9b7b27dcb94955ec95207d" + integrity sha512-QTaY0XjjhTQOdguARF0lGKm5/mEq9PD9/VhZZegHDIBq2tQwgNpHc3dneD4mGo2iJs+fTKv5Bp0fZ+BRuY3Z0g== + dependencies: + csv-generate "^3.4.3" + csv-parse "^4.16.3" + csv-stringify "^5.6.5" + stream-transform "^2.1.3" + currency-symbol-map@~2: version "2.2.0" resolved "https://registry.yarnpkg.com/currency-symbol-map/-/currency-symbol-map-2.2.0.tgz#2b3c1872ff1ac2ce595d8273e58e1fff0272aea2" @@ -9181,11 +9741,6 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -data-uri-to-buffer@3: - version "3.0.1" - resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636" - integrity sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og== - dataloader@2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.1.0.tgz#c69c538235e85e7ac6c6c444bae8ecabf5de9df7" @@ -9196,11 +9751,6 @@ date-and-time@^0.6.3: resolved "https://registry.yarnpkg.com/date-and-time/-/date-and-time-0.6.3.tgz#2daee52df67c28bd93bce862756ac86b68cf4237" integrity sha512-lcWy3AXDRJOD7MplwZMmNSRM//kZtJaLz4n6D1P5z9wEmZGBKhJRBIr1Xs9KNQJmdXPblvgffynYji4iylUTcA== -date-utils@*: - version "1.2.21" - resolved "https://registry.yarnpkg.com/date-utils/-/date-utils-1.2.21.tgz#61fb16cdc1274b3c9acaaffe9fc69df8720a2b64" - integrity sha512-wJMBjqlwXR0Iv0wUo/lFbhSQ7MmG1hl36iuxuE91kW+5b5sWbase73manEqNH9sOLFAMG83B4ffNKq9/Iq0FVA== - dateformat@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" @@ -9235,20 +9785,13 @@ debug@3.2.6: ms "^2.1.1" debug@4, debug@4.3.4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.3, debug@^4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== - dependencies: - ms "2.1.2" - -debug@4.3.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" - integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" -debug@^3.0.1, debug@^3.1.0, debug@^3.2.6, debug@^3.2.7: +debug@^3.0.1, debug@^3.1.0, debug@^3.2.6: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== @@ -9396,12 +9939,7 @@ deep-extend@^0.6.0: resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== -deep-freeze@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/deep-freeze/-/deep-freeze-0.0.1.tgz#3a0b0005de18672819dfd38cd31f91179c893e84" - integrity sha512-Z+z8HiAvsGwmjqlphnHW5oz6yWlOwu6EQfFTjmeTWlDeda3FS2yv3jhq35TX/ewmsnqB+RX2IdsIOyjJCQN5tg== - -deep-is@^0.1.3, deep-is@~0.1.3: +deep-is@~0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== @@ -9451,6 +9989,15 @@ deferred-leveldown@~5.3.0: abstract-leveldown "~6.2.1" inherits "^2.0.3" +define-data-property@^1.0.1, define-data-property@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" + integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== + dependencies: + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + define-lazy-prop@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" @@ -9491,16 +10038,6 @@ defined@~1.0.1: resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.1.tgz#c0b9db27bfaffd95d6f61399419b893df0f91ebf" integrity sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q== -degenerator@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-3.0.3.tgz#a081ac30052ca84e1d1c6e86c972ae8dabbc4079" - integrity sha512-FTq/qYMeBJACu1gHcXJvzsRBTK6aw5zWCYbEnIOyamOt5UJufWJRQ5XfDb6OuayfJWvmWAHgcZyt43vm/hbj7g== - dependencies: - ast-types "^0.13.2" - escodegen "^1.8.1" - esprima "^4.0.0" - vm2 "^3.9.11" - delay@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d" @@ -9521,12 +10058,7 @@ delimit-stream@0.1.0: resolved "https://registry.yarnpkg.com/delimit-stream/-/delimit-stream-0.1.0.tgz#9b8319477c0e5f8aeb3ce357ae305fc25ea1cd2b" integrity sha512-a02fiQ7poS5CnjiJBAsjGLPp5EwVoGHNeu9sziBd9huppRfsAFIpv5zNLv0V1gbop53ilngAf5Kf331AwcoRBQ== -denque@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1" - integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw== - -depd@2.0.0, depd@^2.0.0, depd@~2.0.0: +depd@2.0.0, depd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== @@ -9549,7 +10081,7 @@ des.js@^1.0.0: inherits "^2.0.1" minimalistic-assert "^1.0.0" -destroy@1.2.0, destroy@^1.0.4: +destroy@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== @@ -9598,36 +10130,7 @@ detect-node@2.0.3: resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.3.tgz#a2033c09cc8e158d37748fbde7507832bd6ce127" integrity sha512-64uDTOK+fKEa6XoSbkkDoeAX8Ep1XhwxwZtL1aw1En5p5UOK/ekJoFqd5BB1o+uOvF1iHVv6qDUxdOQ/VgWEQg== -detox@^17.13.2: - version "17.14.9" - resolved "https://registry.yarnpkg.com/detox/-/detox-17.14.9.tgz#e269de0937e615eac92ac283b2303751bcf2c072" - integrity sha512-WLpl7BJ2xecZJ02hFFWgzo61RnlO/LlsH/U8GWztoXsT0z0t9iKsBci8AhSSErQfH0IHnNtDcZknIxt0gzNhoQ== - dependencies: - bunyan "^1.8.12" - bunyan-debug-stream "^1.1.0" - chalk "^2.4.2" - child-process-promise "^2.2.0" - find-up "^4.1.0" - fs-extra "^4.0.2" - funpermaproxy "^1.0.1" - get-port "^2.1.0" - ini "^1.3.4" - lodash "^4.17.5" - minimist "^1.2.0" - proper-lockfile "^3.0.2" - resolve-from "^5.0.0" - sanitize-filename "^1.6.1" - shell-quote "^1.7.2" - signal-exit "^3.0.3" - tail "^2.0.0" - telnet-client "1.2.8" - tempfile "^2.0.0" - which "^1.3.1" - ws "^3.3.1" - yargs "^16.0.3" - yargs-unparser "^2.0.0" - -dezalgo@^1.0.0, dezalgo@^1.0.4: +dezalgo@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.4.tgz#751235260469084c132157dfa857f386d4c33d81" integrity sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig== @@ -9635,13 +10138,6 @@ dezalgo@^1.0.0, dezalgo@^1.0.4: asap "^2.0.0" wrappy "1" -dicer@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.3.1.tgz#abf28921e3475bc5e801e74e0159fd94f927ba97" - integrity sha512-ObioMtXnmjYs3aRtpIJt9rgQSPCIhKVkFPip+E9GUDyWl8N435znUxK/JfNwGZJ2wnn5JKQ7Ly3vOK5Q5dylGA== - dependencies: - streamsearch "^1.1.0" - diff-sequences@^29.4.3: version "29.4.3" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" @@ -9765,7 +10261,7 @@ dot-prop@^4.1.0: dependencies: is-obj "^1.0.0" -dot-prop@^5.1.0, dot-prop@^5.2.0: +dot-prop@^5.1.0: version "5.3.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== @@ -9794,11 +10290,6 @@ dotenv@8.2.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== -dotenv@^6.1.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064" - integrity sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w== - dotenv@^8.2.0: version "8.6.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" @@ -9837,13 +10328,6 @@ dtrace-provider@~0.8: dependencies: nan "^2.14.0" -duplexer2@~0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" - integrity sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA== - dependencies: - readable-stream "^2.0.2" - duplexer3@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.5.tgz#0b5e4d7bad5de8901ea4440624c8e1d20099217e" @@ -9864,7 +10348,7 @@ duplexify@^3.5.0, duplexify@^3.6.0: readable-stream "^2.0.0" stream-shift "^1.0.0" -duplexify@^4.0.0, duplexify@^4.1.1: +duplexify@^4.0.0: version "4.1.2" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-4.1.2.tgz#18b4f8d28289132fa0b9573c898d9f903f81c7b0" integrity sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw== @@ -9962,11 +10446,6 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -enabled@2.0.x: - version "2.0.0" - resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" - integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== - encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -9993,7 +10472,7 @@ encoding-down@~5.0.0: level-errors "^2.0.0" xtend "^4.0.1" -encoding@^0.1.11, encoding@^0.1.12, encoding@^0.1.13: +encoding@^0.1.11, encoding@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== @@ -10019,6 +10498,14 @@ ends-with@^0.2.0: resolved "https://registry.yarnpkg.com/ends-with/-/ends-with-0.2.0.tgz#2f9da98d57a50cfda4571ce4339000500f4e6b8a" integrity sha512-lRppY4dK3VkqBdR242sKcAJeYc8Gf/DhoX9AWvWI2RzccmLnqBQfwm2k4oSDv5MPDjUqawCauXhZkyWxkVhRsg== +enquirer@^2.3.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.4.1.tgz#93334b3fbd74fc7097b224ab4a8fb7e40bf4ae56" + integrity sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ== + dependencies: + ansi-colors "^4.1.1" + strip-ansi "^6.0.1" + enquirer@~2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" @@ -10065,6 +10552,21 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" +error@7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/error/-/error-7.0.2.tgz#a5f75fff4d9926126ddac0ea5dc38e689153cb02" + integrity sha512-UtVv4l5MhijsYUxPJo4390gzfZvAnTHreNnDjnTZaKIiZ/SemXxAhBkYSKtWa5RtBXbLP8tMgn/n0RUa/H7jXw== + dependencies: + string-template "~0.2.1" + xtend "~4.0.0" + +error@^7.0.0: + version "7.2.1" + resolved "https://registry.yarnpkg.com/error/-/error-7.2.1.tgz#eab21a4689b5f684fc83da84a0e390de82d94894" + integrity sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA== + dependencies: + string-template "~0.2.1" + es-abstract@^1.17.0-next.1, es-abstract@^1.19.0, es-abstract@^1.20.4, es-abstract@^1.21.2: version "1.21.2" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.2.tgz#a56b9695322c8a185dc25975aa3b8ec31d0e7eff" @@ -10105,6 +10607,51 @@ es-abstract@^1.17.0-next.1, es-abstract@^1.19.0, es-abstract@^1.20.4, es-abstrac unbox-primitive "^1.0.2" which-typed-array "^1.1.9" +es-abstract@^1.22.1: + version "1.22.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.3.tgz#48e79f5573198de6dee3589195727f4f74bc4f32" + integrity sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA== + dependencies: + array-buffer-byte-length "^1.0.0" + arraybuffer.prototype.slice "^1.0.2" + available-typed-arrays "^1.0.5" + call-bind "^1.0.5" + es-set-tostringtag "^2.0.1" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.2" + get-symbol-description "^1.0.0" + globalthis "^1.0.3" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + internal-slot "^1.0.5" + is-array-buffer "^3.0.2" + is-callable "^1.2.7" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-typed-array "^1.1.12" + is-weakref "^1.0.2" + object-inspect "^1.13.1" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.5.1" + safe-array-concat "^1.0.1" + safe-regex-test "^1.0.0" + string.prototype.trim "^1.2.8" + string.prototype.trimend "^1.0.7" + string.prototype.trimstart "^1.0.7" + typed-array-buffer "^1.0.0" + typed-array-byte-length "^1.0.0" + typed-array-byte-offset "^1.0.0" + typed-array-length "^1.0.4" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.13" + es-array-method-boxes-properly@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" @@ -10134,6 +10681,13 @@ es-set-tostringtag@^2.0.1: has "^1.0.3" has-tostringtag "^1.0.0" +es-shim-unscopables@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763" + integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== + dependencies: + hasown "^2.0.0" + es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -10143,7 +10697,7 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@^0.10.53, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46: +es5-ext@^0.10.35, es5-ext@^0.10.50: version "0.10.62" resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5" integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA== @@ -10181,26 +10735,11 @@ es6-symbol@^3.1.1, es6-symbol@^3.1.3: d "^1.0.1" ext "^1.1.2" -es6-weak-map@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53" - integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== - dependencies: - d "1" - es5-ext "^0.10.46" - es6-iterator "^2.0.3" - es6-symbol "^3.1.1" - escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== -escape-goat@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" - integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== - escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -10211,7 +10750,7 @@ escape-latex@1.2.0: resolved "https://registry.yarnpkg.com/escape-latex/-/escape-latex-1.2.0.tgz#07c03818cf7dac250cce517f4fda1b001ef2bca1" integrity sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw== -escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== @@ -10238,18 +10777,6 @@ escodegen@1.8.x: optionalDependencies: source-map "~0.2.0" -escodegen@^1.8.1: - version "1.14.3" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" - integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== - dependencies: - esprima "^4.0.1" - estraverse "^4.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" - eslint-scope@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" @@ -10312,11 +10839,6 @@ eslint@^5.16.0, eslint@^5.5.0, eslint@^5.6.0: table "^5.2.3" text-table "^0.2.0" -esm@^3.2.25: - version "3.2.25" - resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" - integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== - espree@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a" @@ -10338,7 +10860,7 @@ esprima@2.7.x, esprima@^2.7.1: resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" integrity sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A== -esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: +esprima@^4.0.0, esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== @@ -10362,7 +10884,7 @@ estraverse@^1.9.1: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" integrity sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA== -estraverse@^4.1.1, estraverse@^4.2.0: +estraverse@^4.1.1: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== @@ -10594,6 +11116,16 @@ ethereum-cryptography@^0.1.3: secp256k1 "^4.0.1" setimmediate "^1.0.5" +ethereum-cryptography@^2.0.0, ethereum-cryptography@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-2.1.2.tgz#18fa7108622e56481157a5cb7c01c0c6a672eb67" + integrity sha512-Z5Ba0T0ImZ8fqXrJbpHcbpAvIswRte2wGNR/KePnu8GbbvgJ47lMxT/ZZPG6i9Jaht4azPDop4HaM00J0J59ug== + dependencies: + "@noble/curves" "1.1.0" + "@noble/hashes" "1.3.1" + "@scure/bip32" "1.3.1" + "@scure/bip39" "1.2.1" + ethereum-types@^3.7.1: version "3.7.1" resolved "https://registry.yarnpkg.com/ethereum-types/-/ethereum-types-3.7.1.tgz#8fa75e5d9f5da3c85535ea0d4bcd2614b1d650a8" @@ -10705,7 +11237,7 @@ ethereumjs-util@7.1.5, ethereumjs-util@^7.0.10, ethereumjs-util@^7.0.2, ethereum ethereum-cryptography "^0.1.3" rlp "^2.2.4" -ethereumjs-util@^5.0.0, ethereumjs-util@^5.0.1, ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.2, ethereumjs-util@^5.1.5, ethereumjs-util@^5.2.0: +ethereumjs-util@^5.0.0, ethereumjs-util@^5.0.1, ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.2, ethereumjs-util@^5.1.5: version "5.2.1" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz#a833f0e5fca7e5b361384dc76301a721f537bf65" integrity sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ== @@ -10881,14 +11413,6 @@ ethjs-util@0.1.6, ethjs-util@^0.1.3: is-hex-prefixed "1.0.0" strip-hex-prefix "1.0.0" -event-emitter@^0.3.5: - version "0.3.5" - resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" - integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA== - dependencies: - d "1" - es5-ext "~0.10.14" - event-target-shim@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" @@ -10914,11 +11438,6 @@ eventemitter3@^4.0.4: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== -events-listener@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/events-listener/-/events-listener-1.1.0.tgz#dd49b4628480eba58fde31b870ee346b3990b349" - integrity sha512-Kd3EgYfODHueq6GzVfs/VUolh2EgJsS8hkO3KpnDrxVjU3eq63eXM2ujXkhPP+OkeUOhL8CxdfZbQXzryb5C4g== - events@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" @@ -11000,40 +11519,6 @@ execa@^5.0.0: signal-exit "^3.0.3" strip-final-newline "^2.0.0" -exegesis-express@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/exegesis-express/-/exegesis-express-2.0.1.tgz#f162cdd68ee93dc14d832b02b1dbeab053697ee9" - integrity sha512-8ORl1YRygYGPdR+zcClMqzaU+JQuvdNIw/s0RNwYluxNecEHkDEcXFmO6A5T79p7e48KI8iXJYt6KIn4Z9z4bg== - dependencies: - exegesis "^2.5.7" - -exegesis@^2.5.7: - version "2.5.7" - resolved "https://registry.yarnpkg.com/exegesis/-/exegesis-2.5.7.tgz#232c4b01361bc2bf0d9d4c07549c479e77f2b7a3" - integrity sha512-Y0gEY3hgoLa80aMUm8rhhlIW3/KWo4uqN5hKJqok2GLh3maZjRLRC+p0gj33Jw3upAOKOXeRgScT5rtRoMyxwQ== - dependencies: - "@apidevtools/json-schema-ref-parser" "^9.0.3" - ajv "^6.12.2" - body-parser "^1.18.3" - content-type "^1.0.4" - deep-freeze "0.0.1" - events-listener "^1.1.0" - glob "^7.1.3" - json-ptr "^2.2.0" - json-schema-traverse "^1.0.0" - lodash "^4.17.11" - openapi3-ts "^2.0.1" - promise-breaker "^5.0.0" - pump "^3.0.0" - qs "^6.6.0" - raw-body "^2.3.3" - semver "^7.0.0" - -exit-code@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/exit-code/-/exit-code-1.0.2.tgz#ce165811c9f117af6a5f882940b96ae7f9aecc34" - integrity sha512-U80QYrKun5np62yRqG6geNRP5TZKU2HF73Bb6IE3XjDHXKlserAdP14tIaP3W9J6ezv84DwbpbRTAtu4FsKcgw== - exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -11103,7 +11588,7 @@ export-files@^2.0.1: dependencies: lazy-cache "^1.0.3" -express@^4.14.0, express@^4.16.4, express@^4.17.1: +express@^4.14.0, express@^4.17.1, express@^4.17.6: version "4.18.2" resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== @@ -11167,7 +11652,12 @@ extend@^3.0.0, extend@^3.0.1, extend@^3.0.2, extend@~3.0.2: resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== -external-editor@^3.0.3: +extendable-error@^0.1.5: + version "0.1.7" + resolved "https://registry.yarnpkg.com/extendable-error/-/extendable-error-0.1.7.tgz#60b9adf206264ac920058a7395685ae4670c2b96" + integrity sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg== + +external-editor@^3.0.3, external-editor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== @@ -11305,7 +11795,7 @@ fast-levenshtein@~2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== -fast-safe-stringify@^2.0.6, fast-safe-stringify@^2.1.1: +fast-safe-stringify@^2.0.6: version "2.1.1" resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== @@ -11334,13 +11824,6 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" -faye-websocket@0.11.4: - version "0.11.4" - resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" - integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== - dependencies: - websocket-driver ">=0.5.1" - fb-watchman@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" @@ -11355,11 +11838,6 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" -fecha@^4.2.0: - version "4.2.3" - resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd" - integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw== - fetch-cookie@0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/fetch-cookie/-/fetch-cookie-0.11.0.tgz#e046d2abadd0ded5804ce7e2cae06d4331c15407" @@ -11439,11 +11917,6 @@ file-uri-to-path@1.0.0: resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== -file-uri-to-path@2: - version "2.0.0" - resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-2.0.0.tgz#7b415aeba227d575851e0a5b0c640d7656403fba" - integrity sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg== - filelist@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" @@ -11456,11 +11929,6 @@ filename-regex@^2.0.0: resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" integrity sha512-BTCqyBaWBTsauvnHiE8i562+EdJj+oUpkqWp2R1iCoR8f6oo8STRu3of7WJJ0TqWtxN50a5YFpzYK4Jj9esYfQ== -filesize@^6.1.0: - version "6.4.0" - resolved "https://registry.yarnpkg.com/filesize/-/filesize-6.4.0.tgz#914f50471dd66fdca3cefe628bd0cde4ef769bcd" - integrity sha512-mjFIpOHC4jbfcTfoh4rkWpI31mF7viw9ikj/JyLoKzqlwG/YsefKfvYlYhdYdg/9mtK2z1AzgN/0LvVQ3zdlSQ== - fill-keys@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/fill-keys/-/fill-keys-1.0.2.tgz#9a8fa36f4e8ad634e3bf6b4f3c8882551452eb20" @@ -11497,19 +11965,6 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -finalhandler@1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" - integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== - dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.3" - statuses "~1.5.0" - unpipe "~1.0.0" - finalhandler@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" @@ -11555,7 +12010,7 @@ find-up@4.1.0, find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" -find-up@5.0.0: +find-up@5.0.0, find-up@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== @@ -11578,6 +12033,14 @@ find-up@^2.0.0, find-up@^2.1.0: dependencies: locate-path "^2.0.0" +find-yarn-workspace-root2@1.2.16: + version "1.2.16" + resolved "https://registry.yarnpkg.com/find-yarn-workspace-root2/-/find-yarn-workspace-root2-1.2.16.tgz#60287009dd2f324f59646bdb4b7610a6b301c2a9" + integrity sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA== + dependencies: + micromatch "^4.0.2" + pkg-dir "^4.2.0" + find-yarn-workspace-root@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz#f47fb8d239c900eb78179aa81b66673eac88f7bd" @@ -11595,116 +12058,6 @@ findup-sync@^1.0.0: micromatch "^2.3.7" resolve-dir "^0.1.0" -firebase-admin@^9.12.0: - version "9.12.0" - resolved "https://registry.yarnpkg.com/firebase-admin/-/firebase-admin-9.12.0.tgz#d7e889e97c9c31610efbcd131bb6d06a783af757" - integrity sha512-AtA7OH5RbIFGoc0gZOQgaYC6cdjdhZv4w3XgWoupkPKO1HY+0GzixOuXDa75kFeoVyhIyo4PkLg/GAC1dC1P6w== - dependencies: - "@firebase/database-compat" "^0.1.1" - "@firebase/database-types" "^0.7.2" - "@types/node" ">=12.12.47" - dicer "^0.3.0" - jsonwebtoken "^8.5.1" - jwks-rsa "^2.0.2" - node-forge "^0.10.0" - optionalDependencies: - "@google-cloud/firestore" "^4.5.0" - "@google-cloud/storage" "^5.3.0" - -firebase-functions-test@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/firebase-functions-test/-/firebase-functions-test-0.3.3.tgz#d26626d6c3edb03cd27eb79c12980c2551722517" - integrity sha512-dCppF/2Ztv87IyyBaUQlT1Z05ial5v/3LB0huS2ktXz05yNiID5FVIKtO0/+w9Q7/SThJ8qIDG0hoGDPt4Xcug== - dependencies: - "@types/lodash" "^4.14.104" - lodash "^4.17.5" - -firebase-functions@^3.15.7: - version "3.24.1" - resolved "https://registry.yarnpkg.com/firebase-functions/-/firebase-functions-3.24.1.tgz#50d13274c4ae96b2308a67e9fc76f1a74cff690d" - integrity sha512-GYhoyOV0864HFMU1h/JNBXYNmDk2MlbvU7VO/5qliHX6u/6vhSjTJjlyCG4leDEI8ew8IvmkIC5QquQ1U8hAuA== - dependencies: - "@types/cors" "^2.8.5" - "@types/express" "4.17.3" - cors "^2.8.5" - express "^4.17.1" - lodash "^4.17.14" - node-fetch "^2.6.7" - -firebase-tools@9.20.0: - version "9.20.0" - resolved "https://registry.yarnpkg.com/firebase-tools/-/firebase-tools-9.20.0.tgz#96a551147ccc8720e976357c85fb037e371a030d" - integrity sha512-/5LzkZtW8aC57syHf34FXY1w6g9unb7qdvtlYROdJA33sk2xsWsJmuvtJylhYhTNX8zrwFsmiTHRlaBxA9YWtg== - dependencies: - "@google-cloud/pubsub" "^2.7.0" - "@types/archiver" "^5.1.0" - JSONStream "^1.2.1" - abort-controller "^3.0.0" - ajv "^6.12.6" - archiver "^5.0.0" - body-parser "^1.19.0" - chokidar "^3.0.2" - cjson "^0.3.1" - cli-color "^1.2.0" - cli-table "^0.3.1" - commander "^4.0.1" - configstore "^5.0.1" - cors "^2.8.5" - cross-env "^5.1.3" - cross-spawn "^7.0.1" - csv-streamify "^3.0.4" - dotenv "^6.1.0" - exegesis "^2.5.7" - exegesis-express "^2.0.0" - exit-code "^1.0.2" - express "^4.16.4" - filesize "^6.1.0" - fs-extra "^5.0.0" - glob "^7.1.2" - google-auth-library "^6.1.3" - inquirer "~6.3.1" - js-yaml "^3.13.1" - jsonwebtoken "^8.5.1" - leven "^3.1.0" - lodash "^4.17.21" - marked "^0.7.0" - marked-terminal "^3.3.0" - mime "^2.5.2" - minimatch "^3.0.4" - morgan "^1.10.0" - node-fetch "^2.6.1" - open "^6.3.0" - ora "^3.4.0" - portfinder "^1.0.23" - progress "^2.0.3" - proxy-agent "^5.0.0" - request "^2.87.0" - rimraf "^3.0.0" - semver "^5.7.1" - superstatic "^7.1.0" - tar "^4.3.0" - tcp-port-used "^1.0.1" - tmp "0.0.33" - triple-beam "^1.3.0" - tweetsodium "0.0.5" - universal-analytics "^0.4.16" - unzipper "^0.10.10" - update-notifier "^5.1.0" - uuid "^3.0.0" - winston "^3.0.0" - winston-transport "^4.4.0" - ws "^7.2.3" - -flat-arguments@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/flat-arguments/-/flat-arguments-1.0.2.tgz#9baa780adf0501f282d726c9c6a038dba44ea76f" - integrity sha512-ZIkB09bqQdKP9buPOiZcS/4HK3q992C5q62qAE72d0xWAXfaSbP840BZYUBgHRkzdx6jYRIpKT4ur+Nay/JRlg== - dependencies: - array-flatten "^1.0.0" - as-array "^1.0.0" - lodash.isarguments "^3.0.0" - lodash.isobject "^3.0.0" - flat-cache@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" @@ -11736,11 +12089,6 @@ flow-stoplight@^1.0.0: resolved "https://registry.yarnpkg.com/flow-stoplight/-/flow-stoplight-1.0.0.tgz#4a292c5bcff8b39fa6cc0cb1a853d86f27eeff7b" integrity sha512-rDjbZUKpN8OYhB0IE/vY/I8UWO/602IIJEU/76Tv4LvYnwHCk0BCsvz4eRr9n+FQcri7L5cyaXOo0+/Kh4HisA== -fn.name@1.x.x: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" - integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== - follow-redirects@1.5.10: version "1.5.10" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" @@ -11828,16 +12176,6 @@ form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" -formidable@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/formidable/-/formidable-2.1.2.tgz#fa973a2bec150e4ce7cac15589d7a25fc30ebd89" - integrity sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g== - dependencies: - dezalgo "^1.0.4" - hexoid "^1.0.0" - once "^1.4.0" - qs "^6.11.0" - forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -11922,7 +12260,7 @@ fs-extra@^6.0.1: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@^7.0.0: +fs-extra@^7.0.0, fs-extra@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== @@ -11984,29 +12322,16 @@ fsevents@~2.1.1, fsevents@~2.1.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== -fstream@^1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" - integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg== - dependencies: - graceful-fs "^4.1.2" - inherits "~2.0.0" - mkdirp ">=0.5 0" - rimraf "2" - -ftp@^0.3.10: - version "0.3.10" - resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d" - integrity sha512-faFVML1aBx2UoDStmLwv2Wptt4vw5x03xxX172nhA5Y5HBshW5JweqQ2W4xL4dezQTG8inJsuYcpPHHU3X5OTQ== - dependencies: - readable-stream "1.1.x" - xregexp "2.0.0" - function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + function.prototype.name@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" @@ -12017,6 +12342,16 @@ function.prototype.name@^1.1.5: es-abstract "^1.19.0" functions-have-names "^1.2.2" +function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" + functional-red-black-tree@^1.0.1, functional-red-black-tree@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" @@ -12027,12 +12362,7 @@ functions-have-names@^1.2.2, functions-have-names@^1.2.3: resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== -funpermaproxy@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/funpermaproxy/-/funpermaproxy-1.1.0.tgz#39cb0b8bea908051e4608d8a414f1d87b55bf557" - integrity sha512-2Sp1hWuO8m5fqeFDusyhKqYPT+7rGLw34N3qonDcdRP8+n7M7Gl/yKp/q7oCxnnJ6pWCectOmLFJpsMU/++KrQ== - -ganache@7.8.0, ganache@^7.4.0, "ganache@npm:@soloseng/ganache@7.8.0-beta.1": +ganache@7.8.0, ganache@^7.4.0, "ganache@npm:@celo/ganache@7.8.0-unofficial.0", "ganache@npm:@soloseng/ganache@7.8.0-beta.1": version "7.8.0-beta.1" resolved "https://registry.yarnpkg.com/@soloseng/ganache/-/ganache-7.8.0-beta.1.tgz#168d3c2f1ea05c350e972f227872d4a4b15e5f9e" integrity sha512-ptjs631aKpxG7xexDDyxh5NevKQaUcSfm3/Aeh0smJ/3xQgNrJ+st6CVYVbVbhGkgNcq9NIvCCaIgt0L64GOCA== @@ -12053,21 +12383,6 @@ ganache@7.8.0, ganache@^7.4.0, "ganache@npm:@soloseng/ganache@7.8.0-beta.1": bufferutil "4.0.5" utf-8-validate "5.0.7" -gauge@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" - integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== - dependencies: - aproba "^1.0.3 || ^2.0.0" - color-support "^1.1.2" - console-control-strings "^1.0.0" - has-unicode "^2.0.1" - object-assign "^4.1.1" - signal-exit "^3.0.0" - string-width "^4.2.3" - strip-ansi "^6.0.1" - wide-align "^1.1.2" - gauge@^4.0.3: version "4.0.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce" @@ -12117,6 +12432,16 @@ gaxios@^4.0.0: is-stream "^2.0.0" node-fetch "^2.6.7" +gaxios@^5.0.0: + version "5.1.3" + resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-5.1.3.tgz#f7fa92da0fe197c846441e5ead2573d4979e9013" + integrity sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA== + dependencies: + extend "^3.0.2" + https-proxy-agent "^5.0.0" + is-stream "^2.0.0" + node-fetch "^2.6.9" + gcp-metadata@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-1.0.0.tgz#5212440229fa099fc2f7c2a5cdcb95575e9b2ca6" @@ -12133,6 +12458,14 @@ gcp-metadata@^4.2.0: gaxios "^4.0.0" json-bigint "^1.0.0" +gcp-metadata@^5.0.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-5.3.0.tgz#6f45eb473d0cb47d15001476b48b663744d25408" + integrity sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w== + dependencies: + gaxios "^5.0.0" + json-bigint "^1.0.0" + gcs-resumable-upload@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/gcs-resumable-upload/-/gcs-resumable-upload-1.1.0.tgz#2b06f5876dcf60f18a309343f79ed951aff01399" @@ -12145,13 +12478,6 @@ gcs-resumable-upload@^1.0.0: pumpify "^1.5.1" stream-events "^1.0.4" -generate-function@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f" - integrity sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ== - dependencies: - is-property "^1.0.2" - generate-password@^1.5.1: version "1.7.0" resolved "https://registry.yarnpkg.com/generate-password/-/generate-password-1.7.0.tgz#00ba4eb1e71f89a72307b0d6604ee0d4e7f5770c" @@ -12200,6 +12526,16 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@ has "^1.0.3" has-symbols "^1.0.3" +get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" + integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== + dependencies: + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" @@ -12215,13 +12551,6 @@ get-pkg-repo@^4.0.0: through2 "^2.0.0" yargs "^16.2.0" -get-port@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/get-port/-/get-port-2.1.0.tgz#8783f9dcebd1eea495a334e1a6a251e78887ab1a" - integrity sha512-Za6hwpIQjqx3rxtqHZpVdn4r/74EkANdpp4GKJO2GcjsRrnMD5QfiuDIcEckUrtmCIC13FNZqNkhmucZvNrjhg== - dependencies: - pinkie-promise "^2.0.0" - get-port@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc" @@ -12284,18 +12613,6 @@ get-symbol-description@^1.0.0: call-bind "^1.0.2" get-intrinsic "^1.1.1" -get-uri@3: - version "3.0.2" - resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-3.0.2.tgz#f0ef1356faabc70e1f9404fa3b66b2ba9bfc725c" - integrity sha512-+5s0SJbGoyiJTZZ2JTpFPLMPSch72KEqGOTvQsBqg0RBWvwhWUSYZFAtz3TPW0GXJuLBJPts1E241iHg+VRfhg== - dependencies: - "@tootallnate/once" "1" - data-uri-to-buffer "3" - debug "4" - file-uri-to-path "2" - fs-extra "^8.1.0" - ftp "^0.3.10" - get-value@^1.1.5: version "1.3.1" resolved "https://registry.yarnpkg.com/get-value/-/get-value-1.3.1.tgz#8ac7ef4f20382392b2646548f9b9ad2dc6c89642" @@ -12311,11 +12628,6 @@ get-value@^2.0.3, get-value@^2.0.6: resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" integrity sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA== -getopts@2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/getopts/-/getopts-2.3.0.tgz#71e5593284807e03e2427449d4f6712a268666f4" - integrity sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA== - getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -12465,20 +12777,6 @@ glob-parent@^5.1.1, glob-parent@^5.1.2, glob-parent@~5.1.0, glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" -glob-slash@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/glob-slash/-/glob-slash-1.0.0.tgz#fe52efa433233f74a2fe64c7abb9bc848202ab95" - integrity sha512-ZwFh34WZhZX28ntCMAP1mwyAJkn8+Omagvt/GvA+JQM/qgT0+MR2NPF3vhvgdshfdvDyGZXs8fPXW84K32Wjuw== - -glob-slasher@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/glob-slasher/-/glob-slasher-1.0.1.tgz#747a0e5bb222642ee10d3e05443e109493cb0f8e" - integrity sha512-5MUzqFiycIKLMD1B0dYOE4hGgLLUZUNGGYO4BExdwT32wUwW3DBOE7lMQars7vB1q43Fb3Tyt+HmgLKsJhDYdg== - dependencies: - glob-slash "^1.0.0" - lodash.isobject "^2.4.1" - toxic "^1.0.0" - glob-to-regexp@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" @@ -12594,20 +12892,6 @@ glob@^8.0.1: minimatch "^5.0.1" once "^1.3.0" -global-dirs@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.1.0.tgz#e9046a49c806ff04d6c1825e196c8f0091e8df4d" - integrity sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ== - dependencies: - ini "1.3.7" - -global-dirs@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.1.tgz#0c488971f066baceda21447aecb1a8b911d22485" - integrity sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA== - dependencies: - ini "2.0.0" - global-modules@1.0.0, global-modules@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" @@ -12680,7 +12964,7 @@ globby@^10.0.1: merge2 "^1.2.3" slash "^3.0.0" -globby@^11.0.1, globby@^11.0.2, globby@^11.1.0: +globby@^11.0.0, globby@^11.0.1, globby@^11.0.2, globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -12714,28 +12998,13 @@ google-auth-library@^3.0.0, google-auth-library@^3.1.1: fast-text-encoding "^1.0.0" gaxios "^1.2.1" gcp-metadata "^1.0.0" - gtoken "^2.3.2" - https-proxy-agent "^2.2.1" - jws "^3.1.5" - lru-cache "^5.0.0" - semver "^5.5.0" - -google-auth-library@^6.1.3: - version "6.1.6" - resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-6.1.6.tgz#deacdcdb883d9ed6bac78bb5d79a078877fdf572" - integrity sha512-Q+ZjUEvLQj/lrVHF/IQwRo6p3s8Nc44Zk/DALsN+ac3T4HY/g/3rrufkgtl+nZ1TW7DNAw5cTChdVp4apUXVgQ== - dependencies: - arrify "^2.0.0" - base64-js "^1.3.0" - ecdsa-sig-formatter "^1.0.11" - fast-text-encoding "^1.0.0" - gaxios "^4.0.0" - gcp-metadata "^4.2.0" - gtoken "^5.0.4" - jws "^4.0.0" - lru-cache "^6.0.0" + gtoken "^2.3.2" + https-proxy-agent "^2.2.1" + jws "^3.1.5" + lru-cache "^5.0.0" + semver "^5.5.0" -google-auth-library@^7.0.0, google-auth-library@^7.14.0, google-auth-library@^7.14.1: +google-auth-library@^7.14.0: version "7.14.1" resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-7.14.1.tgz#e3483034162f24cc71b95c8a55a210008826213c" integrity sha512-5Rk7iLNDFhFeBYc3s8l1CqzbEBcdhwR193RlD4vSNFajIcINKI8W8P0JLmBpwymHqqWbX34pJDQu39cSy/6RsA== @@ -12750,25 +13019,6 @@ google-auth-library@^7.0.0, google-auth-library@^7.14.0, google-auth-library@^7. jws "^4.0.0" lru-cache "^6.0.0" -google-gax@2.30.3: - version "2.30.3" - resolved "https://registry.yarnpkg.com/google-gax/-/google-gax-2.30.3.tgz#5d2c227972d99d6a6cd77963c44d0575974e7b60" - integrity sha512-Zsd6hbJBMvAcJS3cYpAsmupvfsxygFR2meUZJcGeR7iUqYHCR/1Hf2aQNB9srrlXQMm91pNiUvW0Kz6Qld8QkA== - dependencies: - "@grpc/grpc-js" "~1.6.0" - "@grpc/proto-loader" "0.6.9" - "@types/long" "^4.0.0" - abort-controller "^3.0.0" - duplexify "^4.0.0" - fast-text-encoding "^1.0.3" - google-auth-library "^7.14.0" - is-stream-ended "^0.1.4" - node-fetch "^2.6.1" - object-hash "^3.0.0" - proto3-json-serializer "^0.1.8" - protobufjs "6.11.2" - retry-request "^4.0.0" - google-gax@^0.25.0: version "0.25.6" resolved "https://registry.yarnpkg.com/google-gax/-/google-gax-0.25.6.tgz#5ea5c743933ba957da63951bc828aef91fb69340" @@ -12808,7 +13058,7 @@ google-gax@^2.0.1, google-gax@^2.24.1: protobufjs "6.11.3" retry-request "^4.0.0" -google-libphonenumber@^3.2.15, google-libphonenumber@^3.2.27: +google-libphonenumber@^3.2.27: version "3.2.32" resolved "https://registry.yarnpkg.com/google-libphonenumber/-/google-libphonenumber-3.2.32.tgz#63c48a9c247b64a3bc2eec21bdf3fcfbf2e148c0" integrity sha512-mcNgakausov/B/eTgVeX8qc8IwWjRrupk9UzZZ/QDEvdh5fAjE7Aa211bkZpZj42zKkeS6MTT8avHUwjcLxuGQ== @@ -12883,7 +13133,7 @@ got@12.1.0: p-cancelable "^3.0.0" responselike "^2.0.0" -got@9.6.0, got@^9.6.0: +got@9.6.0: version "9.6.0" resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== @@ -12937,7 +13187,7 @@ got@^7.1.0: url-parse-lax "^1.0.0" url-to-options "^1.0.1" -graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.6, graceful-fs@^4.2.9: +graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.5, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -13041,13 +13291,6 @@ hard-rejection@^2.1.0: resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg== - dependencies: - ansi-regex "^2.0.0" - has-bigints@^1.0.1, has-bigints@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" @@ -13145,11 +13388,6 @@ has-values@^1.0.0: is-number "^3.0.0" kind-of "^4.0.0" -has-yarn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" - integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== - has@^1.0.3, has@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" @@ -13166,7 +13404,7 @@ hash-base@^3.0.0: readable-stream "^3.6.0" safe-buffer "^5.2.0" -hash-stream-validation@^0.2.1, hash-stream-validation@^0.2.2: +hash-stream-validation@^0.2.1: version "0.2.4" resolved "https://registry.yarnpkg.com/hash-stream-validation/-/hash-stream-validation-0.2.4.tgz#ee68b41bf822f7f44db1142ec28ba9ee7ccb7512" integrity sha512-Gjzu0Xn7IagXVkSu9cSFuK1fqzwtLwFhNhVL8IFJijRNMgUttFbBSIAzKuSIrsFMO1+g1RlsoN49zPIbwPDMGQ== @@ -13187,6 +13425,13 @@ hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: inherits "^2.0.3" minimalistic-assert "^1.0.1" +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== + dependencies: + function-bind "^1.1.2" + hdkey@2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/hdkey/-/hdkey-2.1.0.tgz#755b30b73f54e93c31919c1b2f19205a8e57cb92" @@ -13223,10 +13468,15 @@ header-case@^1.0.0: no-case "^2.2.0" upper-case "^1.1.3" -hexoid@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/hexoid/-/hexoid-1.0.0.tgz#ad10c6573fb907de23d9ec63a711267d9dc9bc18" - integrity sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g== +hexer@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/hexer/-/hexer-1.5.0.tgz#b86ce808598e8a9d1892c571f3cedd86fc9f0653" + integrity sha512-dyrPC8KzBzUJ19QTIo1gXNqIISRXQ0NwteW6OeQHRN4ZuZeHkdODfj0zHBdOlHbRY8GqbqK57C9oWSvQZizFsg== + dependencies: + ansi-color "^0.2.1" + minimist "^1.1.0" + process "^0.10.0" + xtend "^4.0.0" highlight.js@^10.2.0, highlight.js@^10.4.1: version "10.7.3" @@ -13247,11 +13497,6 @@ hmac-drbg@^1.0.1: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -home-dir@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/home-dir/-/home-dir-1.0.0.tgz#2917eb44bdc9072ceda942579543847e3017fe4e" - integrity sha512-PPAP0BMY72XQ0sYwFow8EgHwUYfptkZusnZEGHkBjdKRXIYcVFsbEViqU4k8VrJWf0m7wMr9gscQX9klJYh7zg== - homedir-polyfill@^1.0.0, homedir-polyfill@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" @@ -13354,12 +13599,7 @@ http-https@^1.0.0: resolved "https://registry.yarnpkg.com/http-https/-/http-https-1.0.0.tgz#2f908dd5f1db4068c058cd6e6d4ce392c913389b" integrity sha512-o0PWwVCSp3O0wS6FvNr6xfBCHgt0m1tvPLFOCc2iFDKTRAXhB7m8klDf7ErowFH8POa6dVdGatKU5I1YYwzUyg== -http-parser-js@>=0.5.1: - version "0.5.8" - resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.8.tgz#af23090d9ac4e24573de6f6aecc9d84a48bf20e3" - integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== - -http-proxy-agent@^4.0.0, http-proxy-agent@^4.0.1: +http-proxy-agent@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== @@ -13409,14 +13649,6 @@ http2-wrapper@^2.1.10: quick-lru "^5.1.1" resolve-alpn "^1.2.0" -https-proxy-agent@5, https-proxy-agent@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" - integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== - dependencies: - agent-base "6" - debug "4" - https-proxy-agent@^2.2.1: version "2.2.4" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" @@ -13425,16 +13657,34 @@ https-proxy-agent@^2.2.1: agent-base "^4.3.0" debug "^3.1.0" +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + +human-id@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/human-id/-/human-id-1.0.2.tgz#e654d4b2b0d8b07e45da9f6020d8af17ec0a5df3" + integrity sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw== + human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== -humanize-duration@^3.17.0, humanize-duration@^3.21.0: +humanize-duration@^3.17.0: version "3.28.0" resolved "https://registry.yarnpkg.com/humanize-duration/-/humanize-duration-3.28.0.tgz#f79770c0bec34d3bfd4899338cc40643bc04df72" integrity sha512-jMAxraOOmHuPbffLVDKkEKi/NeG8dMqP8lGRd6Tbf7JgAeG33jjgPWDbXXU7ypCI0o+oNKJFgbSB9FKVdWNI2A== +humanize-duration@^3.29.0: + version "3.30.0" + resolved "https://registry.yarnpkg.com/humanize-duration/-/humanize-duration-3.30.0.tgz#9be623d331116583ff90aeb6ccfdc2e79d6d7372" + integrity sha512-NxpT0fhQTFuMTLnuu1Xp+ozNpYirQnbV3NlOjEKBYlE3uvMRu3LDuq8EPc3gVXxVYnchQfqVM4/+T9iwHPLLeA== + humanize-ms@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" @@ -13471,14 +13721,7 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@^0.5.0: - version "0.5.2" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.5.2.tgz#af6d628dccfb463b7364d97f715e4b74b8c8c2b8" - integrity sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -iconv-lite@^0.6.2, iconv-lite@^0.6.3: +iconv-lite@^0.6.2: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -13557,10 +13800,15 @@ import-fresh@^3.0.0, import-fresh@^3.2.1: parent-module "^1.0.0" resolve-from "^4.0.0" -import-lazy@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" - integrity sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A== +import-in-the-middle@1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.4.2.tgz#2a266676e3495e72c04bbaa5ec14756ba168391b" + integrity sha512-9WOz1Yh/cvO/p69sxRmhyQwrIGGSp7EIdcb+fFNVi7CzQGQB8U1/1XrKVSbEd/GNOAeM0peJtmi7+qphe7NvAw== + dependencies: + acorn "^8.8.2" + acorn-import-assertions "^1.9.0" + cjs-module-lexer "^1.2.2" + module-details-from-path "^1.0.3" import-local@^3.0.2: version "3.1.0" @@ -13598,7 +13846,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -13608,16 +13856,6 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== -ini@1.3.7: - version "1.3.7" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.7.tgz#a09363e1911972ea16d7a8851005d84cf09a9a84" - integrity sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ== - -ini@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" - integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== - ini@^1.3.2, ini@^1.3.4, ini@~1.3.0: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" @@ -13695,30 +13933,6 @@ inquirer@^8.2.4: through "^2.3.6" wrap-ansi "^7.0.0" -inquirer@~6.3.1: - version "6.3.1" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.3.1.tgz#7a413b5e7950811013a3db491c61d1f3b776e8e7" - integrity sha512-MmL624rfkFt4TG9y/Jvmt8vdmOo836U7Y0Hxr2aFk3RelZEGX4Igk0KabWrcaaZaTv9uzglOqWh1Vly+FAWAXA== - dependencies: - ansi-escapes "^3.2.0" - chalk "^2.4.2" - cli-cursor "^2.1.0" - cli-width "^2.0.0" - external-editor "^3.0.3" - figures "^2.0.0" - lodash "^4.17.11" - mute-stream "0.0.7" - run-async "^2.2.0" - rxjs "^6.4.0" - string-width "^2.1.0" - strip-ansi "^5.1.0" - through "^2.3.6" - -install-artifact-from-github@^1.3.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/install-artifact-from-github/-/install-artifact-from-github-1.3.2.tgz#1a16d9508e40330523a3017ae0d4713ccc64de82" - integrity sha512-yCFcLvqk0yQdxx0uJz4t9Z3adDMLAYrcGYv546uRXCSvxE+GqNYhhz/KmrGcUKGI/gVLR9n/e/zM9jX/+ASMJQ== - internal-slot@^1.0.4, internal-slot@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" @@ -13733,11 +13947,6 @@ interpret@^1.0.0: resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== -interpret@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" - integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== - invariant@2: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" @@ -13760,21 +13969,6 @@ io-ts@2.0.1: resolved "https://registry.yarnpkg.com/io-ts/-/io-ts-2.0.1.tgz#1261c12f915c2f48d16393a36966636b48a45aa1" integrity sha512-RezD+WcCfW4VkMkEcQWL/Nmy/nqsWTvTYg7oUmTGzglvSSV2P9h2z1PVeREPFf0GWNzruYleAt1XCMQZSg1xxQ== -ip-regex@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" - integrity sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw== - -ip-regex@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5" - integrity sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q== - -ip@^1.1.5: - version "1.1.8" - resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.8.tgz#ae05948f6b075435ed3307acce04629da8cdbf48" - integrity sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg== - ip@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" @@ -13829,11 +14023,6 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== -is-arrayish@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" - integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== - is-base64@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-base64/-/is-base64-1.1.0.tgz#8ce1d719895030a457c59a7dcaf39b66d99d56b4" @@ -13883,6 +14072,13 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" +is-ci@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.1.tgz#db6ecbed1bd659c43dac0f45661e7674103d1867" + integrity sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ== + dependencies: + ci-info "^3.2.0" + is-core-module@^2.11.0, is-core-module@^2.5.0, is-core-module@^2.8.1: version "2.12.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.0.tgz#36ad62f6f73c8253fd6472517a12483cf03e7ec4" @@ -13890,6 +14086,13 @@ is-core-module@^2.11.0, is-core-module@^2.5.0, is-core-module@^2.8.1: dependencies: has "^1.0.3" +is-core-module@^2.13.0: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== + dependencies: + hasown "^2.0.0" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -14050,22 +14253,6 @@ is-hex-prefixed@1.0.0: resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" integrity sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA== -is-installed-globally@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141" - integrity sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g== - dependencies: - global-dirs "^2.0.1" - is-path-inside "^3.0.1" - -is-installed-globally@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" - integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== - dependencies: - global-dirs "^3.0.0" - is-path-inside "^3.0.2" - is-interactive@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" @@ -14098,16 +14285,6 @@ is-negative-zero@^2.0.2: resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== -is-npm@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d" - integrity sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig== - -is-npm@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-5.0.0.tgz#43e8d65cc56e1b67f8d47262cf667099193f45a8" - integrity sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA== - is-number-object@^1.0.4: version "1.0.7" resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" @@ -14154,11 +14331,6 @@ is-object@^1.0.1, is-object@~1.0.1: resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf" integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== -is-path-inside@^3.0.1, is-path-inside@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== - is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" @@ -14191,16 +14363,6 @@ is-primitive@^2.0.0: resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" integrity sha512-N3w1tFaRfk3UrPfqeRyD+GYDASU3W5VinKhlORy8EWVf/sIdDL9GAcew85XmktCfH+ngG7SRXEVDoO18WMdB/Q== -is-promise@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" - integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== - -is-property@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" - integrity sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g== - is-regex@^1.0.4, is-regex@^1.1.4, is-regex@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" @@ -14267,6 +14429,13 @@ is-string@^1.0.5, is-string@^1.0.7: dependencies: has-tostringtag "^1.0.0" +is-subdir@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-subdir/-/is-subdir-1.2.0.tgz#b791cd28fab5202e91a08280d51d9d7254fd20d4" + integrity sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw== + dependencies: + better-path-resolve "1.0.0" + is-subset@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a6" @@ -14297,6 +14466,13 @@ is-typed-array@^1.1.10, is-typed-array@^1.1.3, is-typed-array@^1.1.9: gopd "^1.0.1" has-tostringtag "^1.0.0" +is-typed-array@^1.1.12: + version "1.1.12" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" + integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== + dependencies: + which-typed-array "^1.1.11" + is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -14321,11 +14497,6 @@ is-upper-case@^1.1.0: dependencies: upper-case "^1.1.0" -is-url@^1.2.2, is-url@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" - integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== - is-utf8@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" @@ -14348,7 +14519,7 @@ is-windows@^0.2.0: resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c" integrity sha512-n67eJYmXbniZB7RF4I/FTjK1s6RPOCTxhYrVYLRaCt3lF0mpWZPKr3T2LSZAqyjQsxR2qMmGYXXzK0YWwcPM1Q== -is-windows@^1.0.1, is-windows@^1.0.2: +is-windows@^1.0.0, is-windows@^1.0.1, is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== @@ -14365,20 +14536,6 @@ is-wsl@^2.1.1, is-wsl@^2.2.0: dependencies: is-docker "^2.0.0" -is-yarn-global@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" - integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== - -is2@^2.0.6: - version "2.0.9" - resolved "https://registry.yarnpkg.com/is2/-/is2-2.0.9.tgz#ff63b441f90de343fa8fac2125ee170da8e8240d" - integrity sha512-rZkHeBn9Zzq52sd9IUIV3a5mfwBY+o2HePMh0wkGBM4z4qjvy2GwVxQ6nNXSfw6MmVP6gf1QIlWjiOavhM3x5g== - dependencies: - deep-is "^0.1.3" - ip-regex "^4.1.0" - is-url "^1.2.4" - isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -14432,6 +14589,11 @@ isomorphic-fetch@^3.0.0: node-fetch "^2.6.1" whatwg-fetch "^3.4.1" +isomorphic-ws@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz#e5529148912ecb9b451b46ed44d53dae1ce04bbf" + integrity sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw== + isomorphic-ws@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" @@ -14529,7 +14691,7 @@ iterate-iterator@^1.0.1: resolved "https://registry.yarnpkg.com/iterate-iterator/-/iterate-iterator-1.0.2.tgz#551b804c9eaa15b847ea6a7cdc2f5bf1ec150f91" integrity sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw== -iterate-value@^1.0.0, iterate-value@^1.0.2: +iterate-value@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/iterate-value/-/iterate-value-1.0.2.tgz#935115bd37d006a52046535ebc8d07e9c9337f57" integrity sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ== @@ -14552,6 +14714,17 @@ jStat@^1.5.2: resolved "https://registry.yarnpkg.com/jStat/-/jStat-1.8.6.tgz#ab4d465b21f583d37a72ab2f97a300492da7575d" integrity sha512-Oh/ePZVSoFigme69pHTQudcGh64cpNH9Lz3hBZcRJWLrDqpw7JfuYU9F3dj9py3tBYmHz7og7ZT8hXTNbYq9Rw== +jaeger-client@^3.15.0: + version "3.19.0" + resolved "https://registry.yarnpkg.com/jaeger-client/-/jaeger-client-3.19.0.tgz#9b5bd818ebd24e818616ee0f5cffe1722a53ae6e" + integrity sha512-M0c7cKHmdyEUtjemnJyx/y9uX16XHocL46yQvyqDlPdvAcwPDbHrIbKjQdBqtiE4apQ/9dmr+ZLJYYPGnurgpw== + dependencies: + node-int64 "^0.4.0" + opentracing "^0.14.4" + thriftrw "^3.5.0" + uuid "^8.3.2" + xorshift "^1.1.1" + jake@^10.8.5: version "10.8.5" resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46" @@ -14946,31 +15119,21 @@ jest@^29.0.2: import-local "^3.0.2" jest-cli "^29.5.0" -jju@^1.1.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a" - integrity sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA== - jmespath@0.16.0: version "0.16.0" resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.16.0.tgz#b15b0a85dfd4d930d43e69ed605943c802785076" integrity sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw== -join-path@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/join-path/-/join-path-1.1.1.tgz#10535a126d24cbd65f7ffcdf15ef2e631076b505" - integrity sha512-jnt9OC34sLXMLJ6YfPQ2ZEKrR9mB5ZbSnQb4LPaOx1c5rTzxpR33L18jjp0r75mGGTJmsil3qwN1B5IBeTnSSA== - dependencies: - as-array "^2.0.0" - url-join "0.0.1" - valid-url "^1" - -jose@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/jose/-/jose-2.0.6.tgz#894ba19169af339d3911be933f913dd02fc57c7c" - integrity sha512-FVoPY7SflDodE4lknJmbAHSUjLCzE2H1F6MS0RYKMQ8SR+lNccpMf8R4eqkNYyyUjR5qZReOzZo5C5YiHOCjjg== +joi@^17.3.0: + version "17.11.0" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.11.0.tgz#aa9da753578ec7720e6f0ca2c7046996ed04fc1a" + integrity sha512-NgB+lZLNoqISVy1rZocE9PZI36bL/77ie924Ri43yEvi9GUUMPeyVIr8KdFTMUlby1p0PBYMk9spIxEUQYqrJQ== dependencies: - "@panva/asn1.js" "^1.0.0" + "@hapi/hoek" "^9.0.0" + "@hapi/topo" "^5.0.0" + "@sideway/address" "^4.1.3" + "@sideway/formula" "^3.0.1" + "@sideway/pinpoint" "^2.0.0" js-sdsl@^4.1.4: version "4.4.0" @@ -15005,7 +15168,7 @@ js-yaml@3.13.1: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@3.14.1, js-yaml@3.x, js-yaml@^3.10.0, js-yaml@^3.12.0, js-yaml@^3.13.0, js-yaml@^3.13.1: +js-yaml@3.14.1, js-yaml@3.x, js-yaml@^3.10.0, js-yaml@^3.12.0, js-yaml@^3.13.0, js-yaml@^3.13.1, js-yaml@^3.6.1: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -15020,11 +15183,6 @@ js-yaml@4.1.0, js-yaml@^4.1.0: dependencies: argparse "^2.0.1" -jsbi@^3.1.1: - version "3.2.5" - resolved "https://registry.yarnpkg.com/jsbi/-/jsbi-3.2.5.tgz#b37bb90e0e5c2814c1c2a1bcd8c729888a2e37d6" - integrity sha512-aBE4n43IPvjaddScbvWRA2YlTzKEynHzu7MqOyTipdHucf/VxS63ViCjxYRg86M8Rxwbt/GfzHl1kKERkt45fQ== - jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -15069,13 +15227,6 @@ json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== -json-parse-helpfulerror@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/json-parse-helpfulerror/-/json-parse-helpfulerror-1.0.3.tgz#13f14ce02eed4e981297b64eb9e3b932e2dd13dc" - integrity sha512-XgP0FGR77+QhUxjXkwOMkC94k3WtqEBfcnjWqhRd82qTat4SWKRE+9kUnynz/shm3I4ea2+qISvTIeGTNU7kJg== - dependencies: - jju "^1.1.0" - json-pointer@^0.6.1: version "0.6.2" resolved "https://registry.yarnpkg.com/json-pointer/-/json-pointer-0.6.2.tgz#f97bd7550be5e9ea901f8c9264c9d436a22a93cd" @@ -15083,13 +15234,6 @@ json-pointer@^0.6.1: dependencies: foreach "^2.0.4" -json-ptr@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/json-ptr/-/json-ptr-2.2.0.tgz#a4de4ed638cb23ae4cd4b51f8bf972a1c2293f1e" - integrity sha512-w9f6/zhz4kykltXMG7MLJWMajxiPj0q+uzQPR1cggNAE/sXoq/C5vjUb/7QNcC3rJsVIIKy37ALTXy1O+3c8QQ== - dependencies: - tslib "^2.2.0" - json-rpc-engine@^5.3.0: version "5.4.0" resolved "https://registry.yarnpkg.com/json-rpc-engine/-/json-rpc-engine-5.4.0.tgz#75758609d849e1dba1e09021ae473f3ab63161e5" @@ -15230,7 +15374,7 @@ jsonpointer@^5.0.0: resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-5.0.1.tgz#2110e0af0900fd37467b5907ecd13a7884a1b559" integrity sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ== -jsonwebtoken@^8.3.0, jsonwebtoken@^8.5.1: +jsonwebtoken@^8.5.1: version "8.5.1" resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== @@ -15284,19 +15428,7 @@ jwa@^2.0.0: ecdsa-sig-formatter "1.0.11" safe-buffer "^5.0.1" -jwks-rsa@^2.0.2: - version "2.1.5" - resolved "https://registry.yarnpkg.com/jwks-rsa/-/jwks-rsa-2.1.5.tgz#bb7bf8c5767836bc273bf5b27870066aca39c1bb" - integrity sha512-IODtn1SwEm7n6GQZnQLY0oxKDrMh7n/jRH1MzE8mlxWMrh2NnMyOsXTebu8vJ1qCpmuTJcL4DdiE0E4h8jnwsA== - dependencies: - "@types/express" "^4.17.14" - "@types/jsonwebtoken" "^8.5.9" - debug "^4.3.4" - jose "^2.0.6" - limiter "^1.1.5" - lru-memoizer "^2.1.4" - -jws@3.x.x, jws@^3.1.5, jws@^3.2.2: +jws@^3.1.5, jws@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== @@ -15312,15 +15444,6 @@ jws@^4.0.0: jwa "^2.0.0" safe-buffer "^5.0.1" -keccak256@^1.0.0: - version "1.0.6" - resolved "https://registry.yarnpkg.com/keccak256/-/keccak256-1.0.6.tgz#dd32fb771558fed51ce4e45a035ae7515573da58" - integrity sha512-8GLiM01PkdJVGUhR1e6M/AvWnSqYS0HaERI+K/QtStGDGlSTx2B1zTqZk4Zlqu5TxHJNTxWAdP9Y+WI50OApUw== - dependencies: - bn.js "^5.2.0" - buffer "^6.0.3" - keccak "^3.0.2" - keccak@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.1.tgz#ae30a0e94dbe43414f741375cff6d64c8bea0bff" @@ -15348,7 +15471,7 @@ keccak@^1.0.2: nan "^2.2.1" safe-buffer "^5.1.0" -keccak@^3.0.0, keccak@^3.0.2: +keccak@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.3.tgz#4bc35ad917be1ef54ff246f904c2bbbf9ac61276" integrity sha512-JZrLIAJWuZxKbCilMpNz5Vj7Vtb4scDG3dMXLOsbzBmQGyjwE61BbW7bJkfKKCShXiQZt3T6sBgALRtmd+nZaQ== @@ -15439,37 +15562,10 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== -knex@^2.1.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/knex/-/knex-2.4.2.tgz#a34a289d38406dc19a0447a78eeaf2d16ebedd61" - integrity sha512-tMI1M7a+xwHhPxjbl/H9K1kHX+VncEYcvCx5K00M16bWvpYPKAZd6QrCu68PtHAdIZNQPWZn0GVhqVBEthGWCg== - dependencies: - colorette "2.0.19" - commander "^9.1.0" - debug "4.3.4" - escalade "^3.1.1" - esm "^3.2.25" - get-package-type "^0.1.0" - getopts "2.3.0" - interpret "^2.2.0" - lodash "^4.17.21" - pg-connection-string "2.5.0" - rechoir "^0.8.0" - resolve-from "^5.0.0" - tarn "^3.0.2" - tildify "2.0.0" - -kuler@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" - integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== - -latest-version@^5.0.0, latest-version@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" - integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== - dependencies: - package-json "^6.3.0" +kleur@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780" + integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ== lazy-cache@^0.1.0: version "0.1.0" @@ -15488,13 +15584,6 @@ lazy-cache@^1.0.3: resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" integrity sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ== -lazystream@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.1.tgz#494c831062f1f9408251ec44db1cba29242a2638" - integrity sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw== - dependencies: - readable-stream "^2.0.5" - lcid@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" @@ -15805,11 +15894,6 @@ libnpmpublish@^6.0.4: semver "^7.3.7" ssri "^9.0.0" -limiter@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/limiter/-/limiter-1.1.5.tgz#8f92a25b3b16c6131293a0cc834b4a838a2aa7c2" - integrity sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA== - lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" @@ -15820,11 +15904,6 @@ lines-and-columns@~2.0.3: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-2.0.3.tgz#b2f0badedb556b747020ab8ea7f0373e22efac1b" integrity sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w== -listenercount@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/listenercount/-/listenercount-1.0.1.tgz#84c8a72ab59c4725321480c975e6508342e70937" - integrity sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ== - load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" @@ -15867,6 +15946,16 @@ load-json-file@^6.2.0: strip-bom "^4.0.0" type-fest "^0.6.0" +load-yaml-file@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/load-yaml-file/-/load-yaml-file-0.2.0.tgz#af854edaf2bea89346c07549122753c07372f64d" + integrity sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw== + dependencies: + graceful-fs "^4.1.5" + js-yaml "^3.13.0" + pify "^4.0.1" + strip-bom "^3.0.0" + locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" @@ -15902,28 +15991,11 @@ lodash-es@^4.2.1: resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== -lodash._isnative@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash._isnative/-/lodash._isnative-2.4.1.tgz#3ea6404b784a7be836c7b57580e1cdf79b14832c" - integrity sha512-BOlKGKNHhCHswGOWtmVb5zBygyxN7EmTuzVOSQI6QSoGhG+kvv71gICFS1TBpnqvT1n53txK8CDK3u5D2/GZxQ== - -lodash._objecttypes@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz#7c0b7f69d98a1f76529f890b0cdb1b4dfec11c11" - integrity sha512-XpqGh1e7hhkOzftBfWE7zt+Yn9mVHFkDhicVttvKLsoCMLVVL+xTQjfjB4X4vtznauxv0QZ5ZAeqjvat0dh62Q== - lodash._reinterpolate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" integrity sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA== -lodash._shimkeys@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash._shimkeys/-/lodash._shimkeys-2.4.1.tgz#6e9cc9666ff081f0b5a6c978b83e242e6949d203" - integrity sha512-lBrglYxLD/6KAJ8IEa5Lg+YHgNAL7FyKqXg4XOUI+Du/vtniLs1ZqS+yHNKPkK54waAgkdUnDOYaWf+rv4B+AA== - dependencies: - lodash._objecttypes "~2.4.1" - lodash.assign@^4.0.3, lodash.assign@^4.0.6: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" @@ -15944,31 +16016,11 @@ lodash.clone@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.clone/-/lodash.clone-4.5.0.tgz#195870450f5a13192478df4bc3d23d2dea1907b6" integrity sha512-GhrVeweiTD6uTmmn5hV/lzgCQhccwReIVRLHp7LT4SopOjqEZ5BbX8b5WWEtAKasjmy8hR7ZPwsYlxRCku5odg== -lodash.clonedeep@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" - integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ== - lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== -lodash.defaults@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" - integrity sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ== - -lodash.difference@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.difference/-/lodash.difference-4.5.0.tgz#9ccb4e505d486b91651345772885a2df27fd017c" - integrity sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA== - -lodash.flatten@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" - integrity sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g== - lodash.get@~4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" @@ -15984,16 +16036,6 @@ lodash.includes@^4.3.0: resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w== -lodash.isarguments@2.4.x: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-2.4.1.tgz#4931a9c08253adf091ae7ca192258a973876ecca" - integrity sha512-CyMQjsJqDgXL8M2xYAP6V2dlVXli8IhWXLsk19uXxiL9/qISjzQXyWtxsumR2q4CnR9FjCnxpuIO1d9KSKBcyA== - -lodash.isarguments@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" - integrity sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg== - lodash.isboolean@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" @@ -16019,18 +16061,6 @@ lodash.isnumber@^3.0.3: resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw== -lodash.isobject@^2.4.1, lodash.isobject@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-2.4.1.tgz#5a2e47fe69953f1ee631a7eba1fe64d2d06558f5" - integrity sha512-sTebg2a1PoicYEZXD5PBdQcTlIJ6hUslrlWr7iV0O7n+i4596s2NQ9I5CaZ5FbXSfya/9WQsrYLANUJv9paYVA== - dependencies: - lodash._objecttypes "~2.4.1" - -lodash.isobject@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-3.0.2.tgz#3c8fb8d5b5bf4bf90ae06e14f2a530a4ed935e1d" - integrity sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA== - lodash.isplainobject@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" @@ -16041,15 +16071,6 @@ lodash.isstring@^4.0.1: resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== -lodash.keys@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-2.4.1.tgz#48dea46df8ff7632b10d706b8acb26591e2b3727" - integrity sha512-ZpJhwvUXHSNL5wYd1RM6CUa2ZuqorG9ngoJ9Ix5Cce+uX7I5O/E06FCJdhSZ33b5dVyeQDnIlWH7B2s5uByZ7g== - dependencies: - lodash._isnative "~2.4.1" - lodash._shimkeys "~2.4.1" - lodash.isobject "~2.4.1" - lodash.memoize@4.x: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -16075,6 +16096,11 @@ lodash.sortby@^4.7.0: resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA== +lodash.startcase@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.startcase/-/lodash.startcase-4.4.0.tgz#9436e34ed26093ed7ffae1936144350915d9add8" + integrity sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg== + lodash.template@^4.4.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" @@ -16090,35 +16116,16 @@ lodash.templatesettings@^4.0.0: dependencies: lodash._reinterpolate "^3.0.0" -lodash.union@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" - integrity sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw== - -lodash.values@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash.values/-/lodash.values-2.4.1.tgz#abf514436b3cb705001627978cbcf30b1280eea4" - integrity sha512-fQwubKvj2Nox2gy6YnjFm8C1I6MIlzKUtBB+Pj7JGtloGqDDL5CPRr4DUUFWPwXWwAl2k3f4C3Aw8H1qAPB9ww== - dependencies: - lodash.keys "~2.4.1" - lodash.values@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.values/-/lodash.values-4.3.0.tgz#a3a6c2b0ebecc5c2cba1c17e6e620fe81b53d347" integrity sha512-r0RwvdCv8id9TUblb/O7rYPwVy6lerCbcawrfdo9iC/1t1wsNMJknO79WNBgwkH0hIeJ08jmvvESbFpNb4jH0Q== -lodash@4.17.21, lodash@^4.16.4, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.5, lodash@^4.2.1, lodash@~4.17.19: +lodash@4.17.21, lodash@^4.16.4, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.2.1, lodash@~4.17.19: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-symbols@2.2.0, log-symbols@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" - integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== - dependencies: - chalk "^2.0.1" - log-symbols@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" @@ -16134,23 +16141,23 @@ log-symbols@4.1.0, log-symbols@^4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" -logform@^2.3.2, logform@^2.4.0: - version "2.5.1" - resolved "https://registry.yarnpkg.com/logform/-/logform-2.5.1.tgz#44c77c34becd71b3a42a3970c77929e52c6ed48b" - integrity sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg== +log-symbols@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" + integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== dependencies: - "@colors/colors" "1.5.0" - "@types/triple-beam" "^1.3.2" - fecha "^4.2.0" - ms "^2.1.1" - safe-stable-stringify "^2.3.1" - triple-beam "^1.3.0" + chalk "^2.0.1" loglevel@^1.6.1, loglevel@^1.6.8: version "1.8.1" resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.1.tgz#5c621f83d5b48c54ae93b6156353f555963377b4" integrity sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg== +long@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/long/-/long-2.4.0.tgz#9fa180bb1d9500cdc29c4156766a1995e1f4524f" + integrity sha512-ijUtjmO/n2A5PaosNG9ZGDsQ3vxJg7ZW8vsY8Kp0f2yIZWhSJvjmegV7t+9RPQKxKrvj8yKGehhS+po14hPLGQ== + long@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" @@ -16234,34 +16241,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -lru-cache@^7.14.1, lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: +lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: version "7.18.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== -lru-cache@~4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.0.2.tgz#1d17679c069cda5d040991a09dbc2c0db377e55e" - integrity sha512-uQw9OqphAGiZhkuPlpFGmdTU2tEuhxTourM/19qGJrxBPHAr/f8BT1a0i/lOclESnGatdJG/UCkP9kZB/Lh1iw== - dependencies: - pseudomap "^1.0.1" - yallist "^2.0.0" - -lru-memoizer@^2.1.4: - version "2.2.0" - resolved "https://registry.yarnpkg.com/lru-memoizer/-/lru-memoizer-2.2.0.tgz#b9d90c91637b4b1a423ef76f3156566691293df8" - integrity sha512-QfOZ6jNkxCcM/BkIPnFsqDhtrazLRsghi9mBwFAzol5GCvj4EkFT899Za3+QwikCg5sRX8JstioBDwOxEyzaNw== - dependencies: - lodash.clonedeep "^4.5.0" - lru-cache "~4.0.0" - -lru-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" - integrity sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ== - dependencies: - es5-ext "~0.10.2" - ltgt@2.2.1, ltgt@^2.1.2, ltgt@~2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5" @@ -16287,7 +16271,7 @@ make-dir@^2.0.0, make-dir@^2.1.0: pify "^4.0.1" semver "^5.6.0" -make-dir@^3.0.0, make-dir@^3.1.0: +make-dir@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== @@ -16321,28 +16305,6 @@ make-fetch-happen@^10.0.3, make-fetch-happen@^10.0.6: socks-proxy-agent "^7.0.0" ssri "^9.0.0" -make-fetch-happen@^9.1.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz#53085a09e7971433e6765f7971bf63f4e05cb968" - integrity sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg== - dependencies: - agentkeepalive "^4.1.3" - cacache "^15.2.0" - http-cache-semantics "^4.1.0" - http-proxy-agent "^4.0.1" - https-proxy-agent "^5.0.0" - is-lambda "^1.0.1" - lru-cache "^6.0.0" - minipass "^3.1.3" - minipass-collect "^1.0.2" - minipass-fetch "^1.3.2" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.4" - negotiator "^0.6.2" - promise-retry "^2.0.1" - socks-proxy-agent "^6.0.0" - ssri "^8.0.0" - makeerror@1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" @@ -16384,23 +16346,6 @@ markdown-table@^1.1.3: resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-1.1.3.tgz#9fcb69bcfdb8717bfd0398c6ec2d93036ef8de60" integrity sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q== -marked-terminal@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/marked-terminal/-/marked-terminal-3.3.0.tgz#25ce0c0299285998c7636beaefc87055341ba1bd" - integrity sha512-+IUQJ5VlZoAFsM5MHNT7g3RHSkA3eETqhRCdXv4niUMAKHQ7lb1yvAcuGPmm4soxhmtX13u4Li6ZToXtvSEH+A== - dependencies: - ansi-escapes "^3.1.0" - cardinal "^2.1.1" - chalk "^2.4.1" - cli-table "^0.3.1" - node-emoji "^1.4.1" - supports-hyperlinks "^1.0.1" - -marked@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/marked/-/marked-0.7.0.tgz#b64201f051d271b1edc10a04d1ae9b74bb8e5c0e" - integrity sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg== - marked@^1.1.1: version "1.2.9" resolved "https://registry.yarnpkg.com/marked/-/marked-1.2.9.tgz#53786f8b05d4c01a2a5a76b7d1ec9943d29d72dc" @@ -16489,20 +16434,6 @@ memdown@~3.0.0: ltgt "~2.2.0" safe-buffer "~5.1.1" -memoizee@^0.4.14: - version "0.4.15" - resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.15.tgz#e6f3d2da863f318d02225391829a6c5956555b72" - integrity sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ== - dependencies: - d "^1.0.1" - es5-ext "^0.10.53" - es6-weak-map "^2.0.3" - event-emitter "^0.3.5" - is-promise "^2.2.2" - lru-queue "^0.1.0" - next-tick "^1.1.0" - timers-ext "^0.1.7" - memory-level@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/memory-level/-/memory-level-1.0.0.tgz#7323c3fd368f9af2f71c3cd76ba403a17ac41692" @@ -16517,6 +16448,23 @@ memorystream@^0.3.1: resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" integrity sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw== +meow@^6.0.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/meow/-/meow-6.1.1.tgz#1ad64c4b76b2a24dfb2f635fddcadf320d251467" + integrity sha512-3YffViIt2QWgTy6Pale5QpopX/IvU3LPL03jOTqp6pGj3VjesdO/U8CuHMKpnQr4shCNCM5fd5XFFvIIl6JBHg== + dependencies: + "@types/minimist" "^1.2.0" + camelcase-keys "^6.2.2" + decamelize-keys "^1.1.0" + hard-rejection "^2.1.0" + minimist-options "^4.0.2" + normalize-package-data "^2.5.0" + read-pkg-up "^7.0.1" + redent "^3.0.0" + trim-newlines "^3.0.0" + type-fest "^0.13.1" + yargs-parser "^18.1.3" + meow@^8.0.0: version "8.1.2" resolved "https://registry.yarnpkg.com/meow/-/meow-8.1.2.tgz#bcbe45bda0ee1729d350c03cffc8395a36c4e897" @@ -16576,11 +16524,16 @@ merkle-patricia-tree@^2.1.2, merkle-patricia-tree@^2.3.2: rlp "^2.0.0" semaphore ">=1.0.1" -methods@^1.1.2, methods@~1.1.2: +methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== +micro-ftch@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/micro-ftch/-/micro-ftch-0.3.1.tgz#6cb83388de4c1f279a034fb0cf96dfc050853c5f" + integrity sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg== + micromatch@^2.3.11, micromatch@^2.3.7: version "2.3.11" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" @@ -16668,16 +16621,11 @@ mime@1.6.0: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@2.6.0, mime@^2.2.0, mime@^2.5.2: +mime@^2.2.0: version "2.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== -mime@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" - integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== - mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" @@ -16758,14 +16706,14 @@ minimatch@5.0.1: dependencies: brace-expansion "^2.0.1" -minimatch@^5.0.1, minimatch@^5.1.0: +minimatch@^5.0.1: version "5.1.6" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== dependencies: brace-expansion "^2.0.1" -minimist-options@4.1.0: +minimist-options@4.1.0, minimist-options@^4.0.2: version "4.1.0" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A== @@ -16791,17 +16739,6 @@ minipass-collect@^1.0.2: dependencies: minipass "^3.0.0" -minipass-fetch@^1.3.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.4.1.tgz#d75e0091daac1b0ffd7e9d41629faff7d0c1f1b6" - integrity sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw== - dependencies: - minipass "^3.1.0" - minipass-sized "^1.0.3" - minizlib "^2.0.0" - optionalDependencies: - encoding "^0.1.12" - minipass-fetch@^2.0.3: version "2.1.2" resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-2.1.2.tgz#95560b50c472d81a3bc76f20ede80eaed76d8add" @@ -16828,7 +16765,7 @@ minipass-json-stream@^1.0.1: jsonparse "^1.3.1" minipass "^3.0.0" -minipass-pipeline@^1.2.2, minipass-pipeline@^1.2.4: +minipass-pipeline@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== @@ -16850,7 +16787,7 @@ minipass@^2.6.0, minipass@^2.9.0: safe-buffer "^5.1.2" yallist "^3.0.0" -minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3, minipass@^3.1.6: +minipass@^3.0.0, minipass@^3.1.1, minipass@^3.1.6: version "3.3.6" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== @@ -16869,7 +16806,7 @@ minizlib@^1.3.3: dependencies: minipass "^2.9.0" -minizlib@^2.0.0, minizlib@^2.1.1, minizlib@^2.1.2: +minizlib@^2.1.1, minizlib@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== @@ -16893,6 +16830,11 @@ mixin-object@^2.0.0: for-in "^0.1.3" is-extendable "^0.1.1" +mixme@^0.5.1: + version "0.5.9" + resolved "https://registry.yarnpkg.com/mixme/-/mixme-0.5.9.tgz#a5a58e17354632179ff3ce5b0fc130899c8ba81c" + integrity sha512-VC5fg6ySUscaWUpI4gxCBTQMH2RdUpNrk+MsbpCYtIvf9SBJdiUey4qE7BXviJsJR4nDQxCZ+3yaYNW3guz/Pw== + mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: version "0.5.3" resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" @@ -16926,13 +16868,6 @@ mkdirp@0.5.1: dependencies: minimist "0.0.8" -mkdirp@0.5.4: - version "0.5.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.4.tgz#fd01504a6797ec5c9be81ff43d204961ed64a512" - integrity sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw== - dependencies: - minimist "^1.2.5" - mkdirp@0.5.5: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" @@ -16940,7 +16875,7 @@ mkdirp@0.5.5: dependencies: minimist "^1.2.5" -mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@^0.5.6, mkdirp@~0.5.1: +mkdirp@0.5.x, mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@~0.5.1: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== @@ -17054,35 +16989,6 @@ mocha@^5.2.0: mkdirp "0.5.1" supports-color "5.4.0" -mocha@^6.2.2: - version "6.2.3" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-6.2.3.tgz#e648432181d8b99393410212664450a4c1e31912" - integrity sha512-0R/3FvjIGH3eEuG17ccFPk117XL2rWxatr81a57D+r/x2uTYZRbdZ4oVidEUMh2W2TJDa7MdAb12Lm2/qrKajg== - dependencies: - ansi-colors "3.2.3" - browser-stdout "1.3.1" - debug "3.2.6" - diff "3.5.0" - escape-string-regexp "1.0.5" - find-up "3.0.0" - glob "7.1.3" - growl "1.10.5" - he "1.2.0" - js-yaml "3.13.1" - log-symbols "2.2.0" - minimatch "3.0.4" - mkdirp "0.5.4" - ms "2.1.1" - node-environment-flags "1.0.5" - object.assign "4.1.0" - strip-json-comments "2.0.1" - supports-color "6.0.0" - which "1.3.1" - wide-align "1.1.3" - yargs "13.3.2" - yargs-parser "13.1.2" - yargs-unparser "1.6.0" - mocha@^7.1.1: version "7.2.0" resolved "https://registry.yarnpkg.com/mocha/-/mocha-7.2.0.tgz#01cc227b00d875ab1eed03a75106689cfed5a604" @@ -17131,6 +17037,11 @@ modify-values@^1.0.0: resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== +module-details-from-path@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b" + integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A== + module-error@^1.0.1, module-error@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/module-error/-/module-error-1.0.2.tgz#8d1a48897ca883f47a45816d4fb3e3c6ba404d86" @@ -17141,22 +17052,11 @@ module-not-found-error@^1.0.1: resolved "https://registry.yarnpkg.com/module-not-found-error/-/module-not-found-error-1.0.1.tgz#cf8b4ff4f29640674d6cdd02b0e3bc523c2bbdc0" integrity sha512-pEk4ECWQXV6z2zjhRZUongnLJNUeGQJ3w6OQ5ctGwD+i5o93qjRQUk2Rt6VdNeu3sEP0AB4LcfvdebpxBRVr4g== -moment@^2.10.6, moment@^2.19.3, moment@^2.22.1, moment@^2.29.0: +moment@^2.10.6, moment@^2.22.1, moment@^2.29.0: version "2.29.4" resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== -morgan@^1.10.0, morgan@^1.8.2: - version "1.10.0" - resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7" - integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ== - dependencies: - basic-auth "~2.0.1" - debug "2.6.9" - depd "~2.0.0" - on-finished "~2.3.0" - on-headers "~1.0.2" - mri@^1.1.4: version "1.2.0" resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b" @@ -17189,15 +17089,6 @@ msal@^1.0.2: dependencies: tslib "^1.9.3" -mssql@^6.3.1: - version "6.4.1" - resolved "https://registry.yarnpkg.com/mssql/-/mssql-6.4.1.tgz#24fcde0a32349aa6e1ba2123119ab254e355f8bb" - integrity sha512-G1I7mM0gfxcH5TGSNoVmxq13Mve5YnQgRAlonqaMlHEjHjMn1g04bsrIQbVHFRdI6++dw/FGWlh8GoItJMoUDw== - dependencies: - debug "^4.3.3" - tarn "^1.1.5" - tedious "^6.7.1" - multi-progress@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/multi-progress/-/multi-progress-2.0.0.tgz#29ccb42cf24874b1c6384f03127ce5dff7b22f2c" @@ -17286,20 +17177,6 @@ mv@~2: ncp "~2.0.0" rimraf "~2.4.0" -mysql2@^2.1.0: - version "2.3.3" - resolved "https://registry.yarnpkg.com/mysql2/-/mysql2-2.3.3.tgz#944f3deca4b16629052ff8614fbf89d5552545a0" - integrity sha512-wxJUev6LgMSgACDkb/InIFxDprRa6T95+VEoR+xPvtngtccNH2dGjEB/fVZ8yg1gWv1510c9CvXuJHi5zUm0ZA== - dependencies: - denque "^2.0.1" - generate-function "^2.3.1" - iconv-lite "^0.6.3" - long "^4.0.0" - lru-cache "^6.0.0" - named-placeholders "^1.1.2" - seq-queue "^0.0.5" - sqlstring "^2.3.2" - mythxjs@^1.3.11: version "1.3.13" resolved "https://registry.yarnpkg.com/mythxjs/-/mythxjs-1.3.13.tgz#b27ec4170307a3f3bd3943e7e8628c2eb070d09d" @@ -17318,14 +17195,7 @@ mz@^2.7.0: object-assign "^4.0.1" thenify-all "^1.0.0" -named-placeholders@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/named-placeholders/-/named-placeholders-1.1.3.tgz#df595799a36654da55dda6152ba7a137ad1d9351" - integrity sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w== - dependencies: - lru-cache "^7.14.1" - -nan@^2.13.2, nan@^2.14.0, nan@^2.17.0, nan@^2.2.1: +nan@^2.13.2, nan@^2.14.0, nan@^2.2.1: version "2.17.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== @@ -17377,21 +17247,6 @@ napi-macros@~2.0.0: resolved "https://registry.yarnpkg.com/napi-macros/-/napi-macros-2.0.0.tgz#2b6bae421e7b96eb687aa6c77a7858640670001b" integrity sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg== -nash@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/nash/-/nash-3.0.0.tgz#bced3a0cb8434c2ad30d1a0d567cfc0c37128eea" - integrity sha512-M5SahEycXUmko3zOvsBkF6p94CWLhnyy9hfpQ9Qzp+rQkQ8D1OaTlfTl1OBWktq9Fak3oDXKU+ev7tiMaMu+1w== - dependencies: - async "^1.3.0" - flat-arguments "^1.0.0" - lodash "^4.17.5" - minimist "^1.1.0" - -native-duplexpair@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/native-duplexpair/-/native-duplexpair-1.0.0.tgz#7899078e64bf3c8a3d732601b3d40ff05db58fa0" - integrity sha512-E7QQoM+3jvNtlmyfqRZ0/U75VFgCls+fSkbml2MpgWkWyz3ox8Y58gNhfuziuQYGNNQAbFZJQck55LHCnCK6CA== - natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -17416,7 +17271,7 @@ needle@^2.2.1: iconv-lite "^0.4.4" sax "^1.2.4" -negotiator@0.6.3, negotiator@^0.6.2, negotiator@^0.6.3: +negotiator@0.6.3, negotiator@^0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== @@ -17433,12 +17288,7 @@ neodoc@^2.0.2: dependencies: ansi-regex "^2.0.0" -netmask@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7" - integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg== - -next-tick@1, next-tick@^1.1.0: +next-tick@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== @@ -17489,21 +17339,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-emoji@^1.4.1: - version "1.11.0" - resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.11.0.tgz#69a0150e6946e2f115e9d7ea4df7971e2628301c" - integrity sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A== - dependencies: - lodash "^4.17.21" - -node-environment-flags@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.5.tgz#fa930275f5bf5dae188d6192b24b4c8bbac3d76a" - integrity sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ== - dependencies: - object.getownpropertydescriptors "^2.0.3" - semver "^5.7.0" - node-environment-flags@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088" @@ -17512,11 +17347,6 @@ node-environment-flags@1.0.6: object.getownpropertydescriptors "^2.0.3" semver "^5.7.0" -node-fetch@2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" - integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== - node-fetch@2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" @@ -17574,23 +17404,7 @@ node-gyp-build@~4.1.0: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.1.1.tgz#d7270b5d86717068d114cc57fff352f96d745feb" integrity sha512-dSq1xmcPDKPZ2EED2S6zw/b9NKsqzXRE6dVr8TVQnI3FJOTteUMuqF3Qqs6LZg+mLGYJWqQzMbIjMtJqTv87nQ== -node-gyp@8.x: - version "8.4.1" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937" - integrity sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w== - dependencies: - env-paths "^2.2.0" - glob "^7.1.4" - graceful-fs "^4.2.6" - make-fetch-happen "^9.1.0" - nopt "^5.0.0" - npmlog "^6.0.0" - rimraf "^3.0.2" - semver "^7.3.5" - tar "^6.1.2" - which "^2.0.2" - -node-gyp@^9.0.0, node-gyp@^9.3.0: +node-gyp@^9.0.0: version "9.3.1" resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.3.1.tgz#1e19f5f290afcc9c46973d68700cbd21a96192e4" integrity sha512-4Q16ZCqq3g8awk6UplT7AuxQ35XN4R/yf/+wSAwcBUAjg7l58RTactWaP8fIDTi0FzI7YcVLujwExakZlfWkXg== @@ -17657,11 +17471,6 @@ node-releases@^2.0.8: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f" integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w== -node-version@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/node-version/-/node-version-1.2.0.tgz#34fde3ffa8e1149bd323983479dda620e1b5060d" - integrity sha512-ma6oU4Sk0qOoKEAymVoTvk8EdXEobdS7m/mAGhDJ8Rouugho48crHBORAmy5BoOcv8wraPM6xumapQp5hl4iIQ== - nofilter@^1.0.3, nofilter@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-1.0.4.tgz#78d6f4b6a613e7ced8b015cec534625f7667006e" @@ -17876,16 +17685,6 @@ npmlog@^4.0.1, npmlog@^4.0.2: gauge "~2.7.3" set-blocking "~2.0.0" -npmlog@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" - integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== - dependencies: - are-we-there-yet "^2.0.0" - console-control-strings "^1.1.0" - gauge "^3.0.0" - set-blocking "^2.0.0" - npmlog@^6.0.0, npmlog@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" @@ -17916,11 +17715,6 @@ number-to-bn@1.7.0: bn.js "4.11.6" strip-hex-prefix "1.0.0" -numeral@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/numeral/-/numeral-2.0.6.tgz#4ad080936d443c2561aed9f2197efffe25f4e506" - integrity sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA== - numeric@^1.2.6: version "1.2.6" resolved "https://registry.yarnpkg.com/numeric/-/numeric-1.2.6.tgz#765b02bef97988fcf880d4eb3f36b80fa31335aa" @@ -18011,6 +17805,11 @@ object-inspect@^1.12.3, object-inspect@^1.9.0, object-inspect@~1.12.3: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== +object-inspect@^1.13.1: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + object-is@^1.0.1: version "1.1.5" resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" @@ -18116,23 +17915,10 @@ oboe@2.1.5: dependencies: http-https "^1.0.0" -"old-identity-sdk@npm:@celo/identity@1.5.2": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@celo/identity/-/identity-1.5.2.tgz#6401aeefbf7893374f6f940c9e0918d0a75d0411" - integrity sha512-/9JTL5P4xTY37hgu6qh5tU1d2GS3duBjP3QL600Zz1KAQrUVgb8g8JPpiRY21oEK6L7ZoNTukQJIuM3sbi//vg== - dependencies: - "@celo/base" "1.5.2" - "@celo/contractkit" "1.5.2" - "@celo/phone-number-privacy-common" "1.0.39" - "@celo/utils" "1.5.2" - "@types/debug" "^4.1.5" - bignumber.js "^9.0.0" - blind-threshold-bls "https://github.com/celo-org/blind-threshold-bls-wasm#e1e2f8a" - cross-fetch "3.0.4" - debug "^4.1.1" - elliptic "^6.5.4" - fp-ts "2.1.1" - io-ts "2.0.1" +obuf@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" + integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== omni-fetch@^0.2.3: version "0.2.3" @@ -18143,25 +17929,13 @@ omni-fetch@^0.2.3: "@types/node" "^6.0.52" caw "^1.2.0" -on-finished@2.4.1, on-finished@^2.2.0: +on-finished@2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== dependencies: ee-first "1.1.1" -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== - dependencies: - ee-first "1.1.1" - -on-headers@^1.0.0, on-headers@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" - integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== - once@1.x, once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -18169,13 +17943,6 @@ once@1.x, once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" -one-time@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/one-time/-/one-time-1.0.0.tgz#e06bc174aed214ed58edede573b433bbf827cb45" - integrity sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g== - dependencies: - fn.name "1.x.x" - onetime@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" @@ -18190,13 +17957,6 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" -open@^6.3.0: - version "6.4.0" - resolved "https://registry.yarnpkg.com/open/-/open-6.4.0.tgz#5c13e96d0dc894686164f18965ecfe889ecfc8a9" - integrity sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg== - dependencies: - is-wsl "^1.1.0" - open@^7.0.0, open@^7.4.2: version "7.4.2" resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" @@ -18214,18 +17974,16 @@ open@^8.4.0: is-docker "^2.1.1" is-wsl "^2.2.0" -openapi3-ts@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/openapi3-ts/-/openapi3-ts-2.0.2.tgz#a200dd838bf24c9086c8eedcfeb380b7eb31e82a" - integrity sha512-TxhYBMoqx9frXyOgnRHufjQfPXomTIHYKhSKJ6jHfj13kS8OEIhvmE8CTuQyKtjjWttAjX5DPxM1vmalEpo8Qw== - dependencies: - yaml "^1.10.2" - opencollective-postinstall@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259" integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q== +opentracing@^0.14.4: + version "0.14.7" + resolved "https://registry.yarnpkg.com/opentracing/-/opentracing-0.14.7.tgz#25d472bd0296dc0b64d7b94cbc995219031428f5" + integrity sha512-vz9iS7MJ5+Bp1URw8Khvdyw1H/hGvzHWlKQ7eRrQojSCDL1/SrWfrY9QebLw97n2deyRtzHRC3MkQfVNUCo91Q== + openzeppelin-solidity@^2.5.0: version "2.5.1" resolved "https://registry.yarnpkg.com/openzeppelin-solidity/-/openzeppelin-solidity-2.5.1.tgz#1cdcce30c4c6a7b6625dab62ccd0440a813ab597" @@ -18314,6 +18072,11 @@ osenv@^0.1.4: os-homedir "^1.0.0" os-tmpdir "^1.0.0" +outdent@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/outdent/-/outdent-0.5.0.tgz#9e10982fdc41492bb473ad13840d22f9655be2ff" + integrity sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q== + p-cancelable@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" @@ -18339,10 +18102,12 @@ p-defer@^1.0.0: resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" integrity sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw== -p-defer@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-3.0.0.tgz#d1dceb4ee9b2b604b1d94ffec83760175d4e6f83" - integrity sha512-ugZxsxmtTln604yeYd29EGrNhazN2lywetzpKhfmQjW/VJmhpDmWbiX+h0zL8V91R0UXkhb3KtPmyq9PZw3aYw== +p-filter@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-filter/-/p-filter-2.1.0.tgz#1b1472562ae7a0f742f0f3d3d3718ea66ff9c09c" + integrity sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw== + dependencies: + p-map "^2.0.0" p-finally@^1.0.0: version "1.0.0" @@ -18373,7 +18138,7 @@ p-limit@^2.0.0, p-limit@^2.2.0: dependencies: p-try "^2.0.0" -p-limit@^3.0.1, p-limit@^3.0.2, p-limit@^3.1.0: +p-limit@^3.0.2, p-limit@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== @@ -18413,6 +18178,11 @@ p-map-series@^2.1.0: resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-2.1.0.tgz#7560d4c452d9da0c07e692fdbfe6e2c81a2a91f2" integrity sha512-RpYIIK1zXSNEOdwxcfe7FdvGcs7+y5n8rifMhMNWvaxRNMPINJHF5GDeuVxWqnfrcHPSCnp7Oo5yNXHId9Av2Q== +p-map@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" + integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== + p-map@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" @@ -18469,40 +18239,6 @@ p-waterfall@^2.1.1: dependencies: p-reduce "^2.0.0" -pac-proxy-agent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-5.0.0.tgz#b718f76475a6a5415c2efbe256c1c971c84f635e" - integrity sha512-CcFG3ZtnxO8McDigozwE3AqAw15zDvGH+OjXO4kzf7IkEKkQ4gxQ+3sdF50WmhQ4P/bVusXcqNE2S3XrNURwzQ== - dependencies: - "@tootallnate/once" "1" - agent-base "6" - debug "4" - get-uri "3" - http-proxy-agent "^4.0.1" - https-proxy-agent "5" - pac-resolver "^5.0.0" - raw-body "^2.2.0" - socks-proxy-agent "5" - -pac-resolver@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-5.0.1.tgz#c91efa3a9af9f669104fa2f51102839d01cde8e7" - integrity sha512-cy7u00ko2KVgBAjuhevqpPeHIkCIqPe1v24cydhWjmeuzaBfmUWFCZJ1iAh5TuVzVZoUzXIW7K8sMYOZ84uZ9Q== - dependencies: - degenerator "^3.0.2" - ip "^1.1.5" - netmask "^2.0.2" - -package-json@^6.3.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" - integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== - dependencies: - got "^9.6.0" - registry-auth-token "^4.0.0" - registry-url "^5.0.0" - semver "^6.2.0" - packet-reader@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/packet-reader/-/packet-reader-1.0.0.tgz#9238e5480dedabacfe1fe3f2771063f164157d74" @@ -18775,13 +18511,6 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== -path-to-regexp@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" - integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== - dependencies: - isarray "0.0.1" - path-to-regexp@^2.2.1: version "2.4.0" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.4.0.tgz#35ce7f333d5616f1c1e1bfe266c3aba2e5b2e704" @@ -18847,16 +18576,16 @@ pg-connection-string@0.1.3: resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-0.1.3.tgz#da1847b20940e42ee1492beaf65d49d91b245df7" integrity sha512-i0NV/CrSkFTaiOQs9AGy3tq0dkSjtTd4d7DfsjeDVZAA4aIHInwfFEmriNYGGJUfZ5x6IAC/QddoUpUJjQAi0w== -pg-connection-string@2.5.0, pg-connection-string@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.5.0.tgz#538cadd0f7e603fc09a12590f3b8a452c2c0cf34" - integrity sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ== - pg-int8@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c" integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw== +pg-numeric@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pg-numeric/-/pg-numeric-1.0.2.tgz#816d9a44026086ae8ae74839acd6a09b0636aa3a" + integrity sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw== + pg-packet-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/pg-packet-stream/-/pg-packet-stream-1.1.0.tgz#e45c3ae678b901a2873af1e17b92d787962ef914" @@ -18867,12 +18596,7 @@ pg-pool@^2.0.10: resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-2.0.10.tgz#842ee23b04e86824ce9d786430f8365082d81c4a" integrity sha512-qdwzY92bHf3nwzIUcj+zJ0Qo5lpG/YxchahxIN8+ZVmXqkahKXsnl2aiJPHLYN9o5mB/leG+Xh6XKxtP7e0sjg== -pg-pool@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.6.0.tgz#3190df3e4747a0d23e5e9e8045bcd99bda0a712e" - integrity sha512-clFRf2ksqd+F497kWFyM21tMjeikn60oGDmqMT8UBrynEwVEX/5R5xd2sdvdo1cZCFlguORNpVuqxIj+aK4cfQ== - -pg-protocol@^1.2.0, pg-protocol@^1.6.0: +pg-protocol@*, pg-protocol@^1.2.0: version "1.6.0" resolved "https://registry.yarnpkg.com/pg-protocol/-/pg-protocol-1.6.0.tgz#4c91613c0315349363af2084608db843502f8833" integrity sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q== @@ -18888,6 +18612,19 @@ pg-types@^2.1.0, pg-types@^2.2.0: postgres-date "~1.0.4" postgres-interval "^1.1.0" +pg-types@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-4.0.1.tgz#31857e89d00a6c66b06a14e907c3deec03889542" + integrity sha512-hRCSDuLII9/LE3smys1hRHcu5QGcLs9ggT7I/TCs0IE+2Eesxi9+9RWAAwZ0yaGjxoWICF/YHLOEjydGujoJ+g== + dependencies: + pg-int8 "1.0.1" + pg-numeric "1.0.2" + postgres-array "~3.0.1" + postgres-bytea "~3.0.0" + postgres-date "~2.0.1" + postgres-interval "^3.0.0" + postgres-range "^1.1.1" + pg@^7.18.0: version "7.18.2" resolved "https://registry.yarnpkg.com/pg/-/pg-7.18.2.tgz#4e219f05a00aff4db6aab1ba02f28ffa4513b0bb" @@ -18902,19 +18639,6 @@ pg@^7.18.0: pgpass "1.x" semver "4.3.2" -pg@^8.2.1: - version "8.10.0" - resolved "https://registry.yarnpkg.com/pg/-/pg-8.10.0.tgz#5b8379c9b4a36451d110fc8cd98fc325fe62ad24" - integrity sha512-ke7o7qSTMb47iwzOSaZMfeR7xToFdkE71ifIipOAAaLIM0DYzfOAXlgFFmYUIE2BcJtvnVlGCID84ZzCegE8CQ== - dependencies: - buffer-writer "2.0.0" - packet-reader "1.0.0" - pg-connection-string "^2.5.0" - pg-pool "^3.6.0" - pg-protocol "^1.6.0" - pg-types "^2.1.0" - pgpass "1.x" - pgpass@1.x: version "1.0.5" resolved "https://registry.yarnpkg.com/pgpass/-/pgpass-1.0.5.tgz#9b873e4a564bb10fa7a7dbd55312728d422a223d" @@ -19017,15 +18741,6 @@ popper.js@1.14.3: resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.14.3.tgz#1438f98d046acf7b4d78cd502bf418ac64d4f095" integrity sha512-3lmujhsHXzb83+sI0PzfrE3O1XHZG8m8MXNMTupvA6LrM1/nnsiqYaacYc/RIente9VqnTDPztGEM8uvPAMGyg== -portfinder@^1.0.23: - version "1.0.32" - resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.32.tgz#2fe1b9e58389712429dc2bea5beb2146146c7f81" - integrity sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg== - dependencies: - async "^2.6.4" - debug "^3.2.7" - mkdirp "^0.5.6" - posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" @@ -19036,16 +18751,33 @@ postgres-array@~2.0.0: resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e" integrity sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA== +postgres-array@~3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-3.0.2.tgz#68d6182cb0f7f152a7e60dc6a6889ed74b0a5f98" + integrity sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog== + postgres-bytea@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-1.0.0.tgz#027b533c0aa890e26d172d47cf9ccecc521acd35" integrity sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w== +postgres-bytea@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-3.0.0.tgz#9048dc461ac7ba70a6a42d109221619ecd1cb089" + integrity sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw== + dependencies: + obuf "~1.1.2" + postgres-date@~1.0.4: version "1.0.7" resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.7.tgz#51bc086006005e5061c591cee727f2531bf641a8" integrity sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q== +postgres-date@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-2.0.1.tgz#638b62e5c33764c292d37b08f5257ecb09231457" + integrity sha512-YtMKdsDt5Ojv1wQRvUhnyDJNSr2dGIC96mQVKz7xufp07nfuFONzdaowrMHjlAzY6GDLd4f+LUHHAAM1h4MdUw== + postgres-interval@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-1.2.0.tgz#b460c82cb1587507788819a06aa0fffdb3544695" @@ -19053,6 +18785,16 @@ postgres-interval@^1.1.0: dependencies: xtend "^4.0.0" +postgres-interval@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-3.0.0.tgz#baf7a8b3ebab19b7f38f07566c7aab0962f0c86a" + integrity sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw== + +postgres-range@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/postgres-range/-/postgres-range-1.1.3.tgz#9ccd7b01ca2789eb3c2e0888b3184225fa859f76" + integrity sha512-VdlZoocy5lCP0c/t66xAfclglEapXPCIVhqqJRncYpvbCgImF0w67aPKfbqUMr72tO2k5q0TdTZwCLjPTI6C9g== + pouchdb-abstract-mapreduce@7.3.1: version "7.3.1" resolved "https://registry.yarnpkg.com/pouchdb-abstract-mapreduce/-/pouchdb-abstract-mapreduce-7.3.1.tgz#96ff4a0f41cbe273f3f52fde003b719005a2093c" @@ -19281,6 +19023,16 @@ precond@0.2: resolved "https://registry.yarnpkg.com/precond/-/precond-0.2.3.tgz#aa9591bcaa24923f1e0f4849d240f47efc1075ac" integrity sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ== +preferred-pm@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/preferred-pm/-/preferred-pm-3.1.2.tgz#aedb70550734a574dffcbf2ce82642bd1753bdd6" + integrity sha512-nk7dKrcW8hfCZ4H6klWcdRknBOXWzNQByJ0oJyX97BOupsYD+FzLS4hflgEu/uPUEHZCuRfMxzCBsuWd7OzT8Q== + dependencies: + find-up "^5.0.0" + find-yarn-workspace-root2 "1.2.16" + path-exists "^4.0.0" + which-pm "2.0.0" + prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -19374,16 +19126,16 @@ proc-log@^2.0.0, proc-log@^2.0.1: resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-2.0.1.tgz#8f3f69a1f608de27878f91f5c688b225391cb685" integrity sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw== -process-nextick-args@~1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" - integrity sha512-yN0WQmuCX63LP/TMvAg31nvT6m4vDqJEiiv2CAZqWOGNWutc9DfDk1NPYYmKUFmaVM2UwDowH4u5AHWYP/jxKw== - process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== +process@^0.10.0: + version "0.10.1" + resolved "https://registry.yarnpkg.com/process/-/process-0.10.1.tgz#842457cc51cfed72dc775afeeafb8c6034372725" + integrity sha512-dyIett8dgGIZ/TXKUzeYExt7WA6ldDzys9vTDU/cCA9L17Ypme+KzS+NjQCjpn9xsvi/shbMC+yP/BcFMBz0NA== + process@^0.11.1, process@^0.11.10: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" @@ -19399,23 +19151,11 @@ progress@^2.0.0, progress@^2.0.3: resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== -prom-client@12.0.0: - version "12.0.0" - resolved "https://registry.yarnpkg.com/prom-client/-/prom-client-12.0.0.tgz#9689379b19bd3f6ab88a9866124db9da3d76c6ed" - integrity sha512-JbzzHnw0VDwCvoqf8y1WDtq4wSBAbthMB1pcVI/0lzdqHGJI3KBJDXle70XK+c7Iv93Gihqo0a5LlOn+g8+DrQ== - dependencies: - tdigest "^0.1.1" - promise-all-reject-late@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz#f8ebf13483e5ca91ad809ccc2fcf25f26f8643c2" integrity sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw== -promise-breaker@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/promise-breaker/-/promise-breaker-5.0.0.tgz#58e8541f1619554057da95a211794d7834d30c1d" - integrity sha512-mgsWQuG4kJ1dtO6e/QlNDLFtMkMzzecsC69aI5hlLEjGHFNpHrvGhFi4LiK5jg2SMQj74/diH+wZliL9LpGsyA== - promise-call-limit@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/promise-call-limit/-/promise-call-limit-1.0.2.tgz#f64b8dd9ef7693c9c7613e7dfe8d6d24de3031ea" @@ -19426,11 +19166,6 @@ promise-inflight@^1.0.1: resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g== -promise-polyfill@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-6.1.0.tgz#dfa96943ea9c121fca4de9b5868cb39d3472e057" - integrity sha512-g0LWaH0gFsxovsU7R5LrrhHhWAWiHRnh1GPrhXnPgYsDkIqjRYUYSZEsej/wtleDrz5xVSIDbeKfidztp2XHFQ== - promise-retry@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" @@ -19458,18 +19193,6 @@ promise.allsettled@1.0.2: function-bind "^1.1.1" iterate-value "^1.0.0" -promise.allsettled@^1.0.2: - version "1.0.6" - resolved "https://registry.yarnpkg.com/promise.allsettled/-/promise.allsettled-1.0.6.tgz#8dc8ba8edf429feb60f8e81335b920e109c94b6e" - integrity sha512-22wJUOD3zswWFqgwjNHa1965LvqTX87WPu/lreY2KSd7SVcERfuZ4GfUaOnJNnvtoIv2yXT/W00YIGMetXtFXg== - dependencies: - array.prototype.map "^1.0.5" - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - get-intrinsic "^1.1.3" - iterate-value "^1.0.2" - promise@^8.0.0: version "8.3.0" resolved "https://registry.yarnpkg.com/promise/-/promise-8.3.0.tgz#8cb333d1edeb61ef23869fbb8a4ea0279ab60e0a" @@ -19511,15 +19234,6 @@ promzard@^0.3.0: dependencies: read "1" -proper-lockfile@^3.0.2: - version "3.2.0" - resolved "https://registry.yarnpkg.com/proper-lockfile/-/proper-lockfile-3.2.0.tgz#89ca420eea1d55d38ca552578851460067bcda66" - integrity sha512-iMghHHXv2bsxl6NchhEaFck8tvX3F9cknEEh1SUpguUOBjN7PAAW9BLzmbc1g/mCD1gY3EE2EABBHPJfFdHFmA== - dependencies: - graceful-fs "^4.1.11" - retry "^0.12.0" - signal-exit "^3.0.2" - proto-list@~1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" @@ -19532,26 +19246,7 @@ proto3-json-serializer@^0.1.8: dependencies: protobufjs "^6.11.2" -protobufjs@6.11.2: - version "6.11.2" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.2.tgz#de39fabd4ed32beaa08e9bb1e30d08544c1edf8b" - integrity sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw== - dependencies: - "@protobufjs/aspromise" "^1.1.2" - "@protobufjs/base64" "^1.1.2" - "@protobufjs/codegen" "^2.0.4" - "@protobufjs/eventemitter" "^1.1.0" - "@protobufjs/fetch" "^1.1.0" - "@protobufjs/float" "^1.0.2" - "@protobufjs/inquire" "^1.1.0" - "@protobufjs/path" "^1.1.2" - "@protobufjs/pool" "^1.1.0" - "@protobufjs/utf8" "^1.1.0" - "@types/long" "^4.0.1" - "@types/node" ">=13.7.0" - long "^4.0.0" - -protobufjs@6.11.3, protobufjs@^6.10.0, protobufjs@^6.11.2, protobufjs@^6.11.3, protobufjs@^6.8.0, protobufjs@^6.8.1, protobufjs@^6.8.6, protobufjs@^6.8.8: +protobufjs@6.11.3, protobufjs@^6.11.2, protobufjs@^6.11.3, protobufjs@^6.8.0, protobufjs@^6.8.1, protobufjs@^6.8.6, protobufjs@^6.8.8: version "6.11.3" resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.3.tgz#637a527205a35caa4f3e2a9a4a13ddffe0e7af74" integrity sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg== @@ -19598,6 +19293,24 @@ protobufjs@^7.0.0: "@types/node" ">=13.7.0" long "^5.0.0" +protobufjs@^7.2.3, protobufjs@^7.2.4: + version "7.2.5" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.5.tgz#45d5c57387a6d29a17aab6846dcc283f9b8e7f2d" + integrity sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/node" ">=13.7.0" + long "^5.0.0" + protocols@^2.0.0, protocols@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/protocols/-/protocols-2.0.1.tgz#8f155da3fc0f32644e83c5782c8e8212ccf70a86" @@ -19611,21 +19324,7 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" -proxy-agent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-5.0.0.tgz#d31405c10d6e8431fde96cba7a0c027ce01d633b" - integrity sha512-gkH7BkvLVkSfX9Dk27W6TyNOWWZWRilRfk1XxGNWOYJ2TuedAv1yFpCaU9QSBmBe716XOTNpYNOzhysyw8xn7g== - dependencies: - agent-base "^6.0.0" - debug "4" - http-proxy-agent "^4.0.0" - https-proxy-agent "^5.0.0" - lru-cache "^5.1.1" - pac-proxy-agent "^5.0.0" - proxy-from-env "^1.0.0" - socks-proxy-agent "^5.0.0" - -proxy-from-env@^1.0.0, proxy-from-env@^1.1.0: +proxy-from-env@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== @@ -19644,7 +19343,7 @@ prr@~1.0.1: resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" integrity sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw== -pseudomap@^1.0.1, pseudomap@^1.0.2: +pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== @@ -19699,15 +19398,6 @@ pumpify@^1.5.1: inherits "^2.0.3" pump "^2.0.0" -pumpify@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-2.0.1.tgz#abfc7b5a621307c728b551decbbefb51f0e4aa1e" - integrity sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw== - dependencies: - duplexify "^4.1.1" - inherits "^2.0.3" - pump "^3.0.0" - punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" @@ -19728,13 +19418,6 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== -pupa@^2.0.1, pupa@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" - integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A== - dependencies: - escape-goat "^2.0.0" - pure-rand@^5.0.1: version "5.0.5" resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-5.0.5.tgz#bda2a7f6a1fc0f284d78d78ca5902f26f2ad35cf" @@ -19794,7 +19477,7 @@ qs@6.11.0: dependencies: side-channel "^1.0.4" -qs@^6.11.0, qs@^6.4.0, qs@^6.5.2, qs@^6.6.0, qs@^6.7.0: +qs@^6.4.0, qs@^6.5.2, qs@^6.7.0: version "6.11.1" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.1.tgz#6c29dff97f0c0060765911ba65cbc9764186109f" integrity sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ== @@ -19889,7 +19572,7 @@ raw-body@2.5.1: iconv-lite "0.4.24" unpipe "1.0.0" -raw-body@2.5.2, raw-body@^2.2.0, raw-body@^2.3.3: +raw-body@2.5.2: version "2.5.2" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== @@ -19899,7 +19582,7 @@ raw-body@2.5.2, raw-body@^2.2.0, raw-body@^2.3.3: iconv-lite "0.4.24" unpipe "1.0.0" -rc@1.2.8, rc@^1.1.2, rc@^1.2.7, rc@^1.2.8: +rc@^1.1.2, rc@^1.2.7: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== @@ -19909,15 +19592,6 @@ rc@1.2.8, rc@^1.1.2, rc@^1.2.7, rc@^1.2.8: minimist "^1.2.0" strip-json-comments "~2.0.1" -re2@^1.15.8: - version "1.18.0" - resolved "https://registry.yarnpkg.com/re2/-/re2-1.18.0.tgz#6d6f47c5aaa436eb7a7d68b260f8cf25d7948619" - integrity sha512-MoCYZlJ9YUgksND9asyNF2/x532daXU/ARp1UeJbQ5flMY6ryKNEhrWt85aw3YluzOJlC3vXpGgK2a1jb0b4GA== - dependencies: - install-artifact-from-github "^1.3.1" - nan "^2.17.0" - node-gyp "^9.3.0" - react-is@^18.0.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" @@ -20006,6 +19680,16 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" +read-yaml-file@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-yaml-file/-/read-yaml-file-1.1.0.tgz#9362bbcbdc77007cc8ea4519fe1c0b821a7ce0d8" + integrity sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA== + dependencies: + graceful-fs "^4.1.5" + js-yaml "^3.6.1" + pify "^4.0.1" + strip-bom "^3.0.0" + read@1, read@1.0.x, read@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" @@ -20013,7 +19697,7 @@ read@1, read@1.0.x, read@^1.0.7: dependencies: mute-stream "~0.0.4" -readable-stream@1.1.14, readable-stream@1.1.x, readable-stream@^1.0.33: +readable-stream@1.1.14, readable-stream@^1.0.33: version "1.1.14" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" integrity sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ== @@ -20023,7 +19707,7 @@ readable-stream@1.1.14, readable-stream@1.1.x, readable-stream@^1.0.33: isarray "0.0.1" string_decoder "~0.10.x" -"readable-stream@2 || 3", readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.1, readable-stream@^3.0.2, readable-stream@^3.1.0, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: +"readable-stream@2 || 3", readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.1.0, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== @@ -20032,7 +19716,7 @@ readable-stream@1.1.14, readable-stream@1.1.x, readable-stream@^1.0.33: string_decoder "^1.1.1" util-deprecate "^1.0.1" -readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@^2.2.9, readable-stream@^2.3.0, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: +readable-stream@^2.0.0, readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@^2.2.9, readable-stream@^2.3.0, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== @@ -20060,25 +19744,6 @@ readable-stream@~1.0.15: isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@~2.0.0: - version "2.0.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" - integrity sha512-TXcFfb63BQe1+ySzsHZI/5v1aJPCShfqvWJ64ayNImXMsN1Cd0YGk/wm8KB7/OeessgPc9QvS9Zou8QTkFzsLw== - dependencies: - 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.0.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.1.3.tgz#c3d831f51f5e7bfa62fa2ffbe4b508c640f09584" - integrity sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA== - dependencies: - minimatch "^5.1.0" - readdir-scoped-modules@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" @@ -20117,13 +19782,6 @@ rechoir@^0.6.2: dependencies: resolve "^1.1.6" -rechoir@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" - integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== - dependencies: - resolve "^1.20.0" - redent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" @@ -20173,6 +19831,11 @@ regenerator-runtime@^0.13.11: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== +regenerator-runtime@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45" + integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA== + regex-cache@^0.4.2: version "0.4.4" resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" @@ -20202,25 +19865,20 @@ regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.4.3: define-properties "^1.2.0" functions-have-names "^1.2.3" +regexp.prototype.flags@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz#90ce989138db209f81492edd734183ce99f9677e" + integrity sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + set-function-name "^2.0.0" + regexpp@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== -registry-auth-token@^4.0.0: - version "4.2.2" - resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.2.tgz#f02d49c3668884612ca031419491a13539e21fac" - integrity sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg== - dependencies: - rc "1.2.8" - -registry-url@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" - integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw== - dependencies: - rc "^1.2.8" - relative@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/relative/-/relative-3.0.2.tgz#0dcd8ec54a5d35a3c15e104503d65375b5a5367f" @@ -20298,7 +19956,7 @@ request-promise@^4.2.2: stealthy-require "^1.1.1" tough-cookie "^2.3.3" -request@^2.67.0, request@^2.79.0, request@^2.85.0, request@^2.87.0, request@^2.88.0, request@^2.88.2: +request@^2.67.0, request@^2.79.0, request@^2.85.0, request@^2.88.0: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -20339,6 +19997,15 @@ require-from-string@^2.0.0, require-from-string@^2.0.1, require-from-string@^2.0 resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== +require-in-the-middle@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-7.2.0.tgz#b539de8f00955444dc8aed95e17c69b0a4f10fcf" + integrity sha512-3TLx5TGyAY6AOqLBoXmHkNql0HIf2RGbuMgCDT2WO/uGVAPJs6h7Kl+bN6TIZGd9bWhWPwnDnTHGtW8Iu77sdw== + dependencies: + debug "^4.1.1" + module-details-from-path "^1.0.3" + resolve "^1.22.1" + require-main-filename@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" @@ -20435,6 +20102,15 @@ resolve@^1.1.6, resolve@^1.10.0, resolve@^1.11.1, resolve@^1.14.2, resolve@^1.20 path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@^1.22.1: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + responselike@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" @@ -20477,7 +20153,7 @@ ret@~0.1.10: resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== -retry-request@^4.0.0, retry-request@^4.2.2: +retry-request@^4.0.0: version "4.2.2" resolved "https://registry.yarnpkg.com/retry-request/-/retry-request-4.2.2.tgz#b7d82210b6d2651ed249ba3497f07ea602f1a903" integrity sha512-xA93uxUD/rogV7BV59agW/JHPGXeREMWiZc9jhcwY4YdZ7QOtC7qbomYg0n4wyk2lJhggjvKvhNX8wln/Aldhg== @@ -20505,13 +20181,6 @@ revalidator@0.1.x: resolved "https://registry.yarnpkg.com/revalidator/-/revalidator-0.1.8.tgz#fece61bfa0c1b52a206bd6b18198184bdd523a3b" integrity sha512-xcBILK2pA9oh4SiinPEZfhP8HfrB/ha+a2fTMyl7Om2WjlDVrOQy99N2MXXlUHqGJz4qEu2duXxHJjDWuK/0xg== -rimraf@2, rimraf@^2.2.8, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3: - version "2.7.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== - dependencies: - glob "^7.1.3" - rimraf@2.6.3: version "2.6.3" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" @@ -20526,6 +20195,13 @@ rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" +rimraf@^2.2.8, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + rimraf@~2.4.0: version "2.4.5" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.4.5.tgz#ee710ce5d93a8fdb856fb5ea8ff0e2d75934b2da" @@ -20553,24 +20229,6 @@ rlp@^2.0.0, rlp@^2.2.1, rlp@^2.2.2, rlp@^2.2.3, rlp@^2.2.4: dependencies: bn.js "^5.2.0" -router@^1.3.1: - version "1.3.8" - resolved "https://registry.yarnpkg.com/router/-/router-1.3.8.tgz#1509614ae1fbc67139a728481c54b057ecfb04bf" - integrity sha512-461UFH44NtSfIlS83PUg2N7OZo86BC/kB3dY77gJdsODsBhhw7+2uE0tzTINxrY9CahCUVk1VhpWCA5i1yoIEg== - dependencies: - array-flatten "3.0.0" - debug "2.6.9" - methods "~1.1.2" - parseurl "~1.3.3" - path-to-regexp "0.1.7" - setprototypeof "1.2.0" - utils-merge "1.0.1" - -rsvp@^4.8.5: - version "4.8.5" - resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" - integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== - run-async@^2.2.0, run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" @@ -20624,16 +20282,26 @@ safe-array-concat@^1.0.0: has-symbols "^1.0.3" isarray "^2.0.5" -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +safe-array-concat@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.1.tgz#91686a63ce3adbea14d61b14c99572a8ff84754c" + integrity sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + has-symbols "^1.0.3" + isarray "^2.0.5" -safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0: +safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + safe-event-emitter@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/safe-event-emitter/-/safe-event-emitter-1.0.1.tgz#5b692ef22329ed8f69fdce607e50ca734f6f20af" @@ -20662,23 +20330,11 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" -safe-stable-stringify@^2.3.1: - version "2.4.3" - resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" - integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== - "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sanitize-filename@^1.6.1: - version "1.6.3" - resolved "https://registry.yarnpkg.com/sanitize-filename/-/sanitize-filename-1.6.3.tgz#755ebd752045931977e30b2025d340d7c9090378" - integrity sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg== - dependencies: - truncate-utf8-bytes "^1.0.0" - sax@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" @@ -20778,14 +20434,7 @@ semver-compare@^1.0.0: resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" integrity sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow== -semver-diff@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" - integrity sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg== - dependencies: - semver "^6.3.0" - -"semver@2 || 3 || 4 || 5", semver@^5.0.1, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -20828,11 +20477,18 @@ semver@7.x, semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^ dependencies: lru-cache "^6.0.0" -semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^7.5.1, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + semver@~5.4.1: version "5.4.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" @@ -20865,11 +20521,6 @@ sentence-case@^2.1.0: no-case "^2.2.0" upper-case-first "^1.1.2" -seq-queue@^0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e" - integrity sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q== - serialize-javascript@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-3.0.0.tgz#492e489a2d77b7b804ad391a5f5d97870952548e" @@ -20908,6 +20559,25 @@ set-blocking@^2.0.0, set-blocking@~2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== +set-function-length@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed" + integrity sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ== + dependencies: + define-data-property "^1.1.1" + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +set-function-name@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a" + integrity sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA== + dependencies: + define-data-property "^1.0.1" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.0" + set-immediate-shim@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" @@ -20936,7 +20606,7 @@ setimmediate@1.0.4: resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.4.tgz#20e81de622d4a02588ce0c8da8973cbcf1d3138f" integrity sha512-/TjEmXQVEzdod/FFskf3o7oOAsGhHf2j1dZqRFbDzq4F3mvvxflIIi4Hd3bLQE9y/CpwqfSQam5JakI/mi3Pog== -setimmediate@^1.0.5, setimmediate@~1.0.4: +setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== @@ -21005,11 +20675,6 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shell-quote@^1.7.2: - version "1.8.1" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" - integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== - shelljs@^0.8.4: version "0.8.5" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" @@ -21019,6 +20684,11 @@ shelljs@^0.8.4: interpret "^1.0.0" rechoir "^0.6.2" +shimmer@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" + integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== + side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" @@ -21065,13 +20735,6 @@ simple-get@^4.0.0: once "^1.3.1" simple-concat "^1.0.0" -simple-swizzle@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" - integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== - dependencies: - is-arrayish "^0.3.1" - sisteransi@^1.0.0, sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" @@ -21118,6 +20781,18 @@ smart-buffer@^4.2.0: resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== +smartwrap@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/smartwrap/-/smartwrap-2.0.2.tgz#7e25d3dd58b51c6ca4aba3a9e391650ea62698a4" + integrity sha512-vCsKNQxb7PnCNd2wY1WClWifAc2lwqsG8OaswpJkVJsvMGcnEntdTCDajZCkk93Ay1U3t/9puJmb525Rg5MZBA== + dependencies: + array.prototype.flat "^1.2.3" + breakword "^1.0.5" + grapheme-splitter "^1.0.4" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + yargs "^15.1.0" + snake-case@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-2.1.0.tgz#41bdb1b73f30ec66a04d4e2cad1b76387d4d6d9f" @@ -21160,24 +20835,6 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" -socks-proxy-agent@5, socks-proxy-agent@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz#032fb583048a29ebffec2e6a73fca0761f48177e" - integrity sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ== - dependencies: - agent-base "^6.0.2" - debug "4" - socks "^2.3.3" - -socks-proxy-agent@^6.0.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz#2687a31f9d7185e38d530bef1944fe1f1496d6ce" - integrity sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ== - dependencies: - agent-base "^6.0.2" - debug "^4.3.3" - socks "^2.6.2" - socks-proxy-agent@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz#dc069ecf34436621acb41e3efa66ca1b5fed15b6" @@ -21187,7 +20844,7 @@ socks-proxy-agent@^7.0.0: debug "^4.3.3" socks "^2.6.2" -socks@^2.3.3, socks@^2.6.2: +socks@^2.6.2: version "2.7.1" resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55" integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ== @@ -21331,7 +20988,7 @@ source-map-support@0.5.13: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@^0.5.0, source-map-support@^0.5.16, source-map-support@^0.5.17, source-map-support@^0.5.19, source-map-support@^0.5.6: +source-map-support@^0.5.0, source-map-support@^0.5.16, source-map-support@^0.5.17, source-map-support@^0.5.19: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -21349,7 +21006,7 @@ source-map@^0.5.6: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: +source-map@^0.6.0, source-map@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== @@ -21366,6 +21023,14 @@ spark-md5@3.0.2: resolved "https://registry.yarnpkg.com/spark-md5/-/spark-md5-3.0.2.tgz#7952c4a30784347abcee73268e473b9c0167e3fc" integrity sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw== +spawndamnit@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/spawndamnit/-/spawndamnit-2.0.0.tgz#9f762ac5c3476abb994b42ad592b5ad22bb4b0ad" + integrity sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA== + dependencies: + cross-spawn "^5.1.0" + signal-exit "^3.0.2" + spdx-correct@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" @@ -21434,32 +21099,11 @@ split@^1.0.0: dependencies: through "2" -sprintf-js@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673" - integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug== - sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== -sqlite3@^5.0.8: - version "5.1.6" - resolved "https://registry.yarnpkg.com/sqlite3/-/sqlite3-5.1.6.tgz#1d4fbc90fe4fbd51e952e0a90fd8f6c2b9098e97" - integrity sha512-olYkWoKFVNSSSQNvxVUfjiVbz3YtBwTJj+mfV5zpHmqW3sELx2Cf4QCdirMelhM5Zh+KDVaKgQHqCxrqiWHybw== - dependencies: - "@mapbox/node-pre-gyp" "^1.0.0" - node-addon-api "^4.2.0" - tar "^6.1.11" - optionalDependencies: - node-gyp "8.x" - -sqlstring@^2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/sqlstring/-/sqlstring-2.3.3.tgz#2ddc21f03bce2c387ed60680e739922c65751d0c" - integrity sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg== - sshpk@^1.7.0: version "1.17.0" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5" @@ -21475,13 +21119,6 @@ sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" -ssri@^8.0.0, ssri@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" - integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== - dependencies: - minipass "^3.1.1" - ssri@^9.0.0, ssri@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/ssri/-/ssri-9.0.1.tgz#544d4c357a8d7b71a19700074b6883fcb4eae057" @@ -21519,7 +21156,7 @@ statuses@2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== -"statuses@>= 1.5.0 < 2", statuses@~1.5.0: +"statuses@>= 1.5.0 < 2": version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== @@ -21553,10 +21190,12 @@ stream-shift@^1.0.0: resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== -streamsearch@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" - integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== +stream-transform@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/stream-transform/-/stream-transform-2.1.3.tgz#a1c3ecd72ddbf500aa8d342b0b9df38f5aa598e3" + integrity sha512-9GHUiM5hMiCi6Y03jD2ARC1ettBXkQBoQAe7nJsPknnI0ow10aXjTnew8QtYQmLjzn974BnmWEAJgCY6ZP1DeQ== + dependencies: + mixme "^0.5.1" strict-uri-encode@^1.0.0: version "1.1.0" @@ -21568,13 +21207,6 @@ string-hash@^1.1.3: resolved "https://registry.yarnpkg.com/string-hash/-/string-hash-1.1.3.tgz#e8aafc0ac1855b4666929ed7dd1275df5d6c811b" integrity sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A== -string-length@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/string-length/-/string-length-1.0.1.tgz#56970fb1c38558e9e70b728bf3de269ac45adfac" - integrity sha512-MNCACnufWUf3pQ57O5WTBMkKhzYIaKEcUioO0XHrTMafrbBaNk4IyDOLHBv5xbXO0jLLdsYWeFjpjG2hVHRDtw== - dependencies: - strip-ansi "^3.0.0" - string-length@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" @@ -21583,6 +21215,11 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" +string-template@~0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" + integrity sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw== + string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -21600,7 +21237,7 @@ string-width@^1.0.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -21627,6 +21264,15 @@ string.prototype.trim@^1.2.7, string.prototype.trim@~1.2.7: define-properties "^1.1.4" es-abstract "^1.20.4" +string.prototype.trim@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz#f9ac6f8af4bd55ddfa8895e6aea92a96395393bd" + integrity sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + string.prototype.trimend@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" @@ -21636,6 +21282,15 @@ string.prototype.trimend@^1.0.6: define-properties "^1.1.4" es-abstract "^1.20.4" +string.prototype.trimend@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz#1bb3afc5008661d73e2dc015cd4853732d6c471e" + integrity sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + string.prototype.trimstart@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" @@ -21645,6 +21300,15 @@ string.prototype.trimstart@^1.0.6: define-properties "^1.1.4" es-abstract "^1.20.4" +string.prototype.trimstart@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz#d4cdb44b83a4737ffbac2d406e405d43d0184298" + integrity sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -21789,62 +21453,6 @@ sublevel-pouchdb@7.3.1: ltgt "2.2.1" readable-stream "1.1.14" -superagent@^8.0.5: - version "8.0.9" - resolved "https://registry.yarnpkg.com/superagent/-/superagent-8.0.9.tgz#2c6fda6fadb40516515f93e9098c0eb1602e0535" - integrity sha512-4C7Bh5pyHTvU33KpZgwrNKh/VQnvgtCSqPRfJAUdmrtSYePVzVg4E4OzsrbkhJj9O7SO6Bnv75K/F8XVZT8YHA== - dependencies: - component-emitter "^1.3.0" - cookiejar "^2.1.4" - debug "^4.3.4" - fast-safe-stringify "^2.1.1" - form-data "^4.0.0" - formidable "^2.1.2" - methods "^1.1.2" - mime "2.6.0" - qs "^6.11.0" - semver "^7.3.8" - -superstatic@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/superstatic/-/superstatic-7.1.0.tgz#42cc773a0f500fb691841e0533d0b8c31f25997f" - integrity sha512-yBU8iw07nM3Bu4jFc8lnKwLey0cj61OaGmFJZcYC2X+kEpXVmXzERJ3OTAHZAESe1OTeNIuWadt81U5IULGGAA== - dependencies: - basic-auth-connect "^1.0.0" - chalk "^1.1.3" - compare-semver "^1.0.0" - compression "^1.7.0" - connect "^3.6.2" - destroy "^1.0.4" - fast-url-parser "^1.1.3" - fs-extra "^8.1.0" - glob-slasher "^1.0.1" - home-dir "^1.0.0" - is-url "^1.2.2" - join-path "^1.1.1" - lodash "^4.17.19" - mime-types "^2.1.16" - minimatch "^3.0.4" - morgan "^1.8.2" - nash "^3.0.0" - on-finished "^2.2.0" - on-headers "^1.0.0" - path-to-regexp "^1.8.0" - router "^1.3.1" - rsvp "^4.8.5" - string-length "^1.0.0" - update-notifier "^4.1.1" - optionalDependencies: - re2 "^1.15.8" - -supertest@^6.2.3: - version "6.3.3" - resolved "https://registry.yarnpkg.com/supertest/-/supertest-6.3.3.tgz#42f4da199fee656106fd422c094cf6c9578141db" - integrity sha512-EMCG6G8gDu5qEqRQ3JjjPs6+FYT1a7Hv5ApHvtSghmOFJYtsU5S+pSb6Y2EUeCEY3CmEL3mmQ8YWlPOzQomabA== - dependencies: - methods "^1.1.2" - superagent "^8.0.5" - supports-color@5.4.0: version "5.4.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" @@ -21873,11 +21481,6 @@ supports-color@8.1.1, supports-color@^8.0.0, supports-color@^8.1.0: dependencies: has-flag "^4.0.0" -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g== - supports-color@^3.1.0: version "3.2.3" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" @@ -21994,11 +21597,6 @@ table@^5.2.3: slice-ansi "^2.1.0" string-width "^3.0.0" -tail@^2.0.0: - version "2.2.6" - resolved "https://registry.yarnpkg.com/tail/-/tail-2.2.6.tgz#24abd701963639b896c42496d5f416216ec0b558" - integrity sha512-IQ6G4wK/t8VBauYiGPLx+d3fA5XjSVagjWV5SIYzvEvglbQjwEcukeYI68JOPpdydjxhZ9sIgzRlSmwSpphHyw== - tape@^4.4.0: version "4.16.2" resolved "https://registry.yarnpkg.com/tape/-/tape-4.16.2.tgz#7565e6af20426565557266e9dda7215869b297b6" @@ -22053,7 +21651,7 @@ tar-stream@^1.1.2, tar-stream@^1.5.2: to-buffer "^1.1.1" xtend "^4.0.0" -tar-stream@^2.1.4, tar-stream@^2.2.0, tar-stream@~2.2.0: +tar-stream@^2.1.4, tar-stream@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== @@ -22064,7 +21662,7 @@ tar-stream@^2.1.4, tar-stream@^2.2.0, tar-stream@~2.2.0: inherits "^2.0.3" readable-stream "^3.1.1" -tar@^4.0.2, tar@^4.3.0, tar@^4.4.2: +tar@^4.0.2, tar@^4.4.2: version "4.4.19" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA== @@ -22077,7 +21675,7 @@ tar@^4.0.2, tar@^4.3.0, tar@^4.4.2: safe-buffer "^5.2.1" yallist "^3.1.1" -tar@^6.0.2, tar@^6.1.0, tar@^6.1.11, tar@^6.1.2: +tar@^6.1.0, tar@^6.1.11, tar@^6.1.2: version "6.1.13" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.13.tgz#46e22529000f612180601a6fe0680e7da508847b" integrity sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw== @@ -22096,48 +21694,6 @@ targz@^1.0.1: dependencies: tar-fs "^1.8.1" -tarn@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/tarn/-/tarn-1.1.5.tgz#7be88622e951738b9fa3fb77477309242cdddc2d" - integrity sha512-PMtJ3HCLAZeedWjJPgGnCvcphbCOMbtZpjKgLq3qM5Qq9aQud+XHrL0WlrlgnTyS8U+jrjGbEXprFcQrxPy52g== - -tarn@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/tarn/-/tarn-3.0.2.tgz#73b6140fbb881b71559c4f8bfde3d9a4b3d27693" - integrity sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ== - -tcp-port-used@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/tcp-port-used/-/tcp-port-used-1.0.2.tgz#9652b7436eb1f4cfae111c79b558a25769f6faea" - integrity sha512-l7ar8lLUD3XS1V2lfoJlCBaeoaWo/2xfYt81hM7VlvR4RrMVFqfmzfhLVk40hAb368uitje5gPtBRL1m/DGvLA== - dependencies: - debug "4.3.1" - is2 "^2.0.6" - -tdigest@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/tdigest/-/tdigest-0.1.2.tgz#96c64bac4ff10746b910b0e23b515794e12faced" - integrity sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA== - dependencies: - bintrees "1.0.2" - -tedious@^6.7.1: - version "6.7.1" - resolved "https://registry.yarnpkg.com/tedious/-/tedious-6.7.1.tgz#e581be8634a5268b37dffe8930ba2d781edd8a3d" - integrity sha512-61eg/mvUa5vIqZcRizcqw/82dY65kR2uTll1TaUFh0aJ45XOrgbc8axiVR48dva8BahIAlJByaHNfAJ/KmPV0g== - dependencies: - "@azure/ms-rest-nodeauth" "^3.0.10" - "@types/node" "^12.12.17" - "@types/readable-stream" "^2.3.5" - bl "^3.0.0" - depd "^2.0.0" - iconv-lite "^0.5.0" - jsbi "^3.1.1" - native-duplexpair "^1.0.0" - punycode "^2.1.0" - readable-stream "^3.4.0" - sprintf-js "^1.1.2" - teeny-request@7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/teeny-request/-/teeny-request-7.1.1.tgz#2b0d156f4a8ad81de44303302ba8d7f1f05e20e6" @@ -22158,37 +21714,11 @@ teeny-request@^3.11.3: node-fetch "^2.2.0" uuid "^3.3.2" -teeny-request@^7.1.3: - version "7.2.0" - resolved "https://registry.yarnpkg.com/teeny-request/-/teeny-request-7.2.0.tgz#41347ece068f08d741e7b86df38a4498208b2633" - integrity sha512-SyY0pek1zWsi0LRVAALem+avzMLc33MKW/JLLakdP4s9+D7+jHcy5x6P+h94g2QNZsAqQNfX5lsbd3WSeJXrrw== - dependencies: - http-proxy-agent "^5.0.0" - https-proxy-agent "^5.0.0" - node-fetch "^2.6.1" - stream-events "^1.0.5" - uuid "^8.0.0" - -telnet-client@1.2.8: - version "1.2.8" - resolved "https://registry.yarnpkg.com/telnet-client/-/telnet-client-1.2.8.tgz#946c0dadc8daa3f19bb40a3e898cb870403a4ca4" - integrity sha512-W+w4k3QAmULVNhBVT2Fei369kGZCh/TH25M7caJAXW+hLxwoQRuw0di3cX4l0S9fgH3Mvq7u+IFMoBDpEw/eIg== - dependencies: - bluebird "^3.5.4" - temp-dir@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" integrity sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ== -tempfile@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/tempfile/-/tempfile-2.0.0.tgz#6b0446856a9b1114d1856ffcbe509cccb0977265" - integrity sha512-ZOn6nJUgvgC09+doCEF3oB+r3ag7kUvlsXEGX069QRD60p+P3uP7XG9N2/at+EyIRGSN//ZY3LyEotA1YpmjuA== - dependencies: - temp-dir "^1.0.0" - uuid "^3.0.1" - term-size@^2.1.0: version "2.2.1" resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54" @@ -22221,11 +21751,6 @@ text-extensions@^1.0.0: resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== -text-hex@1.0.x: - version "1.0.0" - resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" - integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== - text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -22262,13 +21787,14 @@ thenify-all@^1.0.0: dependencies: any-promise "^1.0.0" -through2@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.1.tgz#384e75314d49f32de12eebb8136b8eb6b5d59da9" - integrity sha512-/vp02SIbpmVHapNMjox4hDBzykPdAOmH5y3INcKaxGfpEPSCMqzdWXyGfqPYyxoBLo1JpxBrlh3Z9esv0vWUYw== +thriftrw@^3.5.0: + version "3.11.4" + resolved "https://registry.yarnpkg.com/thriftrw/-/thriftrw-3.11.4.tgz#84c990ee89e926631c0b475909ada44ee9249870" + integrity sha512-UcuBd3eanB3T10nXWRRMwfwoaC6VMk7qe3/5YIWP2Jtw+EbHqJ0p1/K3x8ixiR5dozKSSfcg1W+0e33G1Di3XA== dependencies: - readable-stream "~2.0.0" - xtend "~4.0.0" + bufrw "^1.2.1" + error "7.0.2" + long "^2.4.0" through2@3.0.2, through2@^3.0.1: version "3.0.2" @@ -22306,24 +21832,11 @@ through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@^2.3.8, resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== -tildify@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/tildify/-/tildify-2.0.0.tgz#f205f3674d677ce698b7067a99e949ce03b4754a" - integrity sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw== - timed-out@^4.0.0, timed-out@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" integrity sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA== -timers-ext@^0.1.5, timers-ext@^0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.7.tgz#6f57ad8578e07a3fb9f91d9387d65647555e25c6" - integrity sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ== - dependencies: - es5-ext "~0.10.46" - next-tick "1" - tiny-async-pool@^1.0.4: version "1.3.0" resolved "https://registry.yarnpkg.com/tiny-async-pool/-/tiny-async-pool-1.3.0.tgz#c013e1b369095e7005db5595f95e646cca6ef8a5" @@ -22343,17 +21856,6 @@ tiny-secp256k1@2.2.1: dependencies: uint8array-tools "0.0.7" -tiny-secp256k1@^1.1.3: - version "1.1.6" - resolved "https://registry.yarnpkg.com/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz#7e224d2bee8ab8283f284e40e6b4acb74ffe047c" - integrity sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA== - dependencies: - bindings "^1.3.0" - bn.js "^4.11.8" - create-hmac "^1.1.7" - elliptic "^6.4.0" - nan "^2.13.2" - tiny-typed-emitter@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz#b3b027fdd389ff81a152c8e847ee2f5be9fad7b5" @@ -22381,7 +21883,7 @@ tmp@^0.1.0: dependencies: rimraf "^2.6.3" -tmp@^0.2.1, tmp@~0.2.1: +tmp@~0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== @@ -22463,22 +21965,6 @@ tough-cookie@^2.3.3, tough-cookie@~2.5.0: universalify "^0.2.0" url-parse "^1.5.3" -tough-cookie@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.1.tgz#9df4f57e739c26930a018184887f4adb7dca73b2" - integrity sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg== - dependencies: - ip-regex "^2.1.0" - psl "^1.1.28" - punycode "^2.1.1" - -toxic@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/toxic/-/toxic-1.0.1.tgz#8c2e2528da591100adc3883f2c0e56acfb1c7288" - integrity sha512-WI3rIGdcaKULYg7KVoB0zcjikqvcYYvcuT6D89bFPz2rVR0Rl0PK6x8/X62rtdLtBKIE985NzVf/auTtGegIIg== - dependencies: - lodash "^4.17.10" - tr46@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" @@ -22491,11 +21977,6 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== -"traverse@>=0.3.0 <0.4": - version "0.3.9" - resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9" - integrity sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ== - treeify@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/treeify/-/treeify-1.1.0.tgz#4e31c6a463accd0943879f30667c4fdaff411bb8" @@ -22511,11 +21992,6 @@ trim-newlines@^3.0.0: resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== -triple-beam@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" - integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw== - truffle-flattener@^1.4.0: version "1.6.0" resolved "https://registry.yarnpkg.com/truffle-flattener/-/truffle-flattener-1.6.0.tgz#abb64488b711e6cca0a9d3e449f6a85e35964c5d" @@ -22593,13 +22069,6 @@ truffle@5.9.0: optionalDependencies: "@truffle/db" "^2.0.24" -truncate-utf8-bytes@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz#405923909592d56f78a5818434b0b78489ca5f2b" - integrity sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ== - dependencies: - utf8-byte-length "^1.0.1" - ts-essentials@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-1.0.4.tgz#ce3b5dade5f5d97cf69889c11bf7d2da8555b15a" @@ -22637,16 +22106,9 @@ ts-jest@^29.0.0: lodash.memoize "4.x" make-error "1.x" semver "7.x" - yargs-parser "^21.0.1" - -ts-mockito@^2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/ts-mockito/-/ts-mockito-2.6.1.tgz#bc9ee2619033934e6fad1c4455aca5b5ace34e73" - integrity sha512-qU9m/oEBQrKq5hwfbJ7MgmVN5Gu6lFnIGWvpxSjrqq6YYEVv+RwVFWySbZMBgazsWqv6ctAyVBpo9TmAxnOEKw== - dependencies: - lodash "^4.17.5" + yargs-parser "^21.0.1" -ts-node@8.10.2, ts-node@^8.3.0, ts-node@^8.4.1, ts-node@^8.5.4: +ts-node@8.10.2: version "8.10.2" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.10.2.tgz#eee03764633b1234ddd37f8db9ec10b75ec7fb8d" integrity sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA== @@ -22657,17 +22119,6 @@ ts-node@8.10.2, ts-node@^8.3.0, ts-node@^8.4.1, ts-node@^8.5.4: source-map-support "^0.5.17" yn "3.1.1" -ts-node@8.3.0: - version "8.3.0" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.3.0.tgz#e4059618411371924a1fb5f3b125915f324efb57" - integrity sha512-dyNS/RqyVTDcmNM4NIBAeDMpsAdaQ+ojdf0GOLqE6nwJOgzEkdRNzJywhDfwnuvB10oa6NLVG1rUJQCpRN7qoQ== - dependencies: - arg "^4.1.0" - diff "^4.0.1" - make-error "^1.1.1" - source-map-support "^0.5.6" - yn "^3.0.0" - ts-node@^10.9.1: version "10.9.1" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" @@ -22711,16 +22162,21 @@ tslib@1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.0.tgz#e37a86fda8cbbaf23a057f473c9f4dc64e5fc2e8" integrity sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ== -tslib@^1, tslib@^1.10.0, tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: +tslib@^1, tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.2.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.5.0: +tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.2.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== +tslib@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + tslib@~2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" @@ -22804,6 +22260,19 @@ tsutils@^3.0.0, tsutils@^3.9.1: dependencies: tslib "^1.8.1" +tty-table@^4.1.5: + version "4.2.2" + resolved "https://registry.yarnpkg.com/tty-table/-/tty-table-4.2.2.tgz#2a548db0278be5023ed40280001e1908bb823463" + integrity sha512-2gvCArMZLxgvpZ2NvQKdnYWIFLe7I/z5JClMuhrDXunmKgSZcQKcZRjN9XjAFiToMz2pUo1dEIXyrm0AwgV5Tw== + dependencies: + chalk "^4.1.2" + csv "^5.5.3" + kleur "^4.1.5" + smartwrap "^2.0.2" + strip-ansi "^6.0.1" + wcwidth "^1.0.1" + yargs "^17.7.1" + tunnel-agent@^0.4.0: version "0.4.3" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" @@ -22816,29 +22285,11 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" -tunnel@0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" - integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== - tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== -tweetnacl@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" - integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== - -tweetsodium@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/tweetsodium/-/tweetsodium-0.0.5.tgz#f63ab4b1d26d6355d82d512a2bbf03cae96eb3e8" - integrity sha512-T3aXZtx7KqQbutTtBfn+P5By3HdBuB1eCoGviIrRJV2sXeToxv2X2cv5RvYqgG26PSnN5m3fYixds22Gkfd11w== - dependencies: - blakejs "^1.1.0" - tweetnacl "^1.0.1" - type-check@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" @@ -22851,16 +22302,16 @@ type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5: resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== +type-fest@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" + integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== + type-fest@^0.18.0: version "0.18.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== -type-fest@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" - integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== - type-fest@^0.21.3: version "0.21.3" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" @@ -22936,6 +22387,36 @@ typechain@2.0.0: ts-essentials "^6.0.3" ts-generator "^0.0.8" +typed-array-buffer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60" + integrity sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + is-typed-array "^1.1.10" + +typed-array-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz#d787a24a995711611fb2b87a4052799517b230d0" + integrity sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz#cbbe89b51fdef9cd6aaf07ad4707340abbc4ea0b" + integrity sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + typed-array-length@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" @@ -23040,11 +22521,6 @@ typescript@4.7.4: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== -typescript@^3.6.4: - version "3.9.10" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8" - integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== - typical@^2.6.0, typical@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/typical/-/typical-2.6.1.tgz#5c080e5d661cbbe38259d2e70a3c7253e873881d" @@ -23088,17 +22564,12 @@ unc-path-regex@^0.1.0: resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" integrity sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg== -underscore@1.12.1: - version "1.12.1" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.12.1.tgz#7bb8cc9b3d397e201cf8553336d262544ead829e" - integrity sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw== - underscore@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961" integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg== -underscore@>1.4.4, "underscore@>= 1.3.1": +underscore@>1.4.4: version "1.13.6" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.6.tgz#04786a1f589dc6c09f761fc5f45b89e935136441" integrity sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A== @@ -23113,13 +22584,6 @@ union-value@^1.0.0: is-extendable "^0.1.1" set-value "^2.0.1" -unique-filename@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" - integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== - dependencies: - unique-slug "^2.0.0" - unique-filename@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-2.0.1.tgz#e785f8675a9a7589e0ac77e0b5c34d2eaeac6da2" @@ -23127,13 +22591,6 @@ unique-filename@^2.0.0: dependencies: unique-slug "^3.0.0" -unique-slug@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" - integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== - dependencies: - imurmurhash "^0.1.4" - unique-slug@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-3.0.0.tgz#6d347cf57c8a7a7a6044aabd0e2d74e4d76dc7c9" @@ -23148,22 +22605,6 @@ unique-string@^1.0.0: dependencies: crypto-random-string "^1.0.0" -unique-string@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" - integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== - dependencies: - crypto-random-string "^2.0.0" - -universal-analytics@^0.4.16: - version "0.4.23" - resolved "https://registry.yarnpkg.com/universal-analytics/-/universal-analytics-0.4.23.tgz#d915e676850c25c4156762471bdd7cf2eaaca8ac" - integrity sha512-lgMIH7XBI6OgYn1woDEmxhGdj8yDefMKg7GkWdeATAlQZFrMrNyxSkpDzY57iY0/6fdlzTbBV03OawvvzG+q7A== - dependencies: - debug "^4.1.1" - request "^2.88.2" - uuid "^3.0.0" - universal-user-agent@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" @@ -23184,11 +22625,6 @@ universalify@^2.0.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== -unorm@^1.3.3: - version "1.6.0" - resolved "https://registry.yarnpkg.com/unorm/-/unorm-1.6.0.tgz#029b289661fba714f1a9af439eb51d9b16c205af" - integrity sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA== - unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" @@ -23202,22 +22638,6 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" -unzipper@^0.10.10: - version "0.10.11" - resolved "https://registry.yarnpkg.com/unzipper/-/unzipper-0.10.11.tgz#0b4991446472cbdb92ee7403909f26c2419c782e" - integrity sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw== - dependencies: - big-integer "^1.6.17" - binary "~0.3.0" - bluebird "~3.4.1" - buffer-indexof-polyfill "~1.0.0" - duplexer2 "~0.1.4" - fstream "^1.0.12" - graceful-fs "^4.2.2" - listenercount "~1.0.1" - readable-stream "~2.3.6" - setimmediate "~1.0.4" - upath@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" @@ -23231,45 +22651,6 @@ update-browserslist-db@^1.0.10: escalade "^3.1.1" picocolors "^1.0.0" -update-notifier@^4.1.1: - version "4.1.3" - resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.3.tgz#be86ee13e8ce48fb50043ff72057b5bd598e1ea3" - integrity sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A== - dependencies: - boxen "^4.2.0" - chalk "^3.0.0" - configstore "^5.0.1" - has-yarn "^2.1.0" - import-lazy "^2.1.0" - is-ci "^2.0.0" - is-installed-globally "^0.3.1" - is-npm "^4.0.0" - is-yarn-global "^0.3.0" - latest-version "^5.0.0" - pupa "^2.0.1" - semver-diff "^3.1.1" - xdg-basedir "^4.0.0" - -update-notifier@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-5.1.0.tgz#4ab0d7c7f36a231dd7316cf7729313f0214d9ad9" - integrity sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw== - dependencies: - boxen "^5.0.0" - chalk "^4.1.0" - configstore "^5.0.1" - has-yarn "^2.1.0" - import-lazy "^2.1.0" - is-ci "^2.0.0" - is-installed-globally "^0.4.0" - is-npm "^5.0.0" - is-yarn-global "^0.3.0" - latest-version "^5.1.0" - pupa "^2.1.1" - semver "^7.3.4" - semver-diff "^3.1.1" - xdg-basedir "^4.0.0" - upper-case-first@^1.1.0, upper-case-first@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-1.1.2.tgz#5d79bedcff14419518fd2edb0a0507c9b6859115" @@ -23294,11 +22675,6 @@ urix@^0.1.0: resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg== -url-join@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/url-join/-/url-join-0.0.1.tgz#1db48ad422d3402469a87f7d97bdebfe4fb1e3c8" - integrity sha512-H6dnQ/yPAAVzMQRvEvyz01hhfQL5qRWSEt7BX8t9DqnPw9BjMb64fjIRq76Uvf1hkHp+mTZvEVJ5guXOT0Xqaw== - url-parse-lax@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" @@ -23386,11 +22762,6 @@ utf-8-validate@^5.0.2: dependencies: node-gyp-build "^4.3.0" -utf8-byte-length@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz#f45f150c4c66eee968186505ab93fcbb8ad6bf61" - integrity sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA== - utf8@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/utf8/-/utf8-2.1.1.tgz#2e01db02f7d8d0944f77104f1609eb0c304cf768" @@ -23467,16 +22838,11 @@ uuid@8.3.2, uuid@^8.0.0, uuid@^8.3.0, uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -uuid@^3.0.0, uuid@^3.0.1, uuid@^3.1.0, uuid@^3.2.1, uuid@^3.3.2, uuid@^3.3.3: +uuid@^3.2.1, uuid@^3.3.2, uuid@^3.3.3: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -uuid@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" - integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== - uuid@^9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" @@ -23501,7 +22867,7 @@ v8-to-istanbul@^9.0.1: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^1.6.0" -valid-url@^1, valid-url@^1.0.9: +valid-url@^1.0.9: version "1.0.9" resolved "https://registry.yarnpkg.com/valid-url/-/valid-url-1.0.9.tgz#1c14479b40f1397a75782f115e4086447433a200" integrity sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA== @@ -23557,13 +22923,21 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" -vm2@^3.9.11: - version "3.9.17" - resolved "https://registry.yarnpkg.com/vm2/-/vm2-3.9.17.tgz#251b165ff8a0e034942b5181057305e39570aeab" - integrity sha512-AqwtCnZ/ERcX+AVj9vUsphY56YANXxRuqMb7GsDtAr0m0PcQX3u0Aj3KWiXM0YAHy7i6JEeHrwOnwXbGYgRpAw== - dependencies: - acorn "^8.7.0" - acorn-walk "^8.2.0" +viem@~1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/viem/-/viem-1.5.4.tgz#4ba43cda4be5ec193d9f1c092955743705a37450" + integrity sha512-/B2KbAiTqiPd6fzJgz4pgS879IXbHfO44RP/0nsRvBEuFJvHQlekNIAHTa4d3LPlsHWAM8GcH4m2P5ZvtEHVxA== + dependencies: + "@adraffy/ens-normalize" "1.9.0" + "@noble/curves" "1.0.0" + "@noble/hashes" "1.3.0" + "@scure/bip32" "1.3.0" + "@scure/bip39" "1.2.0" + "@types/ws" "^8.5.4" + "@wagmi/chains" "1.6.0" + abitype "0.9.3" + isomorphic-ws "5.0.0" + ws "8.12.0" vscode-jsonrpc@^4.0.0: version "4.0.0" @@ -23649,16 +23023,6 @@ web3-bzz@1.2.2: swarm-js "0.1.39" underscore "1.9.1" -web3-bzz@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.3.6.tgz#95f370aecc3ff6ad07f057e6c0c916ef09b04dde" - integrity sha512-ibHdx1wkseujFejrtY7ZyC0QxQ4ATXjzcNUpaLrvM6AEae8prUiyT/OloG9FWDgFD2CPLwzKwfSQezYQlANNlw== - dependencies: - "@types/node" "^12.12.6" - got "9.6.0" - swarm-js "^0.1.40" - underscore "1.12.1" - web3-bzz@1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.8.1.tgz#81397be5ce262d03d82b92e9d8acc11f8a609ea1" @@ -23705,15 +23069,6 @@ web3-core-helpers@1.2.2: web3-eth-iban "1.2.2" web3-utils "1.2.2" -web3-core-helpers@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.3.6.tgz#c478246a9abe4e5456acf42657dac2f7c330be74" - integrity sha512-nhtjA2ZbkppjlxTSwG0Ttu6FcPkVu1rCN5IFAOVpF/L0SEt+jy+O5l90+cjDq0jAYvlBwUwnbh2mR9hwDEJCNA== - dependencies: - underscore "1.12.1" - web3-eth-iban "1.3.6" - web3-utils "1.3.6" - web3-core-helpers@1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.8.1.tgz#7904747b23fd0afa4f2c86ed98ea9418ccad7672" @@ -23766,18 +23121,6 @@ web3-core-method@1.2.2: web3-core-subscriptions "1.2.2" web3-utils "1.2.2" -web3-core-method@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.3.6.tgz#4b0334edd94b03dfec729d113c69a4eb6ebc68ae" - integrity sha512-RyegqVGxn0cyYW5yzAwkPlsSEynkdPiegd7RxgB4ak1eKk2Cv1q2x4C7D2sZjeeCEF+q6fOkVmo2OZNqS2iQxg== - dependencies: - "@ethersproject/transactions" "^5.0.0-beta.135" - underscore "1.12.1" - web3-core-helpers "1.3.6" - web3-core-promievent "1.3.6" - web3-core-subscriptions "1.3.6" - web3-utils "1.3.6" - web3-core-method@1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.8.1.tgz#0fc5a433a9fc784c447522f141c0a8e0163c7790" @@ -23815,13 +23158,6 @@ web3-core-promievent@1.2.2: any-promise "1.3.0" eventemitter3 "3.1.2" -web3-core-promievent@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.3.6.tgz#6c27dc79de8f71b74f5d17acaf9aaf593d3cb0c9" - integrity sha512-Z+QzfyYDTXD5wJmZO5wwnRO8bAAHEItT1XNSPVb4J1CToV/I/SbF7CuF8Uzh2jns0Cm1109o666H7StFFvzVKw== - dependencies: - eventemitter3 "4.0.4" - web3-core-promievent@1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.8.1.tgz#f334c8b2ceac6c2228f06d2a515f6d103157f036" @@ -23858,18 +23194,6 @@ web3-core-requestmanager@1.2.2: web3-providers-ipc "1.2.2" web3-providers-ws "1.2.2" -web3-core-requestmanager@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.3.6.tgz#4fea269fe913fd4fca464b4f7c65cb94857b5b2a" - integrity sha512-2rIaeuqeo7QN1Eex7aXP0ZqeteJEPWXYFS/M3r3LXMiV8R4STQBKE+//dnHJXoo2ctzEB5cgd+7NaJM8S3gPyA== - dependencies: - underscore "1.12.1" - util "^0.12.0" - web3-core-helpers "1.3.6" - web3-providers-http "1.3.6" - web3-providers-ipc "1.3.6" - web3-providers-ws "1.3.6" - web3-core-requestmanager@1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.8.1.tgz#272ffa55b7b568ecbc8e4a257ca080355c31c60e" @@ -23918,15 +23242,6 @@ web3-core-subscriptions@1.2.2: underscore "1.9.1" web3-core-helpers "1.2.2" -web3-core-subscriptions@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.3.6.tgz#ee24e7974d1d72ff6c992c599deba4ef9b308415" - integrity sha512-wi9Z9X5X75OKvxAg42GGIf81ttbNR2TxzkAsp1g+nnp5K8mBwgZvXrIsDuj7Z7gx72Y45mWJADCWjk/2vqNu8g== - dependencies: - eventemitter3 "4.0.4" - underscore "1.12.1" - web3-core-helpers "1.3.6" - web3-core-subscriptions@1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.8.1.tgz#f5ae1380e92746eadfab6475b8a70ef5a1be6bbf" @@ -23981,19 +23296,6 @@ web3-core@1.2.2: web3-core-requestmanager "1.2.2" web3-utils "1.2.2" -web3-core@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.3.6.tgz#a6a761d1ff2f3ee462b8dab679229d2f8e267504" - integrity sha512-gkLDM4T1Sc0T+HZIwxrNrwPg0IfWI0oABSglP2X5ZbBAYVUeEATA0o92LWV8BeF+okvKXLK1Fek/p6axwM/h3Q== - dependencies: - "@types/bn.js" "^4.11.5" - "@types/node" "^12.12.6" - bignumber.js "^9.0.0" - web3-core-helpers "1.3.6" - web3-core-method "1.3.6" - web3-core-requestmanager "1.3.6" - web3-utils "1.3.6" - web3-core@1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.8.1.tgz#050b1c408d1f9b7ae539e90f7f7d1b7a7d10578b" @@ -24037,15 +23339,6 @@ web3-eth-abi@1.2.2: underscore "1.9.1" web3-utils "1.2.2" -web3-eth-abi@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.3.6.tgz#4272ca48d817aa651bbf97b269f5ff10abc2b8a9" - integrity sha512-Or5cRnZu6WzgScpmbkvC6bfNxR26hqiKK4i8sMPFeTUABQcb/FU3pBj7huBLYbp9dH+P5W79D2MqwbWwjj9DoQ== - dependencies: - "@ethersproject/abi" "5.0.7" - underscore "1.12.1" - web3-utils "1.3.6" - web3-eth-abi@1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.8.1.tgz#47455d6513217c4b0866fea6f97b1c4afa0b6535" @@ -24096,23 +23389,6 @@ web3-eth-accounts@1.2.2: web3-core-method "1.2.2" web3-utils "1.2.2" -web3-eth-accounts@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.3.6.tgz#f9fcb50b28ee58090ab292a10d996155caa2b474" - integrity sha512-Ilr0hG6ONbCdSlVKffasCmNwftD5HsNpwyQASevocIQwHdTlvlwO0tb3oGYuajbKOaDzNTwXfz25bttAEoFCGA== - dependencies: - crypto-browserify "3.12.0" - eth-lib "0.2.8" - ethereumjs-common "^1.3.2" - ethereumjs-tx "^2.1.1" - scrypt-js "^3.0.1" - underscore "1.12.1" - uuid "3.3.2" - web3-core "1.3.6" - web3-core-helpers "1.3.6" - web3-core-method "1.3.6" - web3-utils "1.3.6" - web3-eth-accounts@1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.8.1.tgz#1ce7387721f118aeb0376291e4d8bbe2ac323406" @@ -24188,21 +23464,6 @@ web3-eth-contract@1.2.2: web3-eth-abi "1.2.2" web3-utils "1.2.2" -web3-eth-contract@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.3.6.tgz#cccf4d32dc56917fb6923e778498a9ba2a5ba866" - integrity sha512-8gDaRrLF2HCg+YEZN1ov0zN35vmtPnGf3h1DxmJQK5Wm2lRMLomz9rsWsuvig3UJMHqZAQKD7tOl3ocJocQsmA== - dependencies: - "@types/bn.js" "^4.11.5" - underscore "1.12.1" - web3-core "1.3.6" - web3-core-helpers "1.3.6" - web3-core-method "1.3.6" - web3-core-promievent "1.3.6" - web3-core-subscriptions "1.3.6" - web3-eth-abi "1.3.6" - web3-utils "1.3.6" - web3-eth-contract@1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.8.1.tgz#bdf3e33bbcb79a1b6144dffd6a0deefd2e459272" @@ -24259,21 +23520,6 @@ web3-eth-ens@1.2.2: web3-eth-contract "1.2.2" web3-utils "1.2.2" -web3-eth-ens@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.3.6.tgz#0d28c5d4ea7b4462ef6c077545a77956a6cdf175" - integrity sha512-n27HNj7lpSkRxTgSx+Zo7cmKAgyg2ElFilaFlUu/X2CNH23lXfcPm2bWssivH9z0ndhg0OyR4AYFZqPaqDHkJA== - dependencies: - content-hash "^2.5.2" - eth-ens-namehash "2.0.8" - underscore "1.12.1" - web3-core "1.3.6" - web3-core-helpers "1.3.6" - web3-core-promievent "1.3.6" - web3-eth-abi "1.3.6" - web3-eth-contract "1.3.6" - web3-utils "1.3.6" - web3-eth-ens@1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.8.1.tgz#e78a9651fea8282abe8565b001819e2d645e5929" @@ -24327,14 +23573,6 @@ web3-eth-iban@1.2.2: bn.js "4.11.8" web3-utils "1.2.2" -web3-eth-iban@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.3.6.tgz#0d6ba21fe78f190af8919e9cd5453882457209e0" - integrity sha512-nfMQaaLA/zsg5W4Oy/EJQbs8rSs1vBAX6b/35xzjYoutXlpHMQadujDx2RerTKhSHqFXSJeQAfE+2f6mdhYkRQ== - dependencies: - bn.js "^4.11.9" - web3-utils "1.3.6" - web3-eth-iban@1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.8.1.tgz#c6484e5d68ca644aa78431301e7acd5df24598d1" @@ -24375,18 +23613,6 @@ web3-eth-personal@1.2.2: web3-net "1.2.2" web3-utils "1.2.2" -web3-eth-personal@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.3.6.tgz#226137916754c498f0284f22c55924c87a2efcf0" - integrity sha512-pOHU0+/h1RFRYoh1ehYBehRbcKWP4OSzd4F7mDljhHngv6W8ewMHrAN8O1ol9uysN2MuCdRE19qkRg5eNgvzFQ== - dependencies: - "@types/node" "^12.12.6" - web3-core "1.3.6" - web3-core-helpers "1.3.6" - web3-core-method "1.3.6" - web3-net "1.3.6" - web3-utils "1.3.6" - web3-eth-personal@1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.8.1.tgz#00b5ff1898b62044d25ed5fddd8486168d4827cf" @@ -24448,25 +23674,6 @@ web3-eth@1.2.2: web3-net "1.2.2" web3-utils "1.2.2" -web3-eth@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.3.6.tgz#2c650893d540a7a0eb1365dd5b2dca24ac919b7c" - integrity sha512-9+rnywRRpyX3C4hfsAQXPQh6vHh9XzQkgLxo3gyeXfbhbShUoq2gFVuy42vsRs//6JlsKdyZS7Z3hHPHz2wreA== - dependencies: - underscore "1.12.1" - web3-core "1.3.6" - web3-core-helpers "1.3.6" - web3-core-method "1.3.6" - web3-core-subscriptions "1.3.6" - web3-eth-abi "1.3.6" - web3-eth-accounts "1.3.6" - web3-eth-contract "1.3.6" - web3-eth-ens "1.3.6" - web3-eth-iban "1.3.6" - web3-eth-personal "1.3.6" - web3-net "1.3.6" - web3-utils "1.3.6" - web3-eth@1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.8.1.tgz#395f6cd56edaac5dbb23e8cec9886c3fd32c430e" @@ -24534,15 +23741,6 @@ web3-net@1.2.2: web3-core-method "1.2.2" web3-utils "1.2.2" -web3-net@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.3.6.tgz#a56492e2227475e38db29394f8bac305a2446e41" - integrity sha512-KhzU3wMQY/YYjyMiQzbaLPt2kut88Ncx2iqjy3nw28vRux3gVX0WOCk9EL/KVJBiAA/fK7VklTXvgy9dZnnipw== - dependencies: - web3-core "1.3.6" - web3-core-method "1.3.6" - web3-utils "1.3.6" - web3-net@1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.8.1.tgz#2bed4d4b93166724129ec33d0e5dea98880285f4" @@ -24653,14 +23851,6 @@ web3-providers-http@1.2.2: web3-core-helpers "1.2.2" xhr2-cookies "1.1.0" -web3-providers-http@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.3.6.tgz#36e8724a7424d52827819d53fd75dbf31f5422c2" - integrity sha512-OQkT32O1A06dISIdazpGLveZcOXhEo5cEX6QyiSQkiPk/cjzDrXMw4SKZOGQbbS1+0Vjizm1Hrp7O8Vp2D1M5Q== - dependencies: - web3-core-helpers "1.3.6" - xhr2-cookies "1.1.0" - web3-providers-http@1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.8.1.tgz#8aa89c11a9272f11ddb74b871273c92225faa28d" @@ -24698,15 +23888,6 @@ web3-providers-ipc@1.2.2: underscore "1.9.1" web3-core-helpers "1.2.2" -web3-providers-ipc@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.3.6.tgz#cef8d12c1ebb47adce5ebf597f553c623362cb4a" - integrity sha512-+TVsSd2sSVvVgHG4s6FXwwYPPT91boKKcRuEFXqEfAbUC5t52XOgmyc2LNiD9LzPhed65FbV4LqICpeYGUvSwA== - dependencies: - oboe "2.1.5" - underscore "1.12.1" - web3-core-helpers "1.3.6" - web3-providers-ipc@1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.8.1.tgz#6128a3a3a824d06bf0efcfe86325401f8691a5ca" @@ -24741,16 +23922,6 @@ web3-providers-ws@1.2.2: web3-core-helpers "1.2.2" websocket "github:web3-js/WebSocket-Node#polyfill/globalThis" -web3-providers-ws@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.3.6.tgz#e1df617bc89d66165abdf2191da0014c505bfaac" - integrity sha512-bk7MnJf5or0Re2zKyhR3L3CjGululLCHXx4vlbc/drnaTARUVvi559OI5uLytc/1k5HKUUyENAxLvetz2G1dnQ== - dependencies: - eventemitter3 "4.0.4" - underscore "1.12.1" - web3-core-helpers "1.3.6" - websocket "^1.0.32" - web3-providers-ws@1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.8.1.tgz#5e5370e07eb8c615ed298ebc8602b283c7b7d649" @@ -24806,16 +23977,6 @@ web3-shh@1.2.2: web3-core-subscriptions "1.2.2" web3-net "1.2.2" -web3-shh@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.3.6.tgz#4e3486c7eca5cbdb87f88910948223a5b7ea6c20" - integrity sha512-9zRo415O0iBslxBnmu9OzYjNErzLnzOsy+IOvSpIreLYbbAw0XkDWxv3SfcpKnTIWIACBR4AYMIxmmyi5iB3jw== - dependencies: - web3-core "1.3.6" - web3-core-method "1.3.6" - web3-core-subscriptions "1.3.6" - web3-net "1.3.6" - web3-shh@1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.8.1.tgz#028a95cf9d3a36020380938b9a127610efbb9be7" @@ -24892,20 +24053,6 @@ web3-utils@1.2.2: underscore "1.9.1" utf8 "3.0.0" -web3-utils@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.3.6.tgz#390bc9fa3a7179746963cfaca55bb80ac4d8dc10" - integrity sha512-hHatFaQpkQgjGVER17gNx8u1qMyaXFZtM0y0XLGH1bzsjMPlkMPLRcYOrZ00rOPfTEuYFOdrpGOqZXVmGrMZRg== - dependencies: - bn.js "^4.11.9" - eth-lib "0.2.8" - ethereum-bloom-filters "^1.0.6" - ethjs-unit "0.1.6" - number-to-bn "1.7.0" - randombytes "^2.1.0" - underscore "1.12.1" - utf8 "3.0.0" - web3-utils@1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.8.1.tgz#f2f7ca7eb65e6feb9f3d61056d0de6bbd57125ff" @@ -24932,6 +24079,20 @@ web3-utils@1.8.2, web3-utils@^1.0.0-beta.31: randombytes "^2.1.0" utf8 "3.0.0" +web3-utils@^1.10.0: + version "1.10.3" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.10.3.tgz#f1db99c82549c7d9f8348f04ffe4e0188b449714" + integrity sha512-OqcUrEE16fDBbGoQtZXWdavsPzbGIDc5v3VrRTZ0XrIpefC/viZ1ZU9bGEemazyS0catk/3rkOOxpzTfY+XsyQ== + dependencies: + "@ethereumjs/util" "^8.1.0" + bn.js "^5.2.1" + ethereum-bloom-filters "^1.0.6" + ethereum-cryptography "^2.1.2" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + utf8 "3.0.0" + web3@*, web3@1.8.2, web3@^1.2.4: version "1.8.2" resolved "https://registry.yarnpkg.com/web3/-/web3-1.8.2.tgz#95a4e5398fd0f01325264bf8e5e8cdc69a7afe86" @@ -24972,19 +24133,6 @@ web3@1.2.2: web3-shh "1.2.2" web3-utils "1.2.2" -web3@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/web3/-/web3-1.3.6.tgz#599425461c3f9a8cbbefa70616438995f4a064cc" - integrity sha512-jEpPhnL6GDteifdVh7ulzlPrtVQeA30V9vnki9liYlUvLV82ZM7BNOQJiuzlDePuE+jZETZSP/0G/JlUVt6pOA== - dependencies: - web3-bzz "1.3.6" - web3-core "1.3.6" - web3-eth "1.3.6" - web3-eth-personal "1.3.6" - web3-net "1.3.6" - web3-shh "1.3.6" - web3-utils "1.3.6" - web3@1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/web3/-/web3-1.8.1.tgz#8ea67215ef5f3a6f6d3381800b527242ea22885a" @@ -25029,20 +24177,6 @@ webidl-conversions@^4.0.2: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== -websocket-driver@>=0.5.1: - version "0.7.4" - resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" - integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== - dependencies: - http-parser-js ">=0.5.1" - safe-buffer ">=5.1.0" - websocket-extensions ">=0.1.1" - -websocket-extensions@>=0.1.1: - version "0.1.4" - resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" - integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== - websocket@^1.0.28, websocket@^1.0.32: version "1.0.34" resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.34.tgz#2bdc2602c08bf2c82253b730655c0ef7dcab3111" @@ -25065,11 +24199,6 @@ websocket@^1.0.28, websocket@^1.0.32: typedarray-to-buffer "^3.1.5" yaeti "^0.0.6" -whatwg-fetch@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb" - integrity sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q== - whatwg-fetch@>=0.10.0, whatwg-fetch@^3.4.1: version "3.6.2" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c" @@ -25118,6 +24247,25 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== +which-pm@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-pm/-/which-pm-2.0.0.tgz#8245609ecfe64bf751d0eef2f376d83bf1ddb7ae" + integrity sha512-Lhs9Pmyph0p5n5Z3mVnN0yWcbQYUAD7rbQUiMsQxOJ3T57k7RFe35SUwWMf7dsbDZks1uOmw4AecB/JMDj3v/w== + dependencies: + load-yaml-file "^0.2.0" + path-exists "^4.0.0" + +which-typed-array@^1.1.11, which-typed-array@^1.1.13: + version "1.1.13" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.13.tgz#870cd5be06ddb616f504e7b039c4c24898184d36" + integrity sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.4" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + which-typed-array@^1.1.2, which-typed-array@^1.1.9: version "1.1.9" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" @@ -25130,7 +24278,7 @@ which-typed-array@^1.1.2, which-typed-array@^1.1.9: has-tostringtag "^1.0.0" is-typed-array "^1.1.10" -which@1.3.1, which@^1.1.1, which@^1.2.12, which@^1.2.14, which@^1.2.9, which@^1.3.1: +which@1.3.1, which@^1.1.1, which@^1.2.12, which@^1.2.14, which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -25151,7 +24299,7 @@ wide-align@1.1.3: dependencies: string-width "^1.0.2 || 2" -wide-align@^1.1.0, wide-align@^1.1.2, wide-align@^1.1.5: +wide-align@^1.1.0, wide-align@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== @@ -25189,15 +24337,6 @@ window-size@^0.2.0: resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075" integrity sha512-UD7d8HFA2+PZsbKyaOCEy8gMh1oDtHgJh1LfgjQ4zVXmYjAT/kvz3PueITKuqDiIXQe7yzpPnxX3lNc+AhQMyw== -winston-transport@^4.4.0, winston-transport@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.5.0.tgz#6e7b0dd04d393171ed5e4e4905db265f7ab384fa" - integrity sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q== - dependencies: - logform "^2.3.2" - readable-stream "^3.6.0" - triple-beam "^1.3.0" - winston@2.x: version "2.4.7" resolved "https://registry.yarnpkg.com/winston/-/winston-2.4.7.tgz#5791fe08ea7e90db090f1cb31ef98f32531062f1" @@ -25210,23 +24349,6 @@ winston@2.x: isstream "0.1.x" stack-trace "0.0.x" -winston@^3.0.0: - version "3.8.2" - resolved "https://registry.yarnpkg.com/winston/-/winston-3.8.2.tgz#56e16b34022eb4cff2638196d9646d7430fdad50" - integrity sha512-MsE1gRx1m5jdTTO9Ld/vND4krP2To+lgDoMEHGGa4HIlAUyXJtfc7CxQcGXVyz2IBpw5hbFkj2b/AtUdQwyRew== - dependencies: - "@colors/colors" "1.5.0" - "@dabh/diagnostics" "^2.0.2" - async "^3.2.3" - is-stream "^2.0.0" - logform "^2.4.0" - one-time "^1.0.0" - readable-stream "^3.4.0" - safe-stable-stringify "^2.3.1" - stack-trace "0.0.x" - triple-beam "^1.3.0" - winston-transport "^4.5.0" - word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" @@ -25374,12 +24496,17 @@ ws@7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== +ws@8.12.0: + version "8.12.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.12.0.tgz#485074cc392689da78e1828a9ff23585e06cddd8" + integrity sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig== + ws@8.2.3: version "8.2.3" resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba" integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA== -ws@^3.0.0, ws@^3.3.1: +ws@^3.0.0: version "3.3.3" resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA== @@ -25395,7 +24522,7 @@ ws@^5.1.1: dependencies: async-limiter "~1.0.0" -ws@^7.2.0, ws@^7.2.3: +ws@^7.2.0: version "7.5.9" resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== @@ -25405,11 +24532,6 @@ xdg-basedir@^3.0.0: resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4" integrity sha512-1Dly4xqlulvPD3fZUQJLY+FUIeqN3N2MM3uqe4rCJftAvOjFa3jFGfctOgluGx4ahPbUCsZkmJILiP0Vi4T6lQ== -xdg-basedir@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" - integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== - xhr-request-promise@^0.1.2: version "0.1.3" resolved "https://registry.yarnpkg.com/xhr-request-promise/-/xhr-request-promise-0.1.3.tgz#2d5f4b16d8c6c893be97f1a62b0ed4cf3ca5f96c" @@ -25452,7 +24574,7 @@ xhr@^2.0.4, xhr@^2.2.0, xhr@^2.3.3: parse-headers "^2.0.0" xtend "^4.0.0" -xml2js@0.5.0, xml2js@^0.5.0: +xml2js@0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.5.0.tgz#d9440631fbb2ed800203fad106f2724f62c493b7" integrity sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA== @@ -25475,15 +24597,10 @@ xmlhttprequest@*, xmlhttprequest@1.8.0: resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" integrity sha512-58Im/U0mlVBLM38NdZjHyhuMtCqa61469k2YP/AaPbvCoV9aQGUpbJBj1QRm2ytRiVQBD/fsw7L2bJGDVQswBA== -xpath.js@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/xpath.js/-/xpath.js-1.1.0.tgz#3816a44ed4bb352091083d002a383dd5104a5ff1" - integrity sha512-jg+qkfS4K8E7965sqaUl8mRngXiKb3WZGfONgE18pr03FUQiuSV6G+Ej4tS55B+rIQSFEIw3phdVAQ4pPqNWfQ== - -xregexp@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" - integrity sha512-xl/50/Cf32VsGq/1R8jJE5ajH1yMCQkpmoS10QbFZWl2Oor4H0Me64Pu2yxvsRWK3m6soJbmGfzSR7BYmDcWAA== +xorshift@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/xorshift/-/xorshift-1.2.0.tgz#30a4cdd8e9f8d09d959ed2a88c42a09c660e8148" + integrity sha512-iYgNnGyeeJ4t6U11NpA/QiKy+PXn5Aa3Azg5qkwIFz1tBLllQrjjsk9yzD7IAK0naNU4JxdeDgqW9ov4u/hc4g== xss@^1.0.8: version "1.0.14" @@ -25525,7 +24642,7 @@ yaeti@^0.0.6: resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" integrity sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug== -yallist@^2.0.0, yallist@^2.1.2: +yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A== @@ -25545,7 +24662,7 @@ yaml@^1.10.0, yaml@^1.10.2: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yargs-parser@13.1.2, yargs-parser@^13.1.1, yargs-parser@^13.1.2: +yargs-parser@13.1.2, yargs-parser@^13.1.2: version "13.1.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== @@ -25571,6 +24688,14 @@ yargs-parser@^15.0.1: camelcase "^5.0.0" decamelize "^1.2.0" +yargs-parser@^18.1.2, yargs-parser@^18.1.3: + version "18.1.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + yargs-parser@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-2.4.1.tgz#85568de3cf150ff49fa51825f03a8c880ddcc5c4" @@ -25600,7 +24725,7 @@ yargs-unparser@1.6.0: lodash "^4.17.15" yargs "^13.3.0" -yargs-unparser@2.0.0, yargs-unparser@^2.0.0: +yargs-unparser@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== @@ -25626,24 +24751,7 @@ yargs@13.3.2, yargs@^13.2.4, yargs@^13.3.0: y18n "^4.0.0" yargs-parser "^13.1.2" -yargs@14.0.0: - version "14.0.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-14.0.0.tgz#ba4cacc802b3c0b3e36a9e791723763d57a85066" - integrity sha512-ssa5JuRjMeZEUjg7bEL99AwpitxU/zWGAGpdj0di41pOEmJti8NR6kyUIJBkR78DTYNPZOU08luUo0GTHuB+ow== - dependencies: - cliui "^5.0.0" - decamelize "^1.2.0" - find-up "^3.0.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^3.0.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^13.1.1" - -yargs@16.2.0, yargs@^16.0.3, yargs@^16.2.0: +yargs@16.2.0, yargs@^16.2.0: version "16.2.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== @@ -25656,6 +24764,19 @@ yargs@16.2.0, yargs@^16.0.3, yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" +yargs@17.7.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== + 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@^11.0.0: version "11.1.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.1.tgz#5052efe3446a4df5ed669c995886cc0f13702766" @@ -25691,6 +24812,23 @@ yargs@^14.0.0: y18n "^4.0.0" yargs-parser "^15.0.1" +yargs@^15.1.0: + version "15.4.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.2" + yargs@^17.3.1, yargs@^17.5.1, yargs@^17.6.2: version "17.7.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.1.tgz#34a77645201d1a8fc5213ace787c220eabbd0967" @@ -25750,7 +24888,7 @@ yauzl@^2.4.2: buffer-crc32 "~0.2.3" fd-slicer "~1.1.0" -yn@3.1.1, yn@^3.0.0: +yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== @@ -25759,12 +24897,3 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== - -zip-stream@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-4.1.0.tgz#51dd326571544e36aa3f756430b313576dc8fc79" - integrity sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A== - dependencies: - archiver-utils "^2.1.0" - compress-commons "^4.1.0" - readable-stream "^3.6.0"