From 6a68d2ed323692c54b63eb3816f44d6c7eb87cd6 Mon Sep 17 00:00:00 2001 From: Blair Vanderlugt Date: Sat, 28 Aug 2021 16:26:16 +0000 Subject: [PATCH 1/5] docker vm builds --- .gitlab-ci.yml | 30 +++++++++-- apps/cic-base-os/Dockerfile | 34 ------------ apps/cic-base-os/README.md | 1 - apps/cic-cache/.gitlab-ci.yml | 69 ++++++------------------- apps/cic-cache/README.md | 1 + apps/cic-cache/docker/Dockerfile_ci | 38 -------------- apps/cic-cache/docker/run_tests.sh | 10 ++++ apps/cic-eth/.gitlab-ci.yml | 68 ++++++------------------ apps/cic-eth/docker/Dockerfile_ci | 71 -------------------------- apps/cic-eth/docker/run_tests.sh | 11 ++++ apps/cic-eth/tests/run_tests.sh | 10 ++++ apps/cic-meta/.gitlab-ci.yml | 59 ++++++--------------- apps/cic-meta/docker/Dockerfile | 5 +- apps/cic-meta/docker/Dockerfile_ci | 32 ------------ apps/cic-meta/docker/run_tests.sh | 7 +++ apps/cic-notify/.gitlab-ci.yml | 69 ++++++------------------- apps/cic-notify/docker/Dockerfile_ci | 27 ---------- apps/cic-notify/docker/run_tests.sh | 9 ++++ apps/cic-ussd/.gitlab-ci.yml | 68 ++++++------------------ apps/cic-ussd/docker/Dockerfile_ci | 32 ------------ apps/cic-ussd/docker/run_tests.sh | 10 ++++ apps/contract-migration/.gitlab-ci.yml | 50 +++++++++--------- docker-compose.yml | 27 +++++----- scripts/build-push.sh | 9 ++++ scripts/build.sh | 9 ++++ scripts/test-local.sh | 15 ++++++ 26 files changed, 240 insertions(+), 531 deletions(-) delete mode 100644 apps/cic-base-os/Dockerfile delete mode 100644 apps/cic-base-os/README.md delete mode 100644 apps/cic-cache/docker/Dockerfile_ci create mode 100644 apps/cic-cache/docker/run_tests.sh delete mode 100644 apps/cic-eth/docker/Dockerfile_ci create mode 100644 apps/cic-eth/docker/run_tests.sh create mode 100644 apps/cic-eth/tests/run_tests.sh delete mode 100644 apps/cic-meta/docker/Dockerfile_ci create mode 100644 apps/cic-meta/docker/run_tests.sh delete mode 100644 apps/cic-notify/docker/Dockerfile_ci create mode 100644 apps/cic-notify/docker/run_tests.sh delete mode 100644 apps/cic-ussd/docker/Dockerfile_ci create mode 100644 apps/cic-ussd/docker/run_tests.sh create mode 100755 scripts/build-push.sh create mode 100755 scripts/build.sh create mode 100755 scripts/test-local.sh diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 52f8ffd6..6ddc1394 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,14 +1,36 @@ include: - - local: 'ci_templates/.cic-template.yml' - - local: 'apps/contract-migration/.gitlab-ci.yml' + #- local: 'ci_templates/.cic-template.yml' #kaniko build templates + # these includes are app specific unit tests - local: 'apps/cic-eth/.gitlab-ci.yml' - local: 'apps/cic-ussd/.gitlab-ci.yml' - local: 'apps/cic-notify/.gitlab-ci.yml' - local: 'apps/cic-meta/.gitlab-ci.yml' - local: 'apps/cic-cache/.gitlab-ci.yml' - - local: 'apps/data-seeding/.gitlab-ci.yml' + #- local: 'apps/contract-migration/.gitlab-ci.yml' + #- local: 'apps/data-seeding/.gitlab-ci.yml' stages: - build - test - - release + - deploy + +image: registry.gitlab.com/grassrootseconomics/cic-internal-integration/docker-with-compose:latest + +variables: + DOCKER_BUILDKIT: "1" + COMPOSE_DOCKER_CLI_BUILD: "1" + CI_DEBUG_TRACE: "true" + +before_script: + - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY + +# runs on protected branches and pushes to repo +build-push: + stage: build + tags: + - integration + script: + - TAG=$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA sh ./scripts/build-push.sh + rules: + - if: $CI_COMMIT_REF_PROTECTED == "true" + when: always diff --git a/apps/cic-base-os/Dockerfile b/apps/cic-base-os/Dockerfile deleted file mode 100644 index 2af84ab5..00000000 --- a/apps/cic-base-os/Dockerfile +++ /dev/null @@ -1,34 +0,0 @@ -# The solc image messes up the alpine environment, so we have to go all over again -FROM python:3.8.6-slim-buster - -LABEL authors="Louis Holbrook 0826EDA1702D1E87C6E2875121D2E7BB88C2A746" -LABEL spdx-license-identifier="GPL-3.0-or-later" -LABEL description="Base layer for buiding development images for the cic component suite" - -RUN apt-get update && \ - apt-get install -y git gcc g++ libpq-dev && \ - apt-get install -y vim gawk jq telnet openssl iputils-ping curl wget gnupg socat bash procps make python2 postgresql-client - - -RUN echo installing nodejs tooling - -COPY ./dev/nvm.sh /root/ - -# Install nvm with node and npm -# https://stackoverflow.com/questions/25899912/how-to-install-nvm-in-docker -ENV NVM_DIR /root/.nvm -ENV NODE_VERSION 15.3.0 -ENV BANCOR_NODE_VERSION 10.16.0 - -RUN wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh | bash \ - && . $NVM_DIR/nvm.sh \ - && nvm install $NODE_VERSION \ - && nvm alias default $NODE_VERSION \ - && nvm use $NODE_VERSION \ -# So many ridiculously stupid issues with node in docker that take oceans of absolutely wasted time to resolve -# owner of these files is "1001" by default - wtf - && chown -R root:root "$NVM_DIR/versions/node/v$NODE_VERSION" - -ENV NODE_PATH $NVM_DIR/versions/node//v$NODE_VERSION/lib/node_modules -ENV PATH $NVM_DIR/versions/node//v$NODE_VERSION/bin:$PATH - diff --git a/apps/cic-base-os/README.md b/apps/cic-base-os/README.md deleted file mode 100644 index 6551bf40..00000000 --- a/apps/cic-base-os/README.md +++ /dev/null @@ -1 +0,0 @@ -## this is an example base image if we wanted one for all the other apps. Its just OS level things \ No newline at end of file diff --git a/apps/cic-cache/.gitlab-ci.yml b/apps/cic-cache/.gitlab-ci.yml index ea7ec6c0..7a120987 100644 --- a/apps/cic-cache/.gitlab-ci.yml +++ b/apps/cic-cache/.gitlab-ci.yml @@ -1,52 +1,17 @@ -.cic_cache_variables: - variables: - APP_NAME: cic-cache - DOCKERFILE_PATH: docker/Dockerfile_ci - CONTEXT: apps/$APP_NAME - -build-mr-cic-cache: - extends: - - .py_build_merge_request - - .cic_cache_variables - rules: - - if: $CI_PIPELINE_SOURCE == "merge_request_event" - changes: - - apps/cic-cache/**/* - when: always - -test-mr-cic-cache: - stage: test - extends: - - .cic_cache_variables - cache: - key: - files: - - test_requirements.txt - paths: - - /root/.cache/pip - image: $MR_IMAGE_TAG - script: - - cd apps/$APP_NAME/ - - > - pip install --extra-index-url https://pip.grassrootseconomics.net:8433 - --extra-index-url https://gitlab.com/api/v4/projects/27624814/packages/pypi/simple - -r test_requirements.txt - - export PYTHONPATH=. && pytest -x --cov=cic_cache --cov-fail-under=90 --cov-report term-missing tests - needs: ["build-mr-cic-cache"] - rules: - - if: $CI_PIPELINE_SOURCE == "merge_request_event" - changes: - - apps/$APP_NAME/**/* - when: always - -build-push-cic-cache: - extends: - - .py_build_push - - .cic_cache_variables - rules: - - if: $CI_COMMIT_BRANCH == "master" - changes: - - apps/cic-cache/**/* - when: always - - +build-test-cic-cache: + stage: test + tags: + - integration + variables: + APP_NAME: cic-cache + MR_IMAGE_TAG: mr-$APP_NAME-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA + script: + - cd apps/cic-cache + - docker build -t $MR_IMAGE_TAG -f docker/Dockerfile . + - docker run $MR_IMAGE_TAG sh docker/run_tests.sh + allow_failure: true + rules: + - if: $CI_PIPELINE_SOURCE == "merge_request_event" + changes: + - apps/$APP_NAME/**/* + when: always diff --git a/apps/cic-cache/README.md b/apps/cic-cache/README.md index e69de29b..3938e1af 100644 --- a/apps/cic-cache/README.md +++ b/apps/cic-cache/README.md @@ -0,0 +1 @@ +# CIC-CACHE diff --git a/apps/cic-cache/docker/Dockerfile_ci b/apps/cic-cache/docker/Dockerfile_ci deleted file mode 100644 index 67ab3c71..00000000 --- a/apps/cic-cache/docker/Dockerfile_ci +++ /dev/null @@ -1,38 +0,0 @@ -# syntax = docker/dockerfile:1.2 -FROM registry.gitlab.com/grassrootseconomics/cic-base-images:python-3.8.6-dev-55da5f4e as dev - -# RUN pip install $pip_extra_index_url_flag cic-base[full_graph]==0.1.2b9 - -COPY requirements.txt . -#RUN pip install $pip_extra_index_url_flag -r test_requirements.txt -#RUN pip install $pip_extra_index_url_flag . -#RUN pip install .[server] - -ARG EXTRA_INDEX_URL="https://pip.grassrootseconomics.net:8433" -ARG GITLAB_PYTHON_REGISTRY="https://gitlab.com/api/v4/projects/27624814/packages/pypi/simple" -ARG EXTRA_PIP_ARGS="" -RUN pip install --index-url https://pypi.org/simple \ - --extra-index-url $GITLAB_PYTHON_REGISTRY --extra-index-url $EXTRA_INDEX_URL $EXTRA_PIP_ARGS \ - -r requirements.txt - -COPY . . - -RUN python setup.py install - -# ini files in config directory defines the configurable parameters for the application -# they can all be overridden by environment variables -# to generate a list of environment variables from configuration, use: confini-dump -z (executable provided by confini package) -COPY config/ /usr/local/etc/cic-cache/ - -# for db migrations -RUN git clone https://github.com/vishnubob/wait-for-it.git /usr/local/bin/wait-for-it/ -COPY cic_cache/db/migrations/ /usr/local/share/cic-cache/alembic/ - -COPY /docker/start_tracker.sh ./start_tracker.sh -COPY /docker/db.sh ./db.sh -RUN chmod 755 ./*.sh -# Tracker -# ENTRYPOINT ["/usr/local/bin/cic-cache-tracker", "-vv"] -# Server -# ENTRYPOINT [ "/usr/local/bin/uwsgi", "--wsgi-file", "/usr/local/lib/python3.8/site-packages/cic_cache/runnable/server.py", "--http", ":80", "--pyargv", "-vv" ] -ENTRYPOINT [] diff --git a/apps/cic-cache/docker/run_tests.sh b/apps/cic-cache/docker/run_tests.sh new file mode 100644 index 00000000..62c1db72 --- /dev/null +++ b/apps/cic-cache/docker/run_tests.sh @@ -0,0 +1,10 @@ +#! /bin/bash + +set -e + +pip install --extra-index-url https://pip.grassrootseconomics.net:8433 \ +--extra-index-url https://gitlab.com/api/v4/projects/27624814/packages/pypi/simple \ +-r test_requirements.txt + +export PYTHONPATH=. && pytest -x --cov=cic_cache --cov-fail-under=90 --cov-report term-missing tests + diff --git a/apps/cic-eth/.gitlab-ci.yml b/apps/cic-eth/.gitlab-ci.yml index ef2b7462..2fcfde1e 100644 --- a/apps/cic-eth/.gitlab-ci.yml +++ b/apps/cic-eth/.gitlab-ci.yml @@ -1,52 +1,16 @@ -.cic_eth_variables: - variables: - APP_NAME: cic-eth - DOCKERFILE_PATH: docker/Dockerfile_ci - CONTEXT: apps/$APP_NAME - -build-mr-cic-eth: - extends: - - .cic_eth_variables - - .py_build_target_dev - rules: - - if: $CI_PIPELINE_SOURCE == "merge_request_event" - changes: - - apps/cic-eth/**/* - when: always - -test-mr-cic-eth: - stage: test - extends: - - .cic_eth_variables - cache: - key: - files: - - test_requirements.txt - paths: - - /root/.cache/pip - image: $MR_IMAGE_TAG - script: - - cd apps/$APP_NAME/ - - > - pip install --extra-index-url https://pip.grassrootseconomics.net:8433 - --extra-index-url https://gitlab.com/api/v4/projects/27624814/packages/pypi/simple - -r admin_requirements.txt - -r services_requirements.txt - -r test_requirements.txt - - export PYTHONPATH=. && pytest -x --cov=cic_eth --cov-fail-under=90 --cov-report term-missing tests - needs: ["build-mr-cic-eth"] - rules: - - if: $CI_PIPELINE_SOURCE == "merge_request_event" - changes: - - apps/cic-eth/**/* - when: always - -build-push-cic-eth: - extends: - - .py_build_push - - .cic_eth_variables - rules: - - if: $CI_COMMIT_BRANCH == "master" - changes: - - apps/cic-eth/**/* - when: always +build-test-cic-eth: + stage: test + tags: + - integration + variables: + APP_NAME: cic-eth + MR_IMAGE_TAG: mr-$APP_NAME-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA + script: + - cd apps/cic-eth + - docker build -t $MR_IMAGE_TAG -f docker/Dockerfile . + - docker run $MR_IMAGE_TAG sh docker/run_tests.sh + #rules: + #- if: $CI_PIPELINE_SOURCE == "merge_request_event" + # changes: + # - apps/$APP_NAME/**/* + # when: always diff --git a/apps/cic-eth/docker/Dockerfile_ci b/apps/cic-eth/docker/Dockerfile_ci deleted file mode 100644 index e7bcc831..00000000 --- a/apps/cic-eth/docker/Dockerfile_ci +++ /dev/null @@ -1,71 +0,0 @@ -FROM registry.gitlab.com/grassrootseconomics/cic-base-images:python-3.8.6-dev-55da5f4e as dev - -WORKDIR /usr/src/cic-eth - -# Copy just the requirements and install....this _might_ give docker a hint on caching but we -# do load these all into setup.py later -# TODO can we take all the requirements out of setup.py and just do a pip install -r requirements.txt && python setup.py -#COPY cic-eth/requirements.txt . - -ARG EXTRA_INDEX_URL="https://pip.grassrootseconomics.net:8433" -ARG GITLAB_PYTHON_REGISTRY="https://gitlab.com/api/v4/projects/27624814/packages/pypi/simple" -ARG EXTRA_PIP_ARGS="" -#RUN --mount=type=cache,mode=0755,target=/root/.cache/pip \ -# pip install --index-url https://pypi.org/simple \ -# --force-reinstall \ -# --extra-index-url $GITLAB_PYTHON_REGISTRY --extra-index-url $EXTRA_INDEX_URL \ -# -r requirements.txt -COPY *requirements.txt . -RUN pip install --index-url https://pypi.org/simple \ - --extra-index-url $GITLAB_PYTHON_REGISTRY \ - --extra-index-url $EXTRA_INDEX_URL \ - $EXTRA_PIP_ARGS \ - -r requirements.txt \ - -r services_requirements.txt \ - -r admin_requirements.txt - -COPY . . -RUN python setup.py install - -COPY docker/entrypoints/* ./ -RUN chmod 755 *.sh - -# # ini files in config directory defines the configurable parameters for the application -# # they can all be overridden by environment variables -# # to generate a list of environment variables from configuration, use: confini-dump -z (executable provided by confini package) -COPY config/ /usr/local/etc/cic-eth/ -COPY cic_eth/db/migrations/ /usr/local/share/cic-eth/alembic/ -COPY crypto_dev_signer_config/ /usr/local/etc/crypto-dev-signer/ - -# TODO this kind of code sharing across projects should be discouraged...can we make util a library? -#COPY util/liveness/health.sh /usr/local/bin/health.sh -ENTRYPOINT [] - -# ------------------ PRODUCTION CONTAINER ---------------------- -#FROM python:3.8.6-slim-buster as prod -# -#RUN apt-get update && \ -# apt install -y gnupg libpq-dev procps -# -#WORKDIR /root -# -#COPY --from=dev /usr/local/bin/ /usr/local/bin/ -#COPY --from=dev /usr/local/lib/python3.8/site-packages/ \ -# /usr/local/lib/python3.8/site-packages/ -# -#COPY docker/entrypoints/* ./ -#RUN chmod 755 *.sh -# -## # ini files in config directory defines the configurable parameters for the application -## # they can all be overridden by environment variables -## # to generate a list of environment variables from configuration, use: confini-dump -z (executable provided by confini package) -#COPY config/ /usr/local/etc/cic-eth/ -#COPY cic_eth/db/migrations/ /usr/local/share/cic-eth/alembic/ -#COPY crypto_dev_signer_config/ /usr/local/etc/crypto-dev-signer/ -#COPY scripts/ scripts/ -# -## TODO this kind of code sharing across projects should be discouraged...can we make util a library? -##COPY util/liveness/health.sh /usr/local/bin/health.sh -# -#ENTRYPOINT [] -# diff --git a/apps/cic-eth/docker/run_tests.sh b/apps/cic-eth/docker/run_tests.sh new file mode 100644 index 00000000..ef434d5c --- /dev/null +++ b/apps/cic-eth/docker/run_tests.sh @@ -0,0 +1,11 @@ +#! /bin/bash + +set -e + +pip install --extra-index-url https://pip.grassrootseconomics.net:8433 --extra-index-url https://gitlab.com/api/v4/projects/27624814/packages/pypi/simple \ +-r admin_requirements.txt \ +-r services_requirements.txt \ +-r test_requirements.txt + +export PYTHONPATH=. && pytest -x --cov=cic_eth --cov-fail-under=90 --cov-report term-missing tests + diff --git a/apps/cic-eth/tests/run_tests.sh b/apps/cic-eth/tests/run_tests.sh new file mode 100644 index 00000000..30500432 --- /dev/null +++ b/apps/cic-eth/tests/run_tests.sh @@ -0,0 +1,10 @@ +#! /bin/bash + +set -e + +pip install --extra-index-url https://pip.grassrootseconomics.net:8433 --extra-index-url https://gitlab.com/api/v4/projects/27624814/packages/pypi/simple +-r admin_requirements.txt +-r services_requirements.txt +-r test_requirements.txt + +export PYTHONPATH=. && pytest -x --cov=cic_eth --cov-fail-under=90 --cov-report term-missing tests diff --git a/apps/cic-meta/.gitlab-ci.yml b/apps/cic-meta/.gitlab-ci.yml index 8ab7177d..c77d18d7 100644 --- a/apps/cic-meta/.gitlab-ci.yml +++ b/apps/cic-meta/.gitlab-ci.yml @@ -1,43 +1,16 @@ - -.cic_meta_variables: - variables: - APP_NAME: cic-meta - DOCKERFILE_PATH: docker/Dockerfile_ci - CONTEXT: apps/$APP_NAME - -build-mr-cic-meta: - extends: - - .py_build_merge_request - - .cic_meta_variables - rules: - - if: $CI_PIPELINE_SOURCE == "merge_request_event" - changes: - - apps/cic-meta/**/* - when: always - -test-mr-cic-meta: - extends: - - .cic_meta_variables - stage: test - image: $MR_IMAGE_TAG - script: - - cd /root - - npm install --dev - - npm run test - - npm run test:coverage - needs: ["build-mr-cic-meta"] - rules: - - if: $CI_PIPELINE_SOURCE == "merge_request_event" - changes: - - apps/cic-meta/**/* - when: always - -build-push-cic-meta: - extends: - - .py_build_push - - .cic_meta_variables - rules: - - if: $CI_COMMIT_BRANCH == "master" - changes: - - apps/cic-meta/**/* - when: always +build-test-cic-meta: + stage: test + tags: + - integration + variables: + APP_NAME: cic-meta + MR_IMAGE_TAG: mr-$APP_NAME-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA + script: + - cd apps/cic-meta + - docker build -t $MR_IMAGE_TAG -f docker/Dockerfile . + - docker run --entrypoint=sh $MR_IMAGE_TAG docker/run_tests.sh + #rules: + #- if: $CI_PIPELINE_SOURCE == "merge_request_event" + # changes: + # - apps/$APP_NAME/**/* + # when: always diff --git a/apps/cic-meta/docker/Dockerfile b/apps/cic-meta/docker/Dockerfile index 11eb5d9a..aa4c6a28 100644 --- a/apps/cic-meta/docker/Dockerfile +++ b/apps/cic-meta/docker/Dockerfile @@ -15,11 +15,10 @@ RUN --mount=type=cache,mode=0755,target=/root/.npm \ COPY webpack.config.js . COPY tsconfig.json . ## required to build the cic-client-meta module -COPY src/ src/ -COPY scripts/ scripts/ -COPY tests/ tests/ +COPY . . COPY tests/*.asc /root/pgp/ + ## copy runtime configs COPY .config/ /usr/local/etc/cic-meta/ # diff --git a/apps/cic-meta/docker/Dockerfile_ci b/apps/cic-meta/docker/Dockerfile_ci deleted file mode 100644 index 9215ab71..00000000 --- a/apps/cic-meta/docker/Dockerfile_ci +++ /dev/null @@ -1,32 +0,0 @@ -# syntax = docker/dockerfile:1.2 -#FROM node:15.3.0-alpine3.10 -FROM node:lts-alpine3.14 - -WORKDIR /root - -RUN apk add --no-cache postgresql bash - -# copy the dependencies -COPY package.json package-lock.json . -RUN npm set cache /root/.npm && \ - npm ci - -COPY webpack.config.js . -COPY tsconfig.json . -## required to build the cic-client-meta module -COPY src/ src/ -COPY scripts/ scripts/ -COPY tests/ tests/ -COPY tests/*.asc /root/pgp/ - -## copy runtime configs -COPY .config/ /usr/local/etc/cic-meta/ -# -## db migrations -COPY docker/db.sh ./db.sh -RUN chmod 755 ./db.sh -# -RUN alias tsc=node_modules/typescript/bin/tsc -COPY docker/start_server.sh ./start_server.sh -RUN chmod 755 ./start_server.sh -ENTRYPOINT ["sh", "./start_server.sh"] diff --git a/apps/cic-meta/docker/run_tests.sh b/apps/cic-meta/docker/run_tests.sh new file mode 100644 index 00000000..cf3965f2 --- /dev/null +++ b/apps/cic-meta/docker/run_tests.sh @@ -0,0 +1,7 @@ +#! /bin/bash + +set -e + +npm install --dev +npm run test +npm run test:coverage diff --git a/apps/cic-notify/.gitlab-ci.yml b/apps/cic-notify/.gitlab-ci.yml index 1a4ef23e..e0c9597f 100644 --- a/apps/cic-notify/.gitlab-ci.yml +++ b/apps/cic-notify/.gitlab-ci.yml @@ -1,52 +1,17 @@ -.cic_notify_variables: - variables: - APP_NAME: cic-notify - DOCKERFILE_PATH: docker/Dockerfile_ci - CONTEXT: apps/$APP_NAME - -build-mr-cic-notify: - extends: - - .py_build_merge_request - - .cic_notify_variables - rules: - - if: $CI_PIPELINE_SOURCE == "merge_request_event" - changes: - - apps/cic-notify/**/* - when: always - -test-mr-cic-notify: - stage: test - extends: - - .cic_notify_variables - cache: - key: - files: - - test_requirements.txt - paths: - - /root/.cache/pip - image: $MR_IMAGE_TAG - script: - - cd apps/$APP_NAME/ - - > - pip install --extra-index-url https://pip.grassrootseconomics.net:8433 - --extra-index-url https://gitlab.com/api/v4/projects/27624814/packages/pypi/simple - -r test_requirements.txt - - export PYTHONPATH=. && pytest -x --cov=cic_notify --cov-fail-under=90 --cov-report term-missing tests - needs: ["build-mr-cic-notify"] - rules: - - if: $CI_PIPELINE_SOURCE == "merge_request_event" - changes: - - apps/$APP_NAME/**/* - when: always - -build-push-cic-notify: - extends: - - .py_build_push - - .cic_notify_variables - rules: - - if: $CI_COMMIT_BRANCH == "master" - changes: - - apps/cic-notify/**/* - when: always - - +build-test-cic-notify: + stage: test + tags: + - integration + variables: + APP_NAME: cic-notify + MR_IMAGE_TAG: mr-$APP_NAME-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA + script: + - cd apps/cic-notify + - docker build -t $MR_IMAGE_TAG -f docker/Dockerfile . + - docker run $MR_IMAGE_TAG sh docker/run_tests.sh + allow_failure: true + rules: + - if: $CI_PIPELINE_SOURCE == "merge_request_event" + changes: + - apps/$APP_NAME/**/* + when: always diff --git a/apps/cic-notify/docker/Dockerfile_ci b/apps/cic-notify/docker/Dockerfile_ci deleted file mode 100644 index 3db71bc9..00000000 --- a/apps/cic-notify/docker/Dockerfile_ci +++ /dev/null @@ -1,27 +0,0 @@ -# syntax = docker/dockerfile:1.2 -FROM registry.gitlab.com/grassrootseconomics/cic-base-images:python-3.8.6-dev-55da5f4e as dev - -#RUN pip install $pip_extra_index_url_flag cic-base[full_graph]==0.1.2a62 - -ARG EXTRA_INDEX_URL="https://pip.grassrootseconomics.net:8433" -ARG GITLAB_PYTHON_REGISTRY="https://gitlab.com/api/v4/projects/27624814/packages/pypi/simple" -COPY requirements.txt . - -RUN pip install --index-url https://pypi.org/simple \ - --extra-index-url $GITLAB_PYTHON_REGISTRY --extra-index-url $EXTRA_INDEX_URL \ - -r requirements.txt - -COPY . . - -RUN python setup.py install - -COPY docker/*.sh . -RUN chmod +x *.sh - -# ini files in config directory defines the configurable parameters for the application -# they can all be overridden by environment variables -# to generate a list of environment variables from configuration, use: confini-dump -z (executable provided by confini package) -COPY .config/ /usr/local/etc/cic-notify/ -COPY cic_notify/db/migrations/ /usr/local/share/cic-notify/alembic/ - -ENTRYPOINT [] diff --git a/apps/cic-notify/docker/run_tests.sh b/apps/cic-notify/docker/run_tests.sh new file mode 100644 index 00000000..1c2e8678 --- /dev/null +++ b/apps/cic-notify/docker/run_tests.sh @@ -0,0 +1,9 @@ +#! /bin/bash + +set -e + +pip install --extra-index-url https://pip.grassrootseconomics.net:8433 \ +--extra-index-url https://gitlab.com/api/v4/projects/27624814/packages/pypi/simple \ +-r test_requirements.txt + +export PYTHONPATH=. && pytest -x --cov=cic_notify --cov-fail-under=90 --cov-report term-missing tests diff --git a/apps/cic-ussd/.gitlab-ci.yml b/apps/cic-ussd/.gitlab-ci.yml index e028dc6c..09923c75 100644 --- a/apps/cic-ussd/.gitlab-ci.yml +++ b/apps/cic-ussd/.gitlab-ci.yml @@ -1,52 +1,16 @@ -.cic_ussd_variables: - variables: - APP_NAME: cic-ussd - DOCKERFILE_PATH: docker/Dockerfile_ci - CONTEXT: apps/$APP_NAME - -build-mr-cic-ussd: - extends: - - .py_build_merge_request - - .cic_ussd_variables - rules: - - if: $CI_PIPELINE_SOURCE == "merge_request_event" - changes: - - apps/cic-ussd/**/* - when: always - -test-mr-cic-ussd: - stage: test - extends: - - .cic_ussd_variables - cache: - key: - files: - - test_requirements.txt - paths: - - /root/.cache/pip - image: $MR_IMAGE_TAG - script: - - cd apps/$APP_NAME/ - - > - pip install --extra-index-url https://pip.grassrootseconomics.net:8433 - --extra-index-url https://gitlab.com/api/v4/projects/27624814/packages/pypi/simple - -r test_requirements.txt - - export PYTHONPATH=. && pytest -x --cov=cic_ussd --cov-fail-under=90 --cov-report term-missing tests/cic_ussd - needs: ["build-mr-cic-ussd"] - rules: - - if: $CI_PIPELINE_SOURCE == "merge_request_event" - changes: - - apps/$APP_NAME/**/* - when: always - -build-push-cic-ussd: - extends: - - .py_build_push - - .cic_ussd_variables - rules: - - if: $CI_COMMIT_BRANCH == "master" - changes: - - apps/cic-ussd/**/* - when: always - - +build-test-cic-ussd: + stage: test + tags: + - integration + variables: + APP_NAME: cic-ussd + MR_IMAGE_TAG: mr-$APP_NAME-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA + script: + - cd apps/cic-ussd + - docker build -t $MR_IMAGE_TAG -f docker/Dockerfile . + - docker run $MR_IMAGE_TAG sh docker/run_tests.sh + rules: + - if: $CI_PIPELINE_SOURCE == "merge_request_event" + changes: + - apps/$APP_NAME/**/* + when: always diff --git a/apps/cic-ussd/docker/Dockerfile_ci b/apps/cic-ussd/docker/Dockerfile_ci deleted file mode 100644 index 9811f563..00000000 --- a/apps/cic-ussd/docker/Dockerfile_ci +++ /dev/null @@ -1,32 +0,0 @@ -# syntax = docker/dockerfile:1.2 -FROM registry.gitlab.com/grassrootseconomics/cic-base-images:python-3.8.6-dev-55da5f4e as dev -RUN apt-get install -y redis-server - -# create secrets directory -RUN mkdir -vp pgp/keys - -# create application directory -RUN mkdir -vp cic-ussd -RUN mkdir -vp data - -COPY requirements.txt . - -ARG EXTRA_INDEX_URL="https://pip.grassrootseconomics.net:8433" -ARG GITLAB_PYTHON_REGISTRY="https://gitlab.com/api/v4/projects/27624814/packages/pypi/simple" -RUN pip install --index-url https://pypi.org/simple \ - --extra-index-url $GITLAB_PYTHON_REGISTRY --extra-index-url $EXTRA_INDEX_URL \ - -r requirements.txt - -COPY . . -RUN python setup.py install - -COPY cic_ussd/db/ussd_menu.json data/ - -COPY docker/*.sh . -RUN chmod +x /root/*.sh - -# copy config and migration files to definitive file so they can be referenced in path definitions for running scripts -COPY config/ /usr/local/etc/cic-ussd/ -COPY cic_ussd/db/migrations/ /usr/local/share/cic-ussd/alembic - -ENTRYPOINT [] diff --git a/apps/cic-ussd/docker/run_tests.sh b/apps/cic-ussd/docker/run_tests.sh new file mode 100644 index 00000000..ab673411 --- /dev/null +++ b/apps/cic-ussd/docker/run_tests.sh @@ -0,0 +1,10 @@ +#! /bin/bash + +set -e + + +pip install --extra-index-url https://pip.grassrootseconomics.net:8433 \ +--extra-index-url https://gitlab.com/api/v4/projects/27624814/packages/pypi/simple \ +-r test_requirements.txt + +export PYTHONPATH=. && pytest -x --cov=cic_ussd --cov-fail-under=90 --cov-report term-missing tests/cic_ussd diff --git a/apps/contract-migration/.gitlab-ci.yml b/apps/contract-migration/.gitlab-ci.yml index d48f7933..555dfb31 100644 --- a/apps/contract-migration/.gitlab-ci.yml +++ b/apps/contract-migration/.gitlab-ci.yml @@ -1,25 +1,25 @@ -.contract_migration_variables: - variables: - APP_NAME: contract-migration - DOCKERFILE_PATH: docker/Dockerfile_ci - CONTEXT: apps/$APP_NAME - -build-mr-contract-migration: - extends: - - .py_build_merge_request - - .contract_migration_variables - rules: - - if: $CI_PIPELINE_SOURCE == "merge_request_event" - changes: - - apps/contract-migration/**/* - when: always - -build-push-contract-migration: - extends: - - .py_build_push - - .contract_migration_variables - rules: - - if: $CI_COMMIT_BRANCH == "master" - changes: - - apps/contract-migration/**/* - when: always +#.contract_migration_variables: +# variables: +# APP_NAME: contract-migration +# DOCKERFILE_PATH: docker/Dockerfile_ci +# CONTEXT: apps/$APP_NAME +# +#build-mr-contract-migration: +# extends: +# - .py_build_merge_request +# - .contract_migration_variables +# rules: +# - if: $CI_PIPELINE_SOURCE == "merge_request_event" +# changes: +# - apps/contract-migration/**/* +# when: always +# +#build-push-contract-migration: +# extends: +# - .py_build_push +# - .contract_migration_variables +# rules: +# - if: $CI_COMMIT_BRANCH == "master" +# changes: +# - apps/contract-migration/**/* +# when: always diff --git a/docker-compose.yml b/docker-compose.yml index 08010b38..d61cab55 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,20 +13,8 @@ networks: name: cic-network services: - # eth: - # image: trufflesuite/ganache-cli - # ports: - # - ${HTTP_PORT_ETH:-8545} - # - ${WS_PORT_ETH:-8546} - # # Note! -e switch doesnt work, whatever you put there, it will be 100 - # command: "-i 8996 -e 1000 -l 90000000 \ - # -m '${DEV_MNEMONIC:-\"history stumble mystery avoid embark arrive mom foil pledge keep grain dice\"}' \ - # -v --db /tmp/cic/ganache/ganache.db \ - # --noVMErrorsOnRPCResponse --allowUnlimitedContractSize" - # volumes: - # - ganache-db:/tmp/cic/ganache - eth: + image: registry.gitlab.com/grassrootseconomics/cic-internal-integration/bloxberg-node:${TAG:-latest} build: context: apps/bloxbergValidatorSetup restart: unless-stopped @@ -71,6 +59,7 @@ services: - bee-data:/tmp/cic/bee contract-migration: + image: registry.gitlab.com/grassrootseconomics/cic-internal-integration/contract-migration:${TAG:-latest} profiles: - migrations build: @@ -129,6 +118,7 @@ services: - contract-config:/tmp/cic/config cic-cache-tracker: + image: registry.gitlab.com/grassrootseconomics/cic-internal-integration/cic-cache:${TAG:-latest} profiles: - cache build: @@ -170,6 +160,7 @@ services: - contract-config:/tmp/cic/config/:ro cic-cache-tasker: + image: registry.gitlab.com/grassrootseconomics/cic-internal-integration/cic-cache:${TAG:-latest} profiles: - cache build: @@ -210,6 +201,7 @@ services: - contract-config:/tmp/cic/config/:ro cic-cache-server: + image: registry.gitlab.com/grassrootseconomics/cic-internal-integration/cic-cache:${TAG:-latest} profiles: - cache build: @@ -245,6 +237,7 @@ services: cic-eth-tasker: + image: registry.gitlab.com/grassrootseconomics/cic-internal-integration/cic-eth:${TAG:-latest} build: context: apps/cic-eth dockerfile: docker/Dockerfile @@ -298,6 +291,7 @@ services: # command: [/bin/sh, "./start_tasker.sh", -q, cic-eth, -vv ] cic-eth-tracker: + image: registry.gitlab.com/grassrootseconomics/cic-internal-integration/cic-eth:${TAG:-latest} build: context: apps/cic-eth dockerfile: docker/Dockerfile @@ -342,6 +336,7 @@ services: cic-eth-dispatcher: + image: registry.gitlab.com/grassrootseconomics/cic-internal-integration/cic-eth:${TAG:-latest} build: context: apps/cic-eth dockerfile: docker/Dockerfile @@ -386,6 +381,7 @@ services: cic-eth-retrier: + image: registry.gitlab.com/grassrootseconomics/cic-internal-integration/cic-eth:${TAG:-latest} build: context: apps/cic-eth dockerfile: docker/Dockerfile @@ -433,6 +429,7 @@ services: cic-notify-tasker: + image: registry.gitlab.com/grassrootseconomics/cic-internal-integration/cic-notify:${TAG:-latest} build: context: apps/cic-notify dockerfile: docker/Dockerfile @@ -461,6 +458,7 @@ services: cic-meta-server: + image: registry.gitlab.com/grassrootseconomics/cic-internal-integration/cic-meta:${TAG:-latest} profiles: - custodial-meta hostname: meta @@ -496,6 +494,7 @@ services: # command: "/root/start_server.sh -vv" cic-user-ussd-server: + image: registry.gitlab.com/grassrootseconomics/cic-internal-integration/cic-ussd:${TAG:-latest} profiles: - custodial-ussd build: @@ -528,6 +527,7 @@ services: command: "/root/start_cic_user_ussd_server.sh -vv" cic-user-server: + image: registry.gitlab.com/grassrootseconomics/cic-internal-integration/cic-ussd:${TAG:-latest} profiles: - custodial-ussd build: @@ -553,6 +553,7 @@ services: command: "/root/start_cic_user_server.sh -vv" cic-user-tasker: + image: registry.gitlab.com/grassrootseconomics/cic-internal-integration/cic-ussd:${TAG:-latest} profiles: - custodial-ussd build: diff --git a/scripts/build-push.sh b/scripts/build-push.sh new file mode 100755 index 00000000..141a9e6c --- /dev/null +++ b/scripts/build-push.sh @@ -0,0 +1,9 @@ +#! /usr/bin/env sh + +# Exit in case of error +set -e + +TAG=${TAG?Variable not set} \ +sh ./scripts/build.sh + +docker-compose -f docker-compose.yml push diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100755 index 00000000..b3da3a6a --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,9 @@ +#! /usr/bin/env sh + +# Exit in case of error +set -e + +TAG=${TAG?Variable not set} \ +docker-compose \ +-f docker-compose.yml \ +build diff --git a/scripts/test-local.sh b/scripts/test-local.sh new file mode 100755 index 00000000..4c180f21 --- /dev/null +++ b/scripts/test-local.sh @@ -0,0 +1,15 @@ +#! /usr/bin/env bash + +# Exit in case of error +set -e + +docker-compose down -v --remove-orphans # Remove possibly previous broken stacks left hanging after an error + +if [ $(uname -s) = "Linux" ]; then + echo "Remove __pycache__ files" + sudo find . -type d -name __pycache__ -exec rm -r {} \+ +fi + +docker-compose build +docker-compose up -d +docker-compose exec -T backend bash /app/tests-start.sh "$@" From 92cc6a3f2786373bacb186b2dff4cc40342fd25e Mon Sep 17 00:00:00 2001 From: Philip Wafula Date: Sun, 29 Aug 2021 09:55:47 +0000 Subject: [PATCH 2/5] Consolidated ussd dataseeding script --- apps/cic-notify/cic_notify/tasks/sms/db.py | 6 +- apps/cic-notify/cic_notify/tasks/sms/log.py | 7 +- apps/cic-ussd/cic_ussd/account/transaction.py | 11 +- apps/cic-ussd/cic_ussd/metadata/base.py | 2 +- .../cic-ussd/cic_ussd/session/ussd_session.py | 2 +- .../cic_ussd/tasks/callback_handler.py | 14 +- apps/cic-ussd/cic_ussd/tasks/processor.py | 9 +- apps/data-seeding/cic_ussd/import_balance.py | 194 +++++----- apps/data-seeding/cic_ussd/import_pins.py | 72 ++-- apps/data-seeding/cic_ussd/import_task.py | 335 +++++++----------- apps/data-seeding/cic_ussd/import_users.py | 250 +++++++------ .../data-seeding/cic_ussd/import_ussd_data.py | 90 ++--- apps/data-seeding/config/app.ini | 29 +- apps/data-seeding/config/database.ini | 18 +- apps/data-seeding/config/ussd.ini | 5 + apps/data-seeding/create_import_pins.py | 91 ----- apps/data-seeding/import_ussd.sh | 55 +++ apps/data-seeding/verify.py | 3 +- 18 files changed, 520 insertions(+), 673 deletions(-) create mode 100644 apps/data-seeding/config/ussd.ini delete mode 100644 apps/data-seeding/create_import_pins.py create mode 100644 apps/data-seeding/import_ussd.sh diff --git a/apps/cic-notify/cic_notify/tasks/sms/db.py b/apps/cic-notify/cic_notify/tasks/sms/db.py index ca623154..e21b2b79 100644 --- a/apps/cic-notify/cic_notify/tasks/sms/db.py +++ b/apps/cic-notify/cic_notify/tasks/sms/db.py @@ -11,12 +11,12 @@ celery_app = celery.current_app @celery_app.task -def persist_notification(recipient, message): +def persist_notification(message, recipient): """ - :param recipient: - :type recipient: :param message: :type message: + :param recipient: + :type recipient: :return: :rtype: """ diff --git a/apps/cic-notify/cic_notify/tasks/sms/log.py b/apps/cic-notify/cic_notify/tasks/sms/log.py index 30de7741..05a6444a 100644 --- a/apps/cic-notify/cic_notify/tasks/sms/log.py +++ b/apps/cic-notify/cic_notify/tasks/sms/log.py @@ -11,12 +11,13 @@ local_logg = logging.getLogger(__name__) @celery_app.task -def log(recipient, message): +def log(message, recipient): """ - :param recipient: - :type recipient: + :param message: :type message: + :param recipient: + :type recipient: :return: :rtype: """ diff --git a/apps/cic-ussd/cic_ussd/account/transaction.py b/apps/cic-ussd/cic_ussd/account/transaction.py index 02b22270..980aae42 100644 --- a/apps/cic-ussd/cic_ussd/account/transaction.py +++ b/apps/cic-ussd/cic_ussd/account/transaction.py @@ -1,5 +1,6 @@ # standard import import decimal +import json import logging from typing import Dict, Tuple @@ -8,6 +9,8 @@ from cic_eth.api import Api from sqlalchemy.orm.session import Session # local import +from cic_ussd.account.chain import Chain +from cic_ussd.account.tokens import get_cached_default_token from cic_ussd.db.models.account import Account from cic_ussd.db.models.base import SessionBase from cic_ussd.error import UnknownUssdRecipient @@ -59,7 +62,9 @@ def from_wei(value: int) -> float: :return: SRF equivalent of value in Wei :rtype: float """ - value = float(value) / 1e+6 + cached_token_data = json.loads(get_cached_default_token(Chain.spec.__str__())) + token_decimals: int = cached_token_data.get('decimals') + value = float(value) / (10**token_decimals) return truncate(value=value, decimals=2) @@ -70,7 +75,9 @@ def to_wei(value: int) -> int: :return: Wei equivalent of value in SRF :rtype: int """ - return int(value * 1e+6) + cached_token_data = json.loads(get_cached_default_token(Chain.spec.__str__())) + token_decimals: int = cached_token_data.get('decimals') + return int(value * (10**token_decimals)) def truncate(value: float, decimals: int): diff --git a/apps/cic-ussd/cic_ussd/metadata/base.py b/apps/cic-ussd/cic_ussd/metadata/base.py index f184adf6..c422ee7a 100644 --- a/apps/cic-ussd/cic_ussd/metadata/base.py +++ b/apps/cic-ussd/cic_ussd/metadata/base.py @@ -44,7 +44,7 @@ class MetadataRequestsHandler(Metadata): def create(self, data: Union[Dict, str]): """""" - data = json.dumps(data) + data = json.dumps(data).encode('utf-8') result = make_request(method='POST', url=self.url, data=data, headers=self.headers) error_handler(result=result) diff --git a/apps/cic-ussd/cic_ussd/session/ussd_session.py b/apps/cic-ussd/cic_ussd/session/ussd_session.py index c5ec9fbb..9df54afa 100644 --- a/apps/cic-ussd/cic_ussd/session/ussd_session.py +++ b/apps/cic-ussd/cic_ussd/session/ussd_session.py @@ -146,7 +146,7 @@ def create_ussd_session( ) -def update_ussd_session(ussd_session: UssdSession, +def update_ussd_session(ussd_session: DbUssdSession, user_input: str, state: str, data: Optional[dict] = None) -> UssdSession: diff --git a/apps/cic-ussd/cic_ussd/tasks/callback_handler.py b/apps/cic-ussd/cic_ussd/tasks/callback_handler.py index c8350996..f3a80caf 100644 --- a/apps/cic-ussd/cic_ussd/tasks/callback_handler.py +++ b/apps/cic-ussd/cic_ussd/tasks/callback_handler.py @@ -138,26 +138,14 @@ def transaction_balances_callback(self, result: list, param: dict, status_code: balances_data = result[0] available_balance = calculate_available_balance(balances_data) transaction = param - blockchain_address = transaction.get('blockchain_address') transaction['available_balance'] = available_balance queue = self.request.delivery_info.get('routing_key') - s_preferences_metadata = celery.signature( - 'cic_ussd.tasks.metadata.query_preferences_metadata', [blockchain_address], queue=queue - ) s_process_account_metadata = celery.signature( 'cic_ussd.tasks.processor.parse_transaction', [transaction], queue=queue ) s_notify_account = celery.signature('cic_ussd.tasks.notifications.transaction', queue=queue) - - if transaction.get('transaction_type') == 'transfer': - celery.chain(s_preferences_metadata, s_process_account_metadata, s_notify_account).apply_async() - - if transaction.get('transaction_type') == 'tokengift': - s_process_account_metadata = celery.signature( - 'cic_ussd.tasks.processor.parse_transaction', [{}, transaction], queue=queue - ) - celery.chain(s_process_account_metadata, s_notify_account).apply_async() + celery.chain(s_process_account_metadata, s_notify_account).apply_async() @celery_app.task diff --git a/apps/cic-ussd/cic_ussd/tasks/processor.py b/apps/cic-ussd/cic_ussd/tasks/processor.py index 287dbc58..8a188f54 100644 --- a/apps/cic-ussd/cic_ussd/tasks/processor.py +++ b/apps/cic-ussd/cic_ussd/tasks/processor.py @@ -8,6 +8,7 @@ import i18n from chainlib.hash import strip_0x # local imports +from cic_ussd.account.metadata import get_cached_preferred_language from cic_ussd.account.statement import get_cached_statement from cic_ussd.account.transaction import aux_transaction_data, validate_transaction_account from cic_ussd.cache import cache_data, cache_data_key @@ -58,19 +59,17 @@ def cache_statement(parsed_transaction: dict, querying_party: str): @celery_app.task -def parse_transaction(preferences: dict, transaction: dict) -> dict: +def parse_transaction(transaction: dict) -> dict: """This function parses transaction objects and collates all relevant data for system use i.e: - An account's set preferred language. - Account identifier that facilitates notification. - Contextual tags i.e action and direction tags. - :param preferences: An account's set preferences. - :type preferences: dict :param transaction: Transaction object. :type transaction: dict :return: Transaction object with contextual data for use in the system. :rtype: dict """ - preferred_language = preferences.get('preferred_language') + preferred_language = get_cached_preferred_language(transaction.get('blockchain_address')) if not preferred_language: preferred_language = i18n.config.get('fallback') transaction['preferred_language'] = preferred_language @@ -83,6 +82,8 @@ def parse_transaction(preferences: dict, transaction: dict) -> dict: alt_account = session.query(Account).filter_by(blockchain_address=alt_blockchain_address).first() if alt_account: transaction['alt_metadata_id'] = alt_account.standard_metadata_id() + else: + transaction['alt_metadata_id'] = 'GRASSROOTS ECONOMICS' transaction['metadata_id'] = account.standard_metadata_id() transaction['phone_number'] = account.phone_number session.close() diff --git a/apps/data-seeding/cic_ussd/import_balance.py b/apps/data-seeding/cic_ussd/import_balance.py index df5bdfe4..9f974e70 100644 --- a/apps/data-seeding/cic_ussd/import_balance.py +++ b/apps/data-seeding/cic_ussd/import_balance.py @@ -1,64 +1,61 @@ -# standard imports import argparse import logging -import sys import os +import sys # external imports import celery -import confini -import redis from chainlib.chain import ChainSpec from chainlib.eth.address import to_checksum_address from chainlib.eth.connection import EthHTTPConnection +from confini import Config from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer from crypto_dev_signer.keystore.dict import DictKeystore # local imports -from import_task import ImportTask, MetadataTask from import_util import BalanceProcessor, get_celery_worker_status +from import_task import ImportTask, MetadataTask -logging.basicConfig(level=logging.WARNING) +default_config_dir = './config' logg = logging.getLogger() -config_dir = './config' +arg_parser = argparse.ArgumentParser(description='Daemon worker that handles data seeding tasks.') +arg_parser.add_argument('-c', type=str, default=default_config_dir, help='config root to use.') +arg_parser.add_argument('--env-prefix', + default=os.environ.get('CONFINI_ENV_PREFIX'), + dest='env_prefix', + type=str, + help='environment prefix for variables to overwrite configuration.') +arg_parser.add_argument('--head', action='store_true', help='start at current block height (overrides --offset)') +arg_parser.add_argument('-i', '--chain-spec', type=str, dest='i', help='chain spec') +arg_parser.add_argument('--include-balances', dest='include_balances', help='include opening balance transactions', + action='store_true') +arg_parser.add_argument('--meta-host', dest='meta_host', type=str, help='metadata server host') +arg_parser.add_argument('--meta-port', dest='meta_port', type=int, help='metadata server host') +arg_parser.add_argument('-p', '--provider', dest='p', type=str, help='chain rpc provider address') +arg_parser.add_argument('-q', type=str, default='cic-import-ussd', help='celery queue to submit data seeding tasks to.') +arg_parser.add_argument('-r', '--registry-address', type=str, dest='r', help='CIC Registry address') +arg_parser.add_argument('--redis-db', dest='redis_db', type=int, help='redis db to use for task submission and callback') +arg_parser.add_argument('--redis-host', dest='redis_host', type=str, help='redis host to use for task submission') +arg_parser.add_argument('--redis-port', dest='redis_port', type=int, help='redis host to use for task submission') +arg_parser.add_argument('--token-symbol', default='GFT', type=str, dest='token_symbol', + help='Token symbol to use for transactions') +arg_parser.add_argument('-v', help='be verbose', action='store_true') +arg_parser.add_argument('-vv', help='be more verbose', action='store_true') +arg_parser.add_argument('-y', '--key-file', dest='y', type=str, help='Ethereum keystore file to use for signing') +arg_parser.add_argument('--offset', type=int, default=0, help='block offset to start syncer from') +arg_parser.add_argument('--old-chain-spec', type=str, dest='old_chain_spec', default='evm:oldchain:1', + help='chain spec') +arg_parser.add_argument('import_dir', default='out', type=str, help='user export directory') +args = arg_parser.parse_args() -argparser = argparse.ArgumentParser(description='daemon that monitors transactions in new blocks') -argparser.add_argument('-p', '--provider', dest='p', type=str, help='chain rpc provider address') -argparser.add_argument('-y', '--key-file', dest='y', type=str, help='Ethereum keystore file to use for signing') -argparser.add_argument('-c', type=str, default=config_dir, help='config root to use') -argparser.add_argument('--old-chain-spec', type=str, dest='old_chain_spec', default='evm:oldchain:1', help='chain spec') -argparser.add_argument('-i', '--chain-spec', type=str, dest='i', help='chain spec') -argparser.add_argument('-r', '--registry-address', type=str, dest='r', help='CIC Registry address') -argparser.add_argument('--meta-host', dest='meta_host', type=str, help='metadata server host') -argparser.add_argument('--meta-port', dest='meta_port', type=int, help='metadata server host') -argparser.add_argument('--redis-host', dest='redis_host', type=str, help='redis host to use for task submission') -argparser.add_argument('--redis-port', dest='redis_port', type=int, help='redis host to use for task submission') -argparser.add_argument('--redis-db', dest='redis_db', type=int, help='redis db to use for task submission and callback') -argparser.add_argument('--token-symbol', default='GFT', type=str, dest='token_symbol', - help='Token symbol to use for transactions') -argparser.add_argument('--head', action='store_true', help='start at current block height (overrides --offset)') -argparser.add_argument('--env-prefix', default=os.environ.get('CONFINI_ENV_PREFIX'), dest='env_prefix', type=str, - help='environment prefix for variables to overwrite configuration') -argparser.add_argument('-q', type=str, default='cic-import-ussd', help='celery queue to submit transaction tasks to') -argparser.add_argument('--offset', type=int, default=0, help='block offset to start syncer from') -argparser.add_argument('-v', help='be verbose', action='store_true') -argparser.add_argument('-vv', help='be more verbose', action='store_true') -argparser.add_argument('user_dir', default='out', type=str, help='user export directory') -args = argparser.parse_args(sys.argv[1:]) - -if args.v: +if args.vv: + logging.getLogger().setLevel(logging.DEBUG) +elif args.v: logging.getLogger().setLevel(logging.INFO) -elif args.vv: - logging.getLogger().setLevel(logging.DEBUG) - -config_dir = os.path.join(args.c) -os.makedirs(config_dir, 0o777, True) -config = confini.Config(config_dir, args.env_prefix) +config = Config(args.c, args.env_prefix) config.process() - -# override args args_override = { 'CIC_CHAIN_SPEC': getattr(args, 'i'), 'ETH_PROVIDER': getattr(args, 'p'), @@ -73,88 +70,76 @@ args_override = { config.dict_override(args_override, 'cli flag') config.censor('PASSWORD', 'DATABASE') config.censor('PASSWORD', 'SSL') -logg.debug('config loaded from {}:\n{}'.format(config_dir, config)) +logg.debug(f'config loaded from {args.c}:\n{config}') -redis_host = config.get('REDIS_HOST') -redis_port = config.get('REDIS_PORT') -redis_db = config.get('REDIS_DB') -r = redis.Redis(redis_host, redis_port, redis_db) +db_config = { + 'database': config.get('DATABASE_NAME'), + 'host': config.get('DATABASE_HOST'), + 'port': config.get('DATABASE_PORT'), + 'user': config.get('DATABASE_USER'), + 'password': config.get('DATABASE_PASSWORD') +} +ImportTask.db_config = db_config -# create celery apps -celery_app = celery.Celery(backend=config.get('CELERY_RESULT_URL'), broker=config.get('CELERY_BROKER_URL')) -status = get_celery_worker_status(celery_app=celery_app) - -signer_address = None keystore = DictKeystore() -if args.y is not None: - logg.debug('loading keystore file {}'.format(args.y)) - signer_address = keystore.import_keystore_file(args.y) - logg.debug('now have key for signer address {}'.format(signer_address)) - -# define signer +os.path.isfile(args.y) +logg.debug(f'loading keystore file {args.y}') +signer_address = keystore.import_keystore_file(args.y) +logg.debug(f'now have key for signer address {signer_address}') signer = EIP155Signer(keystore) -queue = args.q -chain_str = config.get('CIC_CHAIN_SPEC') -block_offset = 0 -if args.head: - block_offset = -1 -else: - block_offset = args.offset +block_offset = -1 if args.head else args.offset +chain_str = config.get('CIC_CHAIN_SPEC') chain_spec = ChainSpec.from_chain_str(chain_str) +ImportTask.chain_spec = chain_spec old_chain_spec_str = args.old_chain_spec old_chain_spec = ChainSpec.from_chain_str(old_chain_spec_str) -user_dir = args.user_dir # user_out_dir from import_users.py - -token_symbol = args.token_symbol - MetadataTask.meta_host = config.get('META_HOST') MetadataTask.meta_port = config.get('META_PORT') -ImportTask.chain_spec = chain_spec + +txs_dir = os.path.join(args.import_dir, 'txs') +os.makedirs(txs_dir, exist_ok=True) +sys.stdout.write(f'created txs dir: {txs_dir}') + +celery_app = celery.Celery(broker=config.get('CELERY_BROKER_URL'), backend=config.get('CELERY_RESULT_URL')) +get_celery_worker_status(celery_app) def main(): conn = EthHTTPConnection(config.get('ETH_PROVIDER')) - - ImportTask.balance_processor = BalanceProcessor(conn, chain_spec, config.get('CIC_REGISTRY_ADDRESS'), - signer_address, signer) - ImportTask.balance_processor.init(token_symbol) - - # TODO get decimals from token + ImportTask.balance_processor = BalanceProcessor(conn, + chain_spec, + config.get('CIC_REGISTRY_ADDRESS'), + signer_address, + signer) + ImportTask.balance_processor.init(args.token_symbol) balances = {} - f = open('{}/balances.csv'.format(user_dir, 'r')) - remove_zeros = 10 ** 6 - i = 0 - while True: - l = f.readline() - if l is None: - break - r = l.split(',') - try: - address = to_checksum_address(r[0]) - sys.stdout.write('loading balance {} {} {}'.format(i, address, r[1]).ljust(200) + "\r") - except ValueError: - break - balance = int(int(r[1].rstrip()) / remove_zeros) - balances[address] = balance - i += 1 - - f.close() - + accuracy = 10 ** 6 + count = 0 + with open(f'{args.import_dir}/balances.csv', 'r') as balances_file: + while True: + line = balances_file.readline() + if line is None: + break + balance_data = line.split(',') + try: + blockchain_address = to_checksum_address(balance_data[0]) + logg.info( + 'loading balance: {} {} {}'.format(count, blockchain_address, balance_data[1].ljust(200) + "\r")) + except ValueError: + break + balance = int(int(balance_data[1].rstrip()) / accuracy) + balances[blockchain_address] = balance + count += 1 ImportTask.balances = balances - ImportTask.count = i - ImportTask.import_dir = user_dir - - s = celery.signature( - 'import_task.send_txs', - [ - MetadataTask.balance_processor.nonce_offset, - ], - queue=queue, - ) - s.apply_async() + ImportTask.count = count + ImportTask.include_balances = args.include_balances is True + ImportTask.import_dir = args.import_dir + s_send_txs = celery.signature( + 'import_task.send_txs', [ImportTask.balance_processor.nonce_offset], queue=args.q) + s_send_txs.apply_async() argv = ['worker'] if args.vv: @@ -165,6 +150,7 @@ def main(): argv.append(args.q) argv.append('-n') argv.append(args.q) + argv.append(f'--pidfile={args.q}.pid') celery_app.worker_main(argv) diff --git a/apps/data-seeding/cic_ussd/import_pins.py b/apps/data-seeding/cic_ussd/import_pins.py index 827d3840..047faf0a 100644 --- a/apps/data-seeding/cic_ussd/import_pins.py +++ b/apps/data-seeding/cic_ussd/import_pins.py @@ -1,71 +1,63 @@ -# standard import +# standard imports import argparse import csv import logging import os +import psycopg2 -# third-party imports -import celery -import confini +# external imports +from confini import Config # local imports -from import_util import get_celery_worker_status + +default_config_dir = './config' logging.basicConfig(level=logging.WARNING) logg = logging.getLogger() -default_config_dir = './config' - -arg_parser = argparse.ArgumentParser() -arg_parser.add_argument('-c', type=str, default=default_config_dir, help='config root to use') +arg_parser = argparse.ArgumentParser(description='Pins import script.') +arg_parser.add_argument('-c', type=str, default=default_config_dir, help='config root to use.') arg_parser.add_argument('--env-prefix', default=os.environ.get('CONFINI_ENV_PREFIX'), dest='env_prefix', type=str, - help='environment prefix for variables to overwrite configuration') -arg_parser.add_argument('-q', type=str, default='cic-import-ussd', help='celery queue to submit transaction tasks to') + help='environment prefix for variables to overwrite configuration.') +arg_parser.add_argument('import_dir', default='out', type=str, help='user export directory') arg_parser.add_argument('-v', help='be verbose', action='store_true') arg_parser.add_argument('-vv', help='be more verbose', action='store_true') -arg_parser.add_argument('pins_dir', default='out', type=str, help='user export directory') + args = arg_parser.parse_args() -# set log levels -if args.v: - logg.setLevel(logging.INFO) -elif args.vv: - logg.setLevel(logging.DEBUG) +if args.vv: + logging.getLogger().setLevel(logging.DEBUG) +elif args.v: + logging.getLogger().setLevel(logging.INFO) -# process configs -config_dir = args.c -config = confini.Config(config_dir, os.environ.get('CONFINI_ENV_PREFIX')) +config = Config(args.c, args.env_prefix) config.process() config.censor('PASSWORD', 'DATABASE') -logg.debug('config loaded from {}:\n{}'.format(args.c, config)) - -celery_app = celery.Celery(broker=config.get('CELERY_BROKER_URL'), backend=config.get('CELERY_RESULT_URL')) -status = get_celery_worker_status(celery_app=celery_app) - - -db_configs = { - 'database': config.get('DATABASE_NAME'), - 'host': config.get('DATABASE_HOST'), - 'port': config.get('DATABASE_PORT'), - 'user': config.get('DATABASE_USER'), - 'password': config.get('DATABASE_PASSWORD') -} +logg.debug(f'config loaded from {args.c}:\n{config}') def main(): - with open(f'{args.pins_dir}/pins.csv') as pins_file: + with open(f'{args.import_dir}/pins.csv') as pins_file: phone_to_pins = [tuple(row) for row in csv.reader(pins_file)] - s_import_pins = celery.signature( - 'import_task.set_pins', - (db_configs, phone_to_pins), - queue=args.q + db_conn = psycopg2.connect( + database=config.get('DATABASE_NAME'), + host=config.get('DATABASE_HOST'), + port=config.get('DATABASE_PORT'), + user=config.get('DATABASE_USER'), + password=config.get('DATABASE_PASSWORD') ) - result = s_import_pins.apply_async() - logg.debug(f'TASK: {result.id}, STATUS: {result.status}') + db_cursor = db_conn.cursor() + sql = 'UPDATE account SET password_hash = %s WHERE phone_number = %s' + for element in phone_to_pins: + db_cursor.execute(sql, (element[1], element[0])) + logg.debug(f'Updating account: {element[0]} with: {element[1]}') + db_conn.commit() + db_cursor.close() + db_conn.close() if __name__ == '__main__': diff --git a/apps/data-seeding/cic_ussd/import_task.py b/apps/data-seeding/cic_ussd/import_task.py index d04f6dea..00e72f1a 100644 --- a/apps/data-seeding/cic_ussd/import_task.py +++ b/apps/data-seeding/cic_ussd/import_task.py @@ -1,38 +1,37 @@ # standard imports +import csv import json import logging import os import random -import urllib.error -import urllib.parse -import urllib.request +import uuid +from urllib import error, parse, request # external imports import celery import psycopg2 +from celery import Task +from chainlib.chain import ChainSpec from chainlib.eth.address import to_checksum_address -from chainlib.eth.tx import ( - unpack, - raw, -) -from cic_types.models.person import Person -from cic_types.processor import generate_metadata_pointer -from hexathon import ( - strip_0x, - add_0x, -) +from chainlib.eth.tx import raw, unpack +from cic_types.models.person import Person, generate_metadata_pointer +from hexathon import add_0x, strip_0x + +# local imports -logg = logging.getLogger() celery_app = celery.current_app +logg = logging.getLogger() -class ImportTask(celery.Task): +class ImportTask(Task): balances = None - import_dir = 'out' - count = 0 - chain_spec = None balance_processor = None + chain_spec: ChainSpec = None + count = 0 + db_config: dict = None + import_dir = '' + include_balances = False max_retries = None @@ -41,121 +40,70 @@ class MetadataTask(ImportTask): meta_port = None meta_path = '' meta_ssl = False - autoretry_for = ( - urllib.error.HTTPError, - OSError, - ) + autoretry_for = (error.HTTPError, OSError,) retry_jitter = True retry_backoff = True retry_backoff_max = 60 @classmethod - def meta_url(self): + def meta_url(cls): scheme = 'http' - if self.meta_ssl: + if cls.meta_ssl: scheme += 's' - url = urllib.parse.urlparse('{}://{}:{}/{}'.format(scheme, self.meta_host, self.meta_port, self.meta_path)) - return urllib.parse.urlunparse(url) + url = parse.urlparse(f'{scheme}://{cls.meta_host}:{cls.meta_port}/{cls.meta_path}') + return parse.urlunparse(url) -def old_address_from_phone(base_path, phone): - pidx = generate_metadata_pointer(phone.encode('utf-8'), ':cic.phone') - phone_idx_path = os.path.join('{}/phone/{}/{}/{}'.format( - base_path, - pidx[:2], - pidx[2:4], - pidx, - ) - ) - f = open(phone_idx_path, 'r') - old_address = f.read() - f.close() - +def old_address_from_phone(base_path: str, phone_number: str): + pid_x = generate_metadata_pointer(phone_number.encode('utf-8'), ':cic.phone') + phone_idx_path = os.path.join(f'{base_path}/phone/{pid_x[:2]}/{pid_x[2:4]}/{pid_x}') + with open(phone_idx_path, 'r') as f: + old_address = f.read() return old_address @celery_app.task(bind=True, base=MetadataTask) -def resolve_phone(self, phone): - identifier = generate_metadata_pointer(phone.encode('utf-8'), ':cic.phone') - url = urllib.parse.urljoin(self.meta_url(), identifier) - logg.debug('attempt getting phone pointer at {} for phone {}'.format(url, phone)) - r = urllib.request.urlopen(url) - address = json.load(r) - address = address.replace('"', '') - logg.debug('address {} for phone {}'.format(address, phone)) - - return address - - -@celery_app.task(bind=True, base=MetadataTask) -def generate_metadata(self, address, phone): - old_address = old_address_from_phone(self.import_dir, phone) - - logg.debug('address {}'.format(address)) - old_address_upper = strip_0x(old_address).upper() - metadata_path = '{}/old/{}/{}/{}.json'.format( - self.import_dir, - old_address_upper[:2], - old_address_upper[2:4], - old_address_upper, - ) - - f = open(metadata_path, 'r') - o = json.load(f) - f.close() - - u = Person.deserialize(o) - - if u.identities.get('evm') == None: - u.identities['evm'] = {} - sub_chain_str = '{}:{}'.format(self.chain_spec.common_name(), self.chain_spec.network_id()) - u.identities['evm'][sub_chain_str] = [add_0x(address)] - - new_address_clean = strip_0x(address) - filepath = os.path.join( +def generate_person_metadata(self, blockchain_address: str, phone_number: str): + logg.debug(f'blockchain address: {blockchain_address}') + old_blockchain_address = old_address_from_phone(self.import_dir, phone_number) + old_address_upper = strip_0x(old_blockchain_address).upper() + metadata_path = f'{self.import_dir}/old/{old_address_upper[:2]}/{old_address_upper[2:4]}/{old_address_upper}.json' + with open(metadata_path, 'r') as metadata_file: + person_metadata = json.load(metadata_file) + person = Person.deserialize(person_metadata) + if not person.identities.get('evm'): + person.identities['evm'] = {} + sub_chain_str = f'{self.chain_spec.common_name()}:{self.chain_spec.network_id()}' + person.identities['evm'][sub_chain_str] = [add_0x(blockchain_address)] + blockchain_address = strip_0x(blockchain_address) + file_path = os.path.join( self.import_dir, 'new', - new_address_clean[:2].upper(), - new_address_clean[2:4].upper(), - new_address_clean.upper() + '.json', + blockchain_address[:2].upper(), + blockchain_address[2:4].upper(), + blockchain_address.upper() + '.json' ) - os.makedirs(os.path.dirname(filepath), exist_ok=True) - - o = u.serialize() - f = open(filepath, 'w') - f.write(json.dumps(o)) - f.close() - - meta_key = generate_metadata_pointer(bytes.fromhex(new_address_clean), ':cic.person') + os.makedirs(os.path.dirname(file_path), exist_ok=True) + serialized_person_metadata = person.serialize() + with open(file_path, 'w') as metadata_file: + metadata_file.write(json.dumps(serialized_person_metadata)) + logg.debug(f'written person metadata for address: {blockchain_address}') meta_filepath = os.path.join( self.import_dir, 'meta', - '{}.json'.format(new_address_clean.upper()), + '{}.json'.format(blockchain_address.upper()), ) - os.symlink(os.path.realpath(filepath), meta_filepath) + os.symlink(os.path.realpath(file_path), meta_filepath) + return blockchain_address - # write ussd data - ussd_data = { - 'phone': phone, - 'is_activated': 1, - 'preferred_language': random.sample(['en', 'sw'], 1)[0], - 'is_disabled': False - } - ussd_data_dir = os.path.join(self.import_dir, 'ussd') - ussd_data_file_path = os.path.join(ussd_data_dir, f'{old_address}.json') - f = open(ussd_data_file_path, 'w') - f.write(json.dumps(ussd_data)) - f.close() - # write preferences data +@celery_app.task(bind=True, base=MetadataTask) +def generate_preferences_data(self, data: tuple): + blockchain_address: str = data[0] + preferences = data[1] preferences_dir = os.path.join(self.import_dir, 'preferences') - preferences_data = { - 'preferred_language': ussd_data['preferred_language'] - } - - preferences_key = generate_metadata_pointer(bytes.fromhex(new_address_clean[2:]), ':cic.preferences') + preferences_key = generate_metadata_pointer(bytes.fromhex(strip_0x(blockchain_address)), ':cic.preferences') preferences_filepath = os.path.join(preferences_dir, 'meta', preferences_key) - filepath = os.path.join( preferences_dir, 'new', @@ -164,95 +112,95 @@ def generate_metadata(self, address, phone): preferences_key.upper() + '.json' ) os.makedirs(os.path.dirname(filepath), exist_ok=True) - - f = open(filepath, 'w') - f.write(json.dumps(preferences_data)) - f.close() + with open(filepath, 'w') as preferences_file: + preferences_file.write(json.dumps(preferences)) + logg.debug(f'written preferences metadata: {preferences} for address: {blockchain_address}') os.symlink(os.path.realpath(filepath), preferences_filepath) - - logg.debug('found metadata {} for phone {}'.format(o, phone)) - - return address + return blockchain_address @celery_app.task(bind=True, base=MetadataTask) -def opening_balance_tx(self, address, phone, serial): - old_address = old_address_from_phone(self.import_dir, phone) +def generate_pins_data(self, blockchain_address: str, phone_number: str): + pins_file = f'{self.import_dir}/pins.csv' + file_op = 'a' if os.path.exists(pins_file) else 'w' + with open(pins_file, file_op) as pins_file: + password_hash = uuid.uuid4().hex + pins_file.write(f'{phone_number},{password_hash}\n') + logg.debug(f'written pin data for address: {blockchain_address}') + return blockchain_address - k = to_checksum_address(strip_0x(old_address)) - balance = self.balances[k] - logg.debug('found balance {} for address {} phone {}'.format(balance, old_address, phone)) +@celery_app.task(bind=True, base=MetadataTask) +def generate_ussd_data(self, blockchain_address: str, phone_number: str): + ussd_data_file = f'{self.import_dir}/ussd_data.csv' + file_op = 'a' if os.path.exists(ussd_data_file) else 'w' + preferred_language = random.sample(["en", "sw"], 1)[0] + preferences = {'preferred_language': preferred_language} + with open(ussd_data_file, file_op) as ussd_data_file: + ussd_data_file.write(f'{phone_number}, { 1}, {preferred_language}, {False}\n') + logg.debug(f'written ussd data for address: {blockchain_address}') + return blockchain_address, preferences + + +@celery_app.task(bind=True, base=MetadataTask) +def opening_balance_tx(self, blockchain_address: str, phone_number: str, serial: str): + old_blockchain_address = old_address_from_phone(self.import_dir, phone_number) + address = to_checksum_address(strip_0x(old_blockchain_address)) + balance = self.balances[address] + logg.debug(f'found balance: {balance} for address: {address} phone: {phone_number}') decimal_balance = self.balance_processor.get_decimal_amount(int(balance)) - - (tx_hash_hex, o) = self.balance_processor.get_rpc_tx(address, decimal_balance, serial) - + tx_hash_hex, o = self.balance_processor.get_rpc_tx(blockchain_address, decimal_balance, serial) tx = unpack(bytes.fromhex(strip_0x(o)), self.chain_spec) - logg.debug('generated tx token value {} to {} tx hash {}'.format(decimal_balance, address, tx_hash_hex)) - - tx_path = os.path.join( - self.import_dir, - 'txs', - strip_0x(tx_hash_hex), - ) - - f = open(tx_path, 'w') - f.write(strip_0x(o)) - f.close() - - tx_nonce_path = os.path.join( - self.import_dir, - 'txs', - '.' + str(tx['nonce']), - ) + logg.debug(f'generated tx token value: {decimal_balance}: {blockchain_address} tx hash {tx_hash_hex}') + tx_path = os.path.join(self.import_dir, 'txs', strip_0x(tx_hash_hex)) + with open(tx_path, 'w') as tx_file: + tx_file.write(strip_0x(o)) + logg.debug(f'written tx with tx hash: {tx["hash"]} for address: {blockchain_address}') + tx_nonce_path = os.path.join(self.import_dir, 'txs', '.' + str(tx['nonce'])) os.symlink(os.path.realpath(tx_path), tx_nonce_path) - return tx['hash'] -@celery_app.task(bind=True, base=ImportTask, autoretry_for=(FileNotFoundError,), max_retries=None, +@celery_app.task(bind=True, base=MetadataTask) +def resolve_phone(self, phone_number: str): + identifier = generate_metadata_pointer(phone_number.encode('utf-8'), ':cic.phone') + url = parse.urljoin(self.meta_url(), identifier) + logg.debug(f'attempt getting phone pointer at: {url} for phone: {phone_number}') + r = request.urlopen(url) + address = json.load(r) + address = address.replace('"', '') + logg.debug(f'address: {address} for phone: {phone_number}') + return address + + +@celery_app.task(autoretry_for=(FileNotFoundError,), + bind=True, + base=ImportTask, + max_retries=None, default_retry_delay=0.1) def send_txs(self, nonce): - if nonce == self.count + self.balance_processor.nonce_offset: - logg.info('reached nonce {} (offset {} + count {}) exiting'.format(nonce, self.balance_processor.nonce_offset, - self.count)) - return - - logg.debug('attempt to open symlink for nonce {}'.format(nonce)) - tx_nonce_path = os.path.join( - self.import_dir, - 'txs', - '.' + str(nonce), - ) - f = open(tx_nonce_path, 'r') - tx_signed_raw_hex = f.read() - f.close() - - os.unlink(tx_nonce_path) - - o = raw(add_0x(tx_signed_raw_hex)) - tx_hash_hex = self.balance_processor.conn.do(o) - - logg.info('sent nonce {} tx hash {}'.format(nonce, tx_hash_hex)) # tx_signed_raw_hex)) - - nonce += 1 - queue = self.request.delivery_info.get('routing_key') - s = celery.signature( - 'import_task.send_txs', - [ - nonce, - ], - queue=queue, - ) - s.apply_async() + if nonce == self.count + self.balance_processor.nonce_offset: + logg.info(f'reached nonce {nonce} (offset {self.balance_processor.nonce_offset} + count {self.count}).') + celery_app.control.broadcast('shutdown', destination=[f'celery@{queue}']) + logg.debug(f'attempt to open symlink for nonce {nonce}') + tx_nonce_path = os.path.join(self.import_dir, 'txs', '.' + str(nonce)) + with open(tx_nonce_path, 'r') as tx_nonce_file: + tx_signed_raw_hex = tx_nonce_file.read() + os.unlink(tx_nonce_path) + o = raw(add_0x(tx_signed_raw_hex)) + if self.include_balances: + tx_hash_hex = self.balance_processor.conn.do(o) + logg.info(f'sent nonce {nonce} tx hash {tx_hash_hex}') + nonce += 1 + s = celery.signature('import_task.send_txs', [nonce], queue=queue) + s.apply_async() return nonce -@celery_app.task -def set_pins(config: dict, phone_to_pins: list): - # define db connection +@celery_app.task() +def set_pin_data(config: dict, phone_to_pins: list): db_conn = psycopg2.connect( database=config.get('database'), host=config.get('host'), @@ -261,24 +209,17 @@ def set_pins(config: dict, phone_to_pins: list): password=config.get('password') ) db_cursor = db_conn.cursor() - - # update db + sql = 'UPDATE account SET password_hash = %s WHERE phone_number = %s' for element in phone_to_pins: - sql = 'UPDATE account SET password_hash = %s WHERE phone_number = %s' db_cursor.execute(sql, (element[1], element[0])) logg.debug(f'Updating: {element[0]} with: {element[1]}') - - # commit changes db_conn.commit() - - # close connections db_cursor.close() db_conn.close() @celery_app.task -def set_ussd_data(config: dict, ussd_data: dict): - # define db connection +def set_ussd_data(config: dict, ussd_data: list): db_conn = psycopg2.connect( database=config.get('database'), host=config.get('host'), @@ -287,20 +228,12 @@ def set_ussd_data(config: dict, ussd_data: dict): password=config.get('password') ) db_cursor = db_conn.cursor() - - # process ussd_data - account_status = 1 - if ussd_data['is_activated'] == 1: - account_status = 2 - preferred_language = ussd_data['preferred_language'] - phone_number = ussd_data['phone'] - sql = 'UPDATE account SET status = %s, preferred_language = %s WHERE phone_number = %s' - db_cursor.execute(sql, (account_status, preferred_language, phone_number)) - - # commit changes + for element in ussd_data: + status = 2 if int(element[1]) == 1 else 1 + preferred_language = element[2] + phone_number = element[0] + db_cursor.execute(sql, (status, preferred_language, phone_number)) db_conn.commit() - - # close connections db_cursor.close() db_conn.close() diff --git a/apps/data-seeding/cic_ussd/import_users.py b/apps/data-seeding/cic_ussd/import_users.py index 224d090b..72013737 100644 --- a/apps/data-seeding/cic_ussd/import_users.py +++ b/apps/data-seeding/cic_ussd/import_users.py @@ -3,56 +3,61 @@ import argparse import json import logging import os +import redis import sys import time -import urllib.request import uuid +from urllib import request from urllib.parse import urlencode # external imports import celery -import confini import phonenumbers -import redis -from chainlib.chain import ChainSpec from cic_types.models.person import Person +from confini import Config # local imports from import_util import get_celery_worker_status +default_config_dir = './config' logging.basicConfig(level=logging.WARNING) logg = logging.getLogger() -default_config_dir = '/usr/local/etc/cic' +arg_parser = argparse.ArgumentParser(description='Daemon worker that handles data seeding tasks.') +# batch size should be slightly below cumulative gas limit worth, eg 80000 gas txs with 8000000 limit is a bit less than 100 batch size +arg_parser.add_argument('--batch-size', + dest='batch_size', + default=100, + type=int, + help='burst size of sending transactions to node') +arg_parser.add_argument('--batch-delay', dest='batch_delay', default=3, type=int, help='seconds delay between batches') +arg_parser.add_argument('-c', type=str, default=default_config_dir, help='config root to use.') +arg_parser.add_argument('--env-prefix', + default=os.environ.get('CONFINI_ENV_PREFIX'), + dest='env_prefix', + type=str, + help='environment prefix for variables to overwrite configuration.') +arg_parser.add_argument('-i', '--chain-spec', type=str, dest='i', help='chain spec') +arg_parser.add_argument('-q', type=str, default='cic-import-ussd', help='celery queue to submit data seeding tasks to.') +arg_parser.add_argument('--redis-db', dest='redis_db', type=int, help='redis db to use for task submission and callback') +arg_parser.add_argument('--redis-host', dest='redis_host', type=str, help='redis host to use for task submission') +arg_parser.add_argument('--redis-port', dest='redis_port', type=int, help='redis host to use for task submission') +arg_parser.add_argument('--ussd-host', dest='ussd_host', type=str, + help="host to ussd app responsible for processing ussd requests.") +arg_parser.add_argument('--ussd-no-ssl', dest='ussd_no_ssl', help='do not use ssl (careful)', action='store_true') +arg_parser.add_argument('--ussd-port', dest='ussd_port', type=str, + help="port to ussd app responsible for processing ussd requests.") +arg_parser.add_argument('-v', help='be verbose', action='store_true') +arg_parser.add_argument('-vv', help='be more verbose', action='store_true') +arg_parser.add_argument('import_dir', default='out', type=str, help='user export directory') +args = arg_parser.parse_args() -argparser = argparse.ArgumentParser() -argparser.add_argument('-c', type=str, default=default_config_dir, help='config file') -argparser.add_argument('-i', '--chain-spec', dest='i', type=str, help='Chain specification string') -argparser.add_argument('--redis-host', dest='redis_host', type=str, help='redis host to use for task submission') -argparser.add_argument('--redis-port', dest='redis_port', type=int, help='redis host to use for task submission') -argparser.add_argument('--redis-db', dest='redis_db', type=int, help='redis db to use for task submission and callback') -argparser.add_argument('--batch-size', dest='batch_size', default=100, type=int, - help='burst size of sending transactions to node') # batch size should be slightly below cumulative gas limit worth, eg 80000 gas txs with 8000000 limit is a bit less than 100 batch size -argparser.add_argument('--batch-delay', dest='batch_delay', default=3, type=int, help='seconds delay between batches') -argparser.add_argument('--timeout', default=60.0, type=float, help='Callback timeout') -argparser.add_argument('--ussd-host', dest='ussd_host', type=str, - help="host to ussd app responsible for processing ussd requests.") -argparser.add_argument('--ussd-port', dest='ussd_port', type=str, - help="port to ussd app responsible for processing ussd requests.") -argparser.add_argument('--ussd-no-ssl', dest='ussd_no_ssl', help='do not use ssl (careful)', action='store_true') -argparser.add_argument('-q', type=str, default='cic-eth', help='Task queue') -argparser.add_argument('-v', action='store_true', help='Be verbose') -argparser.add_argument('-vv', action='store_true', help='Be more verbose') -argparser.add_argument('user_dir', type=str, help='path to users export dir tree') -args = argparser.parse_args() +if args.vv: + logging.getLogger().setLevel(logging.DEBUG) +elif args.v: + logging.getLogger().setLevel(logging.INFO) -if args.v: - logg.setLevel(logging.INFO) -elif args.vv: - logg.setLevel(logging.DEBUG) - -config_dir = args.c -config = confini.Config(config_dir, os.environ.get('CONFINI_ENV_PREFIX')) +config = Config(args.c, args.env_prefix) config.process() args_override = { 'CIC_CHAIN_SPEC': getattr(args, 'i'), @@ -60,44 +65,29 @@ args_override = { 'REDIS_PORT': getattr(args, 'redis_port'), 'REDIS_DB': getattr(args, 'redis_db'), } -config.dict_override(args_override, 'cli') -logg.debug('config loaded from {}:\n{}'.format(args.c, config)) +config.dict_override(args_override, 'cli flag') +config.censor('PASSWORD', 'DATABASE') +config.censor('PASSWORD', 'SSL') +logg.debug(f'config loaded from {args.c}:\n{config}') -celery_app = celery.Celery(broker=config.get('CELERY_BROKER_URL'), backend=config.get('CELERY_RESULT_URL')) -get_celery_worker_status(celery_app=celery_app) +old_account_dir = os.path.join(args.import_dir, 'old') +os.stat(old_account_dir) +logg.debug(f'created old system data dir: {old_account_dir}') -redis_host = config.get('REDIS_HOST') -redis_port = config.get('REDIS_PORT') -redis_db = config.get('REDIS_DB') -r = redis.Redis(redis_host, redis_port, redis_db) +new_account_dir = os.path.join(args.import_dir, 'new') +os.makedirs(new_account_dir, exist_ok=True) +logg.debug(f'created new system data dir: {new_account_dir}') -ps = r.pubsub() +person_metadata_dir = os.path.join(args.import_dir, 'meta') +os.makedirs(person_metadata_dir, exist_ok=True) +logg.debug(f'created person metadata dir: {person_metadata_dir}') -user_new_dir = os.path.join(args.user_dir, 'new') -os.makedirs(user_new_dir, exist_ok=True) - -ussd_data_dir = os.path.join(args.user_dir, 'ussd') -os.makedirs(ussd_data_dir, exist_ok=True) - -preferences_dir = os.path.join(args.user_dir, 'preferences') +preferences_dir = os.path.join(args.import_dir, 'preferences') os.makedirs(os.path.join(preferences_dir, 'meta'), exist_ok=True) +logg.debug(f'created preferences metadata dir: {preferences_dir}') -meta_dir = os.path.join(args.user_dir, 'meta') -os.makedirs(meta_dir, exist_ok=True) +valid_service_codes = config.get('USSD_SERVICE_CODE').split(",") -user_old_dir = os.path.join(args.user_dir, 'old') -os.stat(user_old_dir) - -txs_dir = os.path.join(args.user_dir, 'txs') -os.makedirs(txs_dir, exist_ok=True) - -chain_spec = ChainSpec.from_chain_str(config.get('CIC_CHAIN_SPEC')) -chain_str = str(chain_spec) - -batch_size = args.batch_size -batch_delay = args.batch_delay -ussd_port = args.ussd_port -ussd_host = args.ussd_host ussd_no_ssl = args.ussd_no_ssl if ussd_no_ssl is True: ussd_ssl = False @@ -105,7 +95,17 @@ else: ussd_ssl = True -def build_ussd_request(phone, host, port, service_code, username, password, ssl=False): +celery_app = celery.Celery(broker=config.get('CELERY_BROKER_URL'), backend=config.get('CELERY_RESULT_URL')) +get_celery_worker_status(celery_app) + + +def build_ussd_request(host: str, + password: str, + phone_number: str, + port: str, + service_code: str, + username: str, + ssl: bool = False): url = 'http' if ssl: url += 's' @@ -115,16 +115,16 @@ def build_ussd_request(phone, host, port, service_code, username, password, ssl= url += '/?username={}&password={}'.format(username, password) logg.info('ussd service url {}'.format(url)) - logg.info('ussd phone {}'.format(phone)) + logg.info('ussd phone {}'.format(phone_number)) session = uuid.uuid4().hex data = { 'sessionId': session, 'serviceCode': service_code, - 'phoneNumber': phone, + 'phoneNumber': phone_number, 'text': service_code, } - req = urllib.request.Request(url) + req = request.Request(url) req.method = 'POST' data_str = urlencode(data) data_bytes = data_str.encode('utf-8') @@ -134,85 +134,77 @@ def build_ussd_request(phone, host, port, service_code, username, password, ssl= return req -def register_ussd(i, u): - phone_object = phonenumbers.parse(u.tel) - phone = phonenumbers.format_number(phone_object, phonenumbers.PhoneNumberFormat.E164) - logg.debug('tel {} {}'.format(u.tel, phone)) - req = build_ussd_request( - phone, - ussd_host, - ussd_port, - config.get('APP_SERVICE_CODE'), - '', - '', - ussd_ssl - ) - response = urllib.request.urlopen(req) +def e164_phone_number(phone_number: str): + phone_object = phonenumbers.parse(phone_number) + return phonenumbers.format_number(phone_object, phonenumbers.PhoneNumberFormat.E164) + + +def register_account(person: Person): + phone_number = e164_phone_number(person.tel) + logg.debug(f'tel: {phone_number}') + req = build_ussd_request(args.ussd_host, + '', + phone_number, + args.ussd_port, + valid_service_codes[0], + '', + ussd_ssl) + response = request.urlopen(req) response_data = response.read().decode('utf-8') - state = response_data[:3] - out = response_data[4:] - logg.debug('ussd reponse: {}'.format(out)) + logg.debug(f'ussd response: {response_data[4:]}') if __name__ == '__main__': - i = 0 j = 0 - for x in os.walk(user_old_dir): + for x in os.walk(old_account_dir): for y in x[2]: if y[len(y) - 5:] != '.json': continue - # handle json containing person object - filepath = os.path.join(x[0], y) - f = open(filepath, 'r') - try: - o = json.load(f) - except json.decoder.JSONDecodeError as e: - f.close() - logg.error('load error for {}: {}'.format(y, e)) - continue - f.close() - u = Person.deserialize(o) - register_ussd(i, u) - - phone_object = phonenumbers.parse(u.tel) - phone = phonenumbers.format_number(phone_object, phonenumbers.PhoneNumberFormat.E164) - - s_phone = celery.signature( - 'import_task.resolve_phone', - [ - phone, - ], - queue='cic-import-ussd', + file_path = os.path.join(x[0], y) + with open(file_path, 'r') as account_file: + try: + account_data = json.load(account_file) + except json.decoder.JSONDecodeError as e: + logg.error('load error for {}: {}'.format(y, e)) + continue + person = Person.deserialize(account_data) + register_account(person) + phone_number = e164_phone_number(person.tel) + s_resolve_phone = celery.signature( + 'import_task.resolve_phone', [phone_number], queue=args.q ) - s_meta = celery.signature( - 'import_task.generate_metadata', - [ - phone, - ], - queue='cic-import-ussd', + s_person_metadata = celery.signature( + 'import_task.generate_person_metadata', [phone_number], queue=args.q ) - s_balance = celery.signature( - 'import_task.opening_balance_tx', - [ - phone, - i, - ], - queue='cic-import-ussd', + s_ussd_data = celery.signature( + 'import_task.generate_ussd_data', [phone_number], queue=args.q ) - s_meta.link(s_balance) - s_phone.link(s_meta) - # block time plus a bit of time for ussd processing - s_phone.apply_async(countdown=7) + s_preferences_metadata = celery.signature( + 'import_task.generate_preferences_data', [], queue=args.q + ) + + s_pins_data = celery.signature( + 'import_task.generate_pins_data', [phone_number], queue=args.q + ) + + s_opening_balance = celery.signature( + 'import_task.opening_balance_tx', [phone_number, i], queue=args.q + ) + celery.chain(s_resolve_phone, + s_person_metadata, + s_ussd_data, + s_preferences_metadata, + s_pins_data, + s_opening_balance).apply_async(countdown=7) i += 1 - sys.stdout.write('imported {} {}'.format(i, u).ljust(200) + "\r") - + sys.stdout.write('imported: {} {}'.format(i, person).ljust(200) + "\r\n") j += 1 - if j == batch_size: - time.sleep(batch_delay) + if j == args.batch_size: + time.sleep(args.batch_delay) j = 0 diff --git a/apps/data-seeding/cic_ussd/import_ussd_data.py b/apps/data-seeding/cic_ussd/import_ussd_data.py index 44f06be9..ecfab8c8 100644 --- a/apps/data-seeding/cic_ussd/import_ussd_data.py +++ b/apps/data-seeding/cic_ussd/import_ussd_data.py @@ -1,67 +1,67 @@ # standard imports import argparse -import json +import csv import logging import os +import psycopg2 # external imports -import celery from confini import Config # local imports + +default_config_dir = './config' logging.basicConfig(level=logging.WARNING) logg = logging.getLogger() -default_config_dir = '/usr/local/etc/cic' +arg_parser = argparse.ArgumentParser(description='Pins import script.') +arg_parser.add_argument('-c', type=str, default=default_config_dir, help='config root to use.') +arg_parser.add_argument('--env-prefix', + default=os.environ.get('CONFINI_ENV_PREFIX'), + dest='env_prefix', + type=str, + help='environment prefix for variables to overwrite configuration.') +arg_parser.add_argument('import_dir', default='out', type=str, help='user export directory') +arg_parser.add_argument('-v', help='be verbose', action='store_true') +arg_parser.add_argument('-vv', help='be more verbose', action='store_true') -arg_parser = argparse.ArgumentParser() -arg_parser.add_argument('-c', type=str, default=default_config_dir, help='config file') -arg_parser.add_argument('-q', type=str, default='cic-import-ussd', help='Task queue') -arg_parser.add_argument('-v', action='store_true', help='Be verbose') -arg_parser.add_argument('-vv', action='store_true', help='Be more verbose') -arg_parser.add_argument('user_dir', type=str, help='path to users export dir tree') args = arg_parser.parse_args() -if args.v: - logg.setLevel(logging.INFO) -elif args.vv: - logg.setLevel(logging.DEBUG) +if args.vv: + logging.getLogger().setLevel(logging.DEBUG) +elif args.v: + logging.getLogger().setLevel(logging.INFO) -config_dir = args.c -config = Config(config_dir, os.environ.get('CONFINI_ENV_PREFIX')) +config = Config(args.c, args.env_prefix) config.process() -logg.debug('config loaded from {}:\n{}'.format(args.c, config)) +config.censor('PASSWORD', 'DATABASE') +logg.debug(f'config loaded from {args.c}:\n{config}') -ussd_data_dir = os.path.join(args.user_dir, 'ussd') -db_configs = { - 'database': config.get('DATABASE_NAME'), - 'host': config.get('DATABASE_HOST'), - 'port': config.get('DATABASE_PORT'), - 'user': config.get('DATABASE_USER'), - 'password': config.get('DATABASE_PASSWORD') -} -celery_app = celery.Celery(broker=config.get('CELERY_BROKER_URL'), backend=config.get('CELERY_RESULT_URL')) +def main(): + with open(f'{args.import_dir}/ussd_data.csv') as ussd_data_file: + ussd_data = [tuple(row) for row in csv.reader(ussd_data_file)] + + db_conn = psycopg2.connect( + database=config.get('DATABASE_NAME'), + host=config.get('DATABASE_HOST'), + port=config.get('DATABASE_PORT'), + user=config.get('DATABASE_USER'), + password=config.get('DATABASE_PASSWORD') + ) + db_cursor = db_conn.cursor() + sql = 'UPDATE account SET status = %s, preferred_language = %s WHERE phone_number = %s' + for element in ussd_data: + status = 2 if int(element[1]) == 1 else 1 + preferred_language = element[2] + phone_number = element[0] + db_cursor.execute(sql, (status, preferred_language, phone_number)) + logg.debug(f'Updating account:{phone_number} with: preferred language: {preferred_language} status: {status}.') + db_conn.commit() + db_cursor.close() + db_conn.close() + if __name__ == '__main__': - for x in os.walk(ussd_data_dir): - for y in x[2]: - - if y[len(y) - 5:] == '.json': - filepath = os.path.join(x[0], y) - f = open(filepath, 'r') - try: - ussd_data = json.load(f) - logg.debug(f'LOADING USSD DATA: {ussd_data}') - except json.decoder.JSONDecodeError as e: - f.close() - logg.error('load error for {}: {}'.format(y, e)) - continue - f.close() - - s_set_ussd_data = celery.signature( - 'import_task.set_ussd_data', - [db_configs, ussd_data] - ) - s_set_ussd_data.apply_async(queue='cic-import-ussd') + main() diff --git a/apps/data-seeding/config/app.ini b/apps/data-seeding/config/app.ini index 82cd4f1d..d47c406d 100644 --- a/apps/data-seeding/config/app.ini +++ b/apps/data-seeding/config/app.ini @@ -1,27 +1,4 @@ [app] -ALLOWED_IP=0.0.0.0/0 -LOCALE_FALLBACK=en -LOCALE_PATH=/usr/src/cic-ussd/var/lib/locale/ -MAX_BODY_LENGTH=1024 -PASSWORD_PEPPER=QYbzKff6NhiQzY3ygl2BkiKOpER8RE/Upqs/5aZWW+I= -SERVICE_CODE=*483*46# - -[phone_number] -REGION=KE - -[ussd] -MENU_FILE=/usr/src/data/ussd_menu.json -user = -pass = - -[statemachine] -STATES=/usr/src/cic-ussd/states/ -TRANSITIONS=/usr/src/cic-ussd/transitions/ - -[client] -host = -port = -ssl = - -[keystore] -file_path = keystore/UTC--2021-01-08T17-18-44.521011372Z--eb3907ecad74a0013c259d5874ae7f22dcbcc95c +allowed_ip=0.0.0.0/0 +max_body_length=1024 +password_pepper= diff --git a/apps/data-seeding/config/database.ini b/apps/data-seeding/config/database.ini index f464cf44..649dd17c 100644 --- a/apps/data-seeding/config/database.ini +++ b/apps/data-seeding/config/database.ini @@ -1,10 +1,10 @@ [database] -NAME=sempo -USER=postgres -PASSWORD= -HOST=localhost -PORT=5432 -ENGINE=postgresql -DRIVER=psycopg2 -DEBUG=0 -POOL_SIZE=1 +name=cic_ussd +user=postgres +password= +host=localhost +port=5432 +engine=postgresql +driver=psycopg2 +debug=0 +pool_size=1 diff --git a/apps/data-seeding/config/ussd.ini b/apps/data-seeding/config/ussd.ini new file mode 100644 index 00000000..d996d16d --- /dev/null +++ b/apps/data-seeding/config/ussd.ini @@ -0,0 +1,5 @@ +[ussd] +menu_file=data/ussd_menu.json +service_code=*483*46#,*483*061#,*384*96# +user = +pass = diff --git a/apps/data-seeding/create_import_pins.py b/apps/data-seeding/create_import_pins.py deleted file mode 100644 index d4dd9716..00000000 --- a/apps/data-seeding/create_import_pins.py +++ /dev/null @@ -1,91 +0,0 @@ -# standard imports -import argparse -import json -import logging -import os -import uuid - -# third-party imports -import bcrypt -import celery -import confini -import phonenumbers -import random -from cic_types.models.person import Person -from cryptography.fernet import Fernet - -# local imports - - -logging.basicConfig(level=logging.WARNING) -logg = logging.getLogger() - -script_dir = os.path.realpath(os.path.dirname(__file__)) -default_config_dir = os.environ.get('CONFINI_DIR', os.path.join(script_dir, 'config')) - -arg_parser = argparse.ArgumentParser() -arg_parser.add_argument('-c', type=str, default=default_config_dir, help='Config dir') -arg_parser.add_argument('-v', action='store_true', help='Be verbose') -arg_parser.add_argument('-vv', action='store_true', help='Be more verbose') -arg_parser.add_argument('--userdir', type=str, help='path to users export dir tree') -arg_parser.add_argument('pins_dir', type=str, help='path to pin export dir tree') - - -args = arg_parser.parse_args() - -if args.v: - logg.setLevel(logging.INFO) -elif args.vv: - logg.setLevel(logging.DEBUG) - -config = confini.Config(args.c, os.environ.get('CONFINI_ENV_PREFIX')) -config.process() -logg.info('loaded config\n{}'.format(config)) - -celery_app = celery.Celery(broker=config.get('CELERY_BROKER_URL'), backend=config.get('CELERY_RESULT_URL')) - -user_dir = args.userdir -pins_dir = args.pins_dir - - -def generate_password_hash(): - key = Fernet.generate_key() - fnt = Fernet(key) - pin = str(random.randint(1000, 9999)) - return fnt.encrypt(bcrypt.hashpw(pin.encode('utf-8'), bcrypt.gensalt())).decode() - - -user_old_dir = os.path.join(user_dir, 'old') -logg.debug(f'reading user data from: {user_old_dir}') - -pins_file = open(f'{pins_dir}/pins.csv', 'w') - -if __name__ == '__main__': - - for x in os.walk(user_old_dir): - for y in x[2]: - # skip non-json files - if y[len(y) - 5:] != '.json': - continue - - # define file path for - filepath = None - if y[:15] != '_ussd_data.json': - filepath = os.path.join(x[0], y) - f = open(filepath, 'r') - try: - o = json.load(f) - except json.decoder.JSONDecodeError as e: - f.close() - logg.error('load error for {}: {}'.format(y, e)) - continue - f.close() - u = Person.deserialize(o) - - phone_object = phonenumbers.parse(u.tel) - phone = phonenumbers.format_number(phone_object, phonenumbers.PhoneNumberFormat.E164) - password_hash = uuid.uuid4().hex - pins_file.write(f'{phone},{password_hash}\n') - logg.info(f'Writing phone: {phone}, password_hash: {password_hash}') - - pins_file.close() diff --git a/apps/data-seeding/import_ussd.sh b/apps/data-seeding/import_ussd.sh new file mode 100644 index 00000000..51726c61 --- /dev/null +++ b/apps/data-seeding/import_ussd.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +echo "Creating seed data..." +python create_import_users.py -vv --dir "$IMPORT_DIR" "$ACCOUNT_COUNT" +wait $! +echo "Purge tasks from celery worker" +celery -A cic_ussd.import_task purge -Q "$CELERY_QUEUE" --broker redis://"$REDIS_HOST":"$REDIS_PORT" -f +echo "Start celery work and import balance job" +if [ "$INCLUDE_BALANCES" != "y" ] +then + echo "Running worker without opening balance transactions" + TARGET_TX_COUNT=$ACCOUNT_COUNT + python cic_ussd/import_balance.py -vv -c "$CONFIG" -p "$ETH_PROVIDER" -r "$CIC_REGISTRY_ADDRESS" --token-symbol "$TOKEN_SYMBOL" -y "$KEYSTORE_PATH" "$IMPORT_DIR" & +else + echo "Running worker with opening balance transactions" + TARGET_TX_COUNT=$((ACCOUNT_COUNT*2)) + python cic_ussd/import_balance.py -vv -c "$CONFIG" -p "$ETH_PROVIDER" -r "$CIC_REGISTRY_ADDRESS" --include-balances --token-symbol "$TOKEN_SYMBOL" -y "$KEYSTORE_PATH" "$IMPORT_DIR" & +fi +until [ -f ./cic-import-ussd.pid ] +do + echo "Polling for celery worker pid file..." +done +IMPORT_BALANCE_JOB=$( Date: Mon, 30 Aug 2021 10:14:22 -0700 Subject: [PATCH 3/5] update readme --- README.md | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 1c63bd95..a41e0026 100644 --- a/README.md +++ b/README.md @@ -2,25 +2,21 @@ ## Getting started -## Make some keys +This repo uses docker-compose and docker buildkit. Set the following environment variables to get started: ``` -docker build -t bloxie . && docker run -v "$(pwd)/keys:/root/keys" --rm -it -t bloxie account new --chain /root/bloxberg.json --keys-path /root/keys +export COMPOSE_DOCKER_CLI_BUILD=1 +export DOCKER_BUILDKIT=1 ``` - -### Prepare the repo - -This is stuff we need to put in makefile but for now... - -File mounts and permisssions need to be set +start services, database, redis and local ethereum node ``` -chmod -R 755 scripts/initdb apps/cic-meta/scripts/initdb -```` - -start cluster +docker-compose up -d ``` -docker-compose up + +Run app/contract-migration to deploy contracts +``` +RUN_MASK=3 docker-compose up contract-migration ``` stop cluster @@ -28,7 +24,7 @@ stop cluster docker-compose down ``` -delete data +stop cluster and delete data ``` docker-compose down -v ``` @@ -38,5 +34,4 @@ rebuild an images docker-compose up --build ``` -Deployment variables are writtend to service-configs/.env after everthing is up. From eb5e6121052a5da34498ac7f970c2883d5d6c8d6 Mon Sep 17 00:00:00 2001 From: Blair Vanderlugt Date: Mon, 30 Aug 2021 11:09:47 -0700 Subject: [PATCH 4/5] minor update to import_ussd script --- apps/data-seeding/import_ussd.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/data-seeding/import_ussd.sh b/apps/data-seeding/import_ussd.sh index 51726c61..02773481 100644 --- a/apps/data-seeding/import_ussd.sh +++ b/apps/data-seeding/import_ussd.sh @@ -1,4 +1,7 @@ #!/usr/bin/env bash + +set -e + echo "Creating seed data..." python create_import_users.py -vv --dir "$IMPORT_DIR" "$ACCOUNT_COUNT" wait $! @@ -15,9 +18,11 @@ else TARGET_TX_COUNT=$((ACCOUNT_COUNT*2)) python cic_ussd/import_balance.py -vv -c "$CONFIG" -p "$ETH_PROVIDER" -r "$CIC_REGISTRY_ADDRESS" --include-balances --token-symbol "$TOKEN_SYMBOL" -y "$KEYSTORE_PATH" "$IMPORT_DIR" & fi + until [ -f ./cic-import-ussd.pid ] do echo "Polling for celery worker pid file..." + sleep 1 done IMPORT_BALANCE_JOB=$( Date: Tue, 31 Aug 2021 11:43:01 -0700 Subject: [PATCH 5/5] fix data seeding node installs --- apps/data-seeding/.dockerignore | 2 +- apps/data-seeding/docker/Dockerfile | 4 +- apps/data-seeding/package-lock.json | 2216 ++++++++++++++++++++++++++- 3 files changed, 2209 insertions(+), 13 deletions(-) diff --git a/apps/data-seeding/.dockerignore b/apps/data-seeding/.dockerignore index 8e2ae51e..a3c84504 100644 --- a/apps/data-seeding/.dockerignore +++ b/apps/data-seeding/.dockerignore @@ -2,7 +2,7 @@ .cache .dot **/doc -**/node_modules +node_modules/ **/venv **/.venv diff --git a/apps/data-seeding/docker/Dockerfile b/apps/data-seeding/docker/Dockerfile index 2d032960..4113c2d1 100644 --- a/apps/data-seeding/docker/Dockerfile +++ b/apps/data-seeding/docker/Dockerfile @@ -9,7 +9,9 @@ COPY package.json \ package-lock.json \ . -RUN --mount=type=cache,mode=0755,target=/root/node_modules npm install + +RUN npm ci --production +#RUN --mount=type=cache,mode=0755,target=/root/node_modules npm install COPY requirements.txt . diff --git a/apps/data-seeding/package-lock.json b/apps/data-seeding/package-lock.json index fed07a65..7f341791 100644 --- a/apps/data-seeding/package-lock.json +++ b/apps/data-seeding/package-lock.json @@ -1,6 +1,2198 @@ { + "name": "data-seeding", + "lockfileVersion": 2, "requires": true, - "lockfileVersion": 1, + "packages": { + "": { + "dependencies": { + "@cicnet/cic-client-meta": "^0.0.11", + "@cicnet/crdt-meta": "^0.0.12", + "vcard-parser": "^1.0.0" + } + }, + "node_modules/@cicnet/cic-client-meta": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/@cicnet/cic-client-meta/-/cic-client-meta-0.0.11.tgz", + "integrity": "sha512-RL9CPXkWQBQzaqMoldnENC/xqinIMyWNSsejs9+qkFOWbAvC4inLdpjjCaooyvLpIZGHF9cLjxHiAdBMR9L8sQ==", + "dependencies": { + "@cicnet/crdt-meta": "^0.0.10", + "@ethereumjs/tx": "^3.0.0-beta.1", + "automerge": "^0.14.1", + "colors": "^1.4.0", + "ethereumjs-wallet": "^1.0.1", + "ini": "^1.3.8", + "openpgp": "^4.10.8", + "pg": "^8.4.2", + "sqlite3": "^5.0.0", + "yargs": "^16.1.0" + }, + "bin": { + "meta-get": "bin/get.js", + "meta-set": "bin/set.js" + }, + "engines": { + "node": ">=14.16.1" + } + }, + "node_modules/@cicnet/cic-client-meta/node_modules/@cicnet/crdt-meta": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@cicnet/crdt-meta/-/crdt-meta-0.0.10.tgz", + "integrity": "sha512-f+H6BQA2tE718KuNYiNzrDJN4wY00zeuhXM6aPKJUX6nryzX9g2r0yf8iDhkz+Fts1R6M7Riz73MfFEa8fgvsw==", + "dependencies": { + "automerge": "^0.14.2", + "ini": "^1.3.8", + "openpgp": "^4.10.8", + "readline-sync": "^1.4.10" + }, + "engines": { + "node": ">=14.16.1" + } + }, + "node_modules/@cicnet/crdt-meta": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@cicnet/crdt-meta/-/crdt-meta-0.0.12.tgz", + "integrity": "sha512-wPX86P1Lsq4RxkVUlhlouhLkMOtkqzHgpNuXicHvWuhH3ks2Nsg7yqvTw9yt+kqj+N8a5pPMrNhKvUEFW8rJjA==", + "dependencies": { + "automerge": "^0.14.2", + "ini": "^1.3.8", + "openpgp": "^4.10.8", + "readline-sync": "^1.4.10" + }, + "engines": { + "node": ">=14.16.1" + } + }, + "node_modules/@ethereumjs/common": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.4.0.tgz", + "integrity": "sha512-UdkhFWzWcJCZVsj1O/H8/oqj/0RVYjLc1OhPjBrQdALAkQHpCp8xXI4WLnuGTADqTdJZww0NtgwG+TRPkXt27w==", + "dependencies": { + "crc-32": "^1.2.0", + "ethereumjs-util": "^7.1.0" + } + }, + "node_modules/@ethereumjs/tx": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.3.0.tgz", + "integrity": "sha512-yTwEj2lVzSMgE6Hjw9Oa1DZks/nKTWM8Wn4ykDNapBPua2f4nXO3qKnni86O6lgDj5fVNRqbDsD0yy7/XNGDEA==", + "dependencies": { + "@ethereumjs/common": "^2.4.0", + "ethereumjs-util": "^7.1.0" + } + }, + "node_modules/@types/bn.js": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz", + "integrity": "sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "16.3.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.3.2.tgz", + "integrity": "sha512-jJs9ErFLP403I+hMLGnqDRWT0RYKSvArxuBVh2veudHV7ifEC1WAmjJADacZ7mRbA2nWgHtn8xyECMAot0SkAw==" + }, + "node_modules/@types/pbkdf2": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz", + "integrity": "sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/secp256k1": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.3.tgz", + "integrity": "sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "node_modules/aes-js": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.1.2.tgz", + "integrity": "sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ==" + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "optional": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "node_modules/are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "node_modules/are-we-there-yet/node_modules/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==", + "dependencies": { + "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" + } + }, + "node_modules/are-we-there-yet/node_modules/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==" + }, + "node_modules/are-we-there-yet/node_modules/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==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "optional": true, + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "optional": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "optional": true + }, + "node_modules/automerge": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/automerge/-/automerge-0.14.2.tgz", + "integrity": "sha512-shiwuJHCbNRI23WZyIECLV4Ovf3WiAFJ7P9BH4l5gON1In/UUbjcSJKRygtIirObw2UQumeYxp3F2XBdSvQHnA==", + "dependencies": { + "immutable": "^3.8.2", + "transit-immutable-js": "^0.7.0", + "transit-js": "^0.8.861", + "uuid": "^3.4.0" + } + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "optional": true, + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "optional": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base-x": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.8.tgz", + "integrity": "sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "optional": true, + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/blakejs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.1.tgz", + "integrity": "sha512-bLG6PHOCZJKNshTjGRBvET0vTciwQE6zFKOKKXPDJfwFBd4Ac0yBfPZqcGvGJap50l7ktvlpFqc2jGVaUgbJgg==" + }, + "node_modules/block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "optional": true, + "dependencies": { + "inherits": "~2.0.0" + }, + "engines": { + "node": "0.4 || >=0.5.8" + } + }, + "node_modules/bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=", + "dependencies": { + "base-x": "^3.0.2" + } + }, + "node_modules/bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "dependencies": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "optional": true + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/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==" + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "optional": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "node_modules/crc-32": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.0.tgz", + "integrity": "sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA==", + "dependencies": { + "exit-on-epipe": "~1.0.1", + "printj": "~1.1.0" + }, + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "optional": true, + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/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==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "optional": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "optional": true, + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/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==" + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "dependencies": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "node_modules/ethereumjs-util": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.0.tgz", + "integrity": "sha512-kR+vhu++mUDARrsMMhsjjzPduRVAeundLGXucGRHF3B4oEltOUspfgCVco4kckucj3FMlLaZHUl9n7/kdmr6Tw==", + "dependencies": { + "@types/bn.js": "^5.1.0", + "bn.js": "^5.1.2", + "create-hash": "^1.1.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/ethereumjs-wallet": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ethereumjs-wallet/-/ethereumjs-wallet-1.0.1.tgz", + "integrity": "sha512-3Z5g1hG1das0JWU6cQ9HWWTY2nt9nXCcwj7eXVNAHKbo00XAZO8+NHlwdgXDWrL0SXVQMvTWN8Q/82DRH/JhPw==", + "dependencies": { + "aes-js": "^3.1.1", + "bs58check": "^2.1.2", + "ethereum-cryptography": "^0.1.3", + "ethereumjs-util": "^7.0.2", + "randombytes": "^2.0.6", + "scrypt-js": "^3.0.1", + "utf8": "^3.0.0", + "uuid": "^3.3.2" + } + }, + "node_modules/ethjs-util": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", + "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", + "dependencies": { + "is-hex-prefixed": "1.0.0", + "strip-hex-prefix": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/exit-on-epipe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz", + "integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "optional": true + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "engines": [ + "node >=0.6.0" + ], + "optional": true + }, + "node_modules/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==", + "optional": true + }, + "node_modules/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==", + "optional": true + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "optional": true, + "engines": { + "node": "*" + } + }, + "node_modules/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==", + "optional": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "dependencies": { + "minipass": "^2.6.0" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "node_modules/fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "optional": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dependencies": { + "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" + } + }, + "node_modules/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==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "optional": true, + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "optional": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "optional": true, + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "optional": true, + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore-walk": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.4.tgz", + "integrity": "sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ==", + "dependencies": { + "minimatch": "^3.0.4" + } + }, + "node_modules/immutable": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", + "integrity": "sha1-wkOZUUVbs5kT2vKBN28VMOEErfM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "node_modules/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": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hex-prefixed": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", + "integrity": "sha1-fY035q135dEnFIkTxXPggtd39VQ=", + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "optional": true + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "optional": true + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "optional": true + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true + }, + "node_modules/json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "optional": true + }, + "node_modules/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==", + "optional": true + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "optional": true + }, + "node_modules/jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "engines": [ + "node >=0.6.0" + ], + "optional": true, + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "node_modules/keccak": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.1.tgz", + "integrity": "sha512-epq90L9jlFWCW7+pQa6JOnKn2Xgl2mtI664seYR6MHskvI9agt7AnDqmAlp9TqU4/caMYbA08Hi5DMZAl5zdkA==", + "hasInstallScript": true, + "dependencies": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/mime-db": { + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", + "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.31", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", + "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", + "optional": true, + "dependencies": { + "mime-db": "1.48.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "node_modules/minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "dependencies": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "node_modules/minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "dependencies": { + "minipass": "^2.9.0" + } + }, + "node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/needle": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.8.0.tgz", + "integrity": "sha512-ZTq6WYkN/3782H1393me3utVYdq2XyqNUFBsprEE3VMAT0+hP/cItpnITpqsY6ep2yeFE4Tqtqwc74VqUlUYtw==", + "dependencies": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" + }, + "node_modules/node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/node-gyp": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", + "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", + "optional": true, + "dependencies": { + "fstream": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "osenv": "0", + "request": "^2.87.0", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^2.0.0", + "which": "1" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/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==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-localstorage": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-localstorage/-/node-localstorage-1.3.1.tgz", + "integrity": "sha512-NMWCSWWc6JbHT5PyWlNT2i8r7PgGYXVntmKawY83k/M0UJScZ5jirb61TLnqKwd815DfBQu+lR3sRw08SPzIaQ==", + "dependencies": { + "write-file-atomic": "^1.1.4" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/node-pre-gyp": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz", + "integrity": "sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==", + "deprecated": "Please upgrade to @mapbox/node-pre-gyp: the non-scoped node-pre-gyp package is deprecated and only the @mapbox scoped package will recieve updates in the future", + "dependencies": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/node-pre-gyp/node_modules/nopt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", + "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", + "dependencies": { + "abbrev": "1", + "osenv": "^0.1.4" + }, + "bin": { + "nopt": "bin/nopt.js" + } + }, + "node_modules/node-pre-gyp/node_modules/tar": { + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", + "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", + "dependencies": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + }, + "engines": { + "node": ">=4.5" + } + }, + "node_modules/nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "optional": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + } + }, + "node_modules/npm-bundled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", + "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", + "dependencies": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" + }, + "node_modules/npm-packlist": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", + "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", + "dependencies": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "node_modules/npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dependencies": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "optional": true, + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/openpgp": { + "version": "4.10.10", + "resolved": "https://registry.npmjs.org/openpgp/-/openpgp-4.10.10.tgz", + "integrity": "sha512-Ub48OogGPjNsr0G/wnJ/SyAQzt/tfcXZTWVZdjKFpXCQV1Ca+upFdSPPkBlGG3lb9EQGOKZJ2tzYNH6ZyKMkDQ==", + "dependencies": { + "asn1.js": "^5.0.0", + "node-fetch": "^2.1.2", + "node-localstorage": "~1.3.0" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "dependencies": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "node_modules/packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "optional": true + }, + "node_modules/pg": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.6.0.tgz", + "integrity": "sha512-qNS9u61lqljTDFvmk/N66EeGq3n6Ujzj0FFyNMGQr6XuEv4tgNTXvJQTfJdcvGit5p5/DWPu+wj920hAJFI+QQ==", + "dependencies": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.5.0", + "pg-pool": "^3.3.0", + "pg-protocol": "^1.5.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "pg-native": ">=2.0.0" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-connection-string": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", + "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.3.0.tgz", + "integrity": "sha512-0O5huCql8/D6PIRFAlmccjphLYWC+JIzvUhSzXSpGaf+tjTZc4nn+Lr7mLXBbFJfvwbP0ywDv73EiaBsxn7zdg==", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz", + "integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.4.tgz", + "integrity": "sha512-YmuA56alyBq7M59vxVBfPJrGSozru8QAdoNlWuW3cz8l+UX3cWge0vTvjKhsSHSJpo3Bom8/Mm6hf0TR5GY0+w==", + "dependencies": { + "split2": "^3.1.1" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/printj": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz", + "integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==", + "bin": { + "printj": "bin/printj.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/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==" + }, + "node_modules/psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "optional": true + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "optional": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/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==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readline-sync": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/readline-sync/-/readline-sync-1.4.10.tgz", + "integrity": "sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "optional": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/rlp": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.6.tgz", + "integrity": "sha512-HAfAmL6SDYNWPUOJNrM500x4Thn4PZsEy5pijPh40U9WfNk0z15hUYzO9xVIMAdIHdFtD8CBDHd75Td1g36Mjg==", + "dependencies": { + "bn.js": "^4.11.1" + }, + "bin": { + "rlp": "bin/rlp" + } + }, + "node_modules/rlp/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "node_modules/scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" + }, + "node_modules/secp256k1": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.2.tgz", + "integrity": "sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg==", + "hasInstallScript": true, + "dependencies": { + "elliptic": "^6.5.2", + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + }, + "node_modules/slide": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=", + "engines": { + "node": "*" + } + }, + "node_modules/split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "dependencies": { + "readable-stream": "^3.0.0" + } + }, + "node_modules/sqlite3": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.0.2.tgz", + "integrity": "sha512-1SdTNo+BVU211Xj1csWa8lV6KM0CtucDwRyA0VHl91wEH1Mgh7RxUpI4rVvG7OhHrzCSGaVyW5g8vKvlrk9DJA==", + "hasInstallScript": true, + "dependencies": { + "node-addon-api": "^3.0.0", + "node-pre-gyp": "^0.11.0" + }, + "optionalDependencies": { + "node-gyp": "3.x" + }, + "peerDependencies": { + "node-gyp": "3.x" + }, + "peerDependenciesMeta": { + "node-gyp": { + "optional": true + } + } + }, + "node_modules/sqlite3/node_modules/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_modules/sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "optional": true, + "dependencies": { + "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" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-hex-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", + "integrity": "sha1-DF8VX+8RUTczd96du1iNoFUA428=", + "dependencies": { + "is-hex-prefixed": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tar": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", + "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", + "deprecated": "This version of tar is no longer supported, and will not receive security updates. Please upgrade asap.", + "optional": true, + "dependencies": { + "block-stream": "*", + "fstream": "^1.0.12", + "inherits": "2" + } + }, + "node_modules/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==", + "optional": true, + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/transit-immutable-js": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/transit-immutable-js/-/transit-immutable-js-0.7.0.tgz", + "integrity": "sha1-mT4lCJtjEf9AIUD1VidtbSUwBdk=", + "peerDependencies": { + "immutable": ">= 3", + "transit-js": ">= 0.8" + } + }, + "node_modules/transit-js": { + "version": "0.8.874", + "resolved": "https://registry.npmjs.org/transit-js/-/transit-js-0.8.874.tgz", + "integrity": "sha512-IDJJGKRzUbJHmN0P15HBBa05nbKor3r2MmG6aSt0UxXIlJZZKcddTk67/U7WyAeW9Hv/VYI02IqLzolsC4sbPA==", + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "optional": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "optional": true + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "optional": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", + "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/vcard-parser": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/vcard-parser/-/vcard-parser-1.0.0.tgz", + "integrity": "sha512-rSEjrjBK3of4VimMR5vBjLLcN5ZCSp9yuVzyx5i4Fwx74Yd0s+DnHtSit/wAAtj1a7/T/qQc0ykwXADoD0+fTQ==" + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "engines": [ + "node >=0.6.0" + ], + "optional": true, + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "optional": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dependencies": { + "string-width": "^1.0.2 || 2" + } + }, + "node_modules/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==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "node_modules/write-file-atomic": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz", + "integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=", + "dependencies": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "slide": "^1.1.5" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dependencies": { + "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" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/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==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + } + }, "dependencies": { "@cicnet/cic-client-meta": { "version": "0.0.11", @@ -1243,7 +3435,8 @@ "pg-pool": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.3.0.tgz", - "integrity": "sha512-0O5huCql8/D6PIRFAlmccjphLYWC+JIzvUhSzXSpGaf+tjTZc4nn+Lr7mLXBbFJfvwbP0ywDv73EiaBsxn7zdg==" + "integrity": "sha512-0O5huCql8/D6PIRFAlmccjphLYWC+JIzvUhSzXSpGaf+tjTZc4nn+Lr7mLXBbFJfvwbP0ywDv73EiaBsxn7zdg==", + "requires": {} }, "pg-protocol": { "version": "1.5.0", @@ -1526,6 +3719,14 @@ "tweetnacl": "~0.14.0" } }, + "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" + } + }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", @@ -1536,14 +3737,6 @@ "strip-ansi": "^3.0.0" } }, - "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": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", @@ -1589,7 +3782,8 @@ "transit-immutable-js": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/transit-immutable-js/-/transit-immutable-js-0.7.0.tgz", - "integrity": "sha1-mT4lCJtjEf9AIUD1VidtbSUwBdk=" + "integrity": "sha1-mT4lCJtjEf9AIUD1VidtbSUwBdk=", + "requires": {} }, "transit-js": { "version": "0.8.874",