Compare commits

..

2 Commits

Author SHA1 Message Date
nolash
5ac0a5b39a Rehabilitate demurrage token deploy in contract miration 2021-08-17 13:29:17 +02:00
nolash
80a3ecd958 Use local config setup for migrations in cic-eth 2021-08-17 11:45:24 +02:00
151 changed files with 2014 additions and 4082 deletions

1
.gitignore vendored
View File

@@ -14,4 +14,3 @@ build/
**/.venv **/.venv
.idea .idea
**/.vim **/.vim
**/*secret.yaml

View File

@@ -1,43 +1,14 @@
include: include:
#- local: 'ci_templates/.cic-template.yml' #kaniko build templates - local: 'ci_templates/.cic-template.yml'
# these includes are app specific unit tests - local: 'apps/contract-migration/.gitlab-ci.yml'
- local: 'apps/cic-eth/.gitlab-ci.yml' - local: 'apps/cic-eth/.gitlab-ci.yml'
- local: 'apps/cic-ussd/.gitlab-ci.yml' - local: 'apps/cic-ussd/.gitlab-ci.yml'
- local: 'apps/cic-notify/.gitlab-ci.yml' - local: 'apps/cic-notify/.gitlab-ci.yml'
- local: 'apps/cic-meta/.gitlab-ci.yml' - local: 'apps/cic-meta/.gitlab-ci.yml'
- local: 'apps/cic-cache/.gitlab-ci.yml' - local: 'apps/cic-cache/.gitlab-ci.yml'
#- local: 'apps/contract-migration/.gitlab-ci.yml' - local: 'apps/data-seeding/.gitlab-ci.yml'
#- local: 'apps/data-seeding/.gitlab-ci.yml'
stages: stages:
- build - build
- test - test
- deploy - release
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
script:
- TAG=latest sh ./scripts/build-push.sh
rules:
- if: $CI_COMMIT_REF_PROTECTED == "true"
when: always
deploy-dev:
stage: deploy
trigger: grassrootseconomics/devops
when: manual

View File

@@ -2,21 +2,25 @@
## Getting started ## Getting started
This repo uses docker-compose and docker buildkit. Set the following environment variables to get started: ## Make some keys
``` ```
export COMPOSE_DOCKER_CLI_BUILD=1 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 DOCKER_BUILDKIT=1
``` ```
start services, database, redis and local ethereum node
```
docker-compose up -d
```
Run app/contract-migration to deploy contracts ### Prepare the repo
This is stuff we need to put in makefile but for now...
File mounts and permisssions need to be set
``` ```
RUN_MASK=3 docker-compose up contract-migration chmod -R 755 scripts/initdb apps/cic-meta/scripts/initdb
````
start cluster
```
docker-compose up
``` ```
stop cluster stop cluster
@@ -24,9 +28,9 @@ stop cluster
docker-compose down docker-compose down
``` ```
stop cluster and delete data delete data
``` ```
docker-compose down -v --remove-orphans docker-compose down -v
``` ```
rebuild an images rebuild an images
@@ -34,7 +38,5 @@ rebuild an images
docker-compose up --build <service_name> docker-compose up --build <service_name>
``` ```
to delete the buildkit cache Deployment variables are writtend to service-configs/.env after everthing is up.
```
docker builder prune --filter type=exec.cachemount
```

View File

@@ -0,0 +1,34 @@
# 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 <dev@holbrook.no> 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

View File

@@ -0,0 +1 @@
## this is an example base image if we wanted one for all the other apps. Its just OS level things

View File

@@ -1,17 +1,52 @@
build-test-cic-cache: .cic_cache_variables:
stage: test variables:
tags: APP_NAME: cic-cache
- integration DOCKERFILE_PATH: docker/Dockerfile_ci
variables: CONTEXT: apps/$APP_NAME
APP_NAME: cic-cache
MR_IMAGE_TAG: mr-$APP_NAME-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA build-mr-cic-cache:
script: extends:
- cd apps/cic-cache - .py_build_merge_request
- docker build -t $MR_IMAGE_TAG -f docker/Dockerfile . - .cic_cache_variables
- docker run $MR_IMAGE_TAG sh docker/run_tests.sh rules:
allow_failure: true - if: $CI_PIPELINE_SOURCE == "merge_request_event"
rules: changes:
- if: $CI_PIPELINE_SOURCE == "merge_request_event" - apps/cic-cache/**/*
changes: when: always
- apps/$APP_NAME/**/*
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

View File

@@ -1 +0,0 @@
# CIC-CACHE

View File

@@ -27,11 +27,11 @@ class RPC:
@staticmethod @staticmethod
def from_config(config): def from_config(config):
chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC')) chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
RPCConnection.register_location(config.get('RPC_PROVIDER'), chain_spec, 'default') RPCConnection.register_location(config.get('RPC_HTTP_PROVIDER'), chain_spec, 'default')
if config.get('SIGNER_PROVIDER'): if config.get('SIGNER_PROVIDER'):
RPCConnection.register_constructor(ConnType.UNIX, EthUnixSignerConnection, tag='signer') RPCConnection.register_constructor(ConnType.UNIX, EthUnixSignerConnection, tag='signer')
RPCConnection.register_location(config.get('SIGNER_PROVIDER'), chain_spec, 'signer') RPCConnection.register_location(config.get('SIGNER_PROVIDER'), chain_spec, 'signer')
rpc = RPC(chain_spec, config.get('RPC_PROVIDER'), signer_provider=config.get('SIGNER_PROVIDER')) rpc = RPC(chain_spec, config.get('RPC_HTTP_PROVIDER'), signer_provider=config.get('SIGNER_PROVIDER'))
logg.info('set up rpc: {}'.format(rpc)) logg.info('set up rpc: {}'.format(rpc))
return rpc return rpc

View File

@@ -5,7 +5,7 @@ version = (
0, 0,
2, 2,
1, 1,
'alpha.2', 'alpha.1',
) )
version_object = semver.VersionInfo( version_object = semver.VersionInfo(

View File

@@ -10,10 +10,9 @@ COPY requirements.txt .
ARG EXTRA_INDEX_URL="https://pip.grassrootseconomics.net:8433" ARG EXTRA_INDEX_URL="https://pip.grassrootseconomics.net:8433"
ARG GITLAB_PYTHON_REGISTRY="https://gitlab.com/api/v4/projects/27624814/packages/pypi/simple" 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 \ RUN --mount=type=cache,mode=0755,target=/root/.cache/pip \
pip install --index-url https://pypi.org/simple \ pip install --index-url https://pypi.org/simple \
--extra-index-url $GITLAB_PYTHON_REGISTRY --extra-index-url $EXTRA_INDEX_URL $EXTRA_PIP_ARGS \ --extra-index-url $GITLAB_PYTHON_REGISTRY --extra-index-url $EXTRA_INDEX_URL \
-r requirements.txt -r requirements.txt
COPY . . COPY . .

View File

@@ -0,0 +1,37 @@
# 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"
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
# 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 <dir> (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 []

View File

@@ -1,10 +0,0 @@
#! /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

View File

@@ -2,13 +2,16 @@ alembic==1.4.2
confini>=0.3.6rc4,<0.5.0 confini>=0.3.6rc4,<0.5.0
uwsgi==2.0.19.1 uwsgi==2.0.19.1
moolb~=0.1.1b2 moolb~=0.1.1b2
cic-eth-registry~=0.6.1a1 cic-eth-registry~=0.5.8a1
SQLAlchemy==1.3.20 SQLAlchemy==1.3.20
semver==2.13.0 semver==2.13.0
psycopg2==2.8.6 psycopg2==2.8.6
celery==4.4.7 celery==4.4.7
redis==3.5.3 redis==3.5.3
chainsyncer[sql]>=0.0.6a3,<0.1.0 chainsyncer[sql]>=0.0.6a1,<0.1.0
erc20-faucet>=0.3.2a2, <0.4.0 erc20-faucet>=0.2.4a2, <0.3.0
chainlib-eth>=0.0.9a14,<0.1.0 #chainlib-eth==0.0.7a5,<0.1.0
eth-address-index>=0.2.3a4,<0.3.0 chainlib-eth==0.0.7a5
#chainlib==0.0.7a4,<0.1.0
chainlib==0.0.7a4
eth-address-index>=0.1.4a1,<0.2.0

View File

@@ -6,5 +6,5 @@ sqlparse==0.4.1
pytest-celery==0.0.0a1 pytest-celery==0.0.0a1
eth_tester==0.5.0b3 eth_tester==0.5.0b3
py-evm==0.3.0a20 py-evm==0.3.0a20
sarafu-faucet~=0.0.7a1 sarafu-faucet~=0.0.5a2
erc20-transfer-authorization>=0.3.5a1,<0.4.0 erc20-transfer-authorization>=0.3.4a1,<0.4.0

View File

@@ -1,4 +1,5 @@
celery==4.4.7 celery==4.4.7
erc20-demurrage-token~=0.0.3a1 erc20-demurrage-token~=0.0.3a1
cic-eth-registry>=0.6.1a2,<0.7.0 cic-eth-registry~=0.5.8a1
cic-eth[services]~=0.12.4a8 chainlib~=0.0.7a1
cic_eth~=0.12.2a4

View File

@@ -1,16 +1,52 @@
build-test-cic-eth: .cic_eth_variables:
stage: test variables:
tags: APP_NAME: cic-eth
- integration DOCKERFILE_PATH: docker/Dockerfile_ci
variables: CONTEXT: apps/$APP_NAME
APP_NAME: cic-eth
MR_IMAGE_TAG: mr-$APP_NAME-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA build-mr-cic-eth:
script: extends:
- cd apps/cic-eth - .cic_eth_variables
- docker build -t $MR_IMAGE_TAG -f docker/Dockerfile . - .py_build_target_dev
- docker run $MR_IMAGE_TAG sh docker/run_tests.sh rules:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_PIPELINE_SOURCE == "merge_request_event"
changes: changes:
- apps/$APP_NAME/**/* - 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 when: always

View File

@@ -1,5 +1,5 @@
SQLAlchemy==1.3.20 SQLAlchemy==1.3.20
cic-eth-registry>=0.6.1a2,<0.7.0 cic-eth-registry>=0.5.6a2,<0.6.0
hexathon~=0.0.1a8 hexathon~=0.0.1a7
chainqueue>=0.0.4a6,<0.1.0 chainqueue>=0.0.3a1,<0.1.0
eth-erc20>=0.1.2a2,<0.2.0 eth-erc20>=0.0.10a3,<0.1.0

View File

@@ -4,6 +4,7 @@ import logging
# external imports # external imports
import celery import celery
from chainlib.eth.constant import ZERO_ADDRESS
from chainlib.chain import ChainSpec from chainlib.chain import ChainSpec
from hexathon import ( from hexathon import (
add_0x, add_0x,
@@ -19,17 +20,18 @@ from cic_eth.task import (
CriticalSQLAlchemyTask, CriticalSQLAlchemyTask,
) )
from cic_eth.error import LockedError from cic_eth.error import LockedError
from cic_eth.encode import (
tx_normalize,
ZERO_ADDRESS_NORMAL,
)
celery_app = celery.current_app celery_app = celery.current_app
logg = logging.getLogger() logg = logging.getLogger()
def normalize_address(a):
if a == None:
return None
return add_0x(hex_uniform(strip_0x(a)))
@celery_app.task(base=CriticalSQLAlchemyTask) @celery_app.task(base=CriticalSQLAlchemyTask)
def lock(chained_input, chain_spec_dict, address=ZERO_ADDRESS_NORMAL, flags=LockEnum.ALL, tx_hash=None): def lock(chained_input, chain_spec_dict, address=ZERO_ADDRESS, flags=LockEnum.ALL, tx_hash=None):
"""Task wrapper to set arbitrary locks """Task wrapper to set arbitrary locks
:param chain_str: Chain spec string representation :param chain_str: Chain spec string representation
@@ -41,7 +43,7 @@ def lock(chained_input, chain_spec_dict, address=ZERO_ADDRESS_NORMAL, flags=Lock
:returns: New lock state for address :returns: New lock state for address
:rtype: number :rtype: number
""" """
address = tx_normalize.wallet_address(address) address = normalize_address(address)
chain_str = '::' chain_str = '::'
if chain_spec_dict != None: if chain_spec_dict != None:
chain_str = str(ChainSpec.from_dict(chain_spec_dict)) chain_str = str(ChainSpec.from_dict(chain_spec_dict))
@@ -51,7 +53,7 @@ def lock(chained_input, chain_spec_dict, address=ZERO_ADDRESS_NORMAL, flags=Lock
@celery_app.task(base=CriticalSQLAlchemyTask) @celery_app.task(base=CriticalSQLAlchemyTask)
def unlock(chained_input, chain_spec_dict, address=ZERO_ADDRESS_NORMAL, flags=LockEnum.ALL): def unlock(chained_input, chain_spec_dict, address=ZERO_ADDRESS, flags=LockEnum.ALL):
"""Task wrapper to reset arbitrary locks """Task wrapper to reset arbitrary locks
:param chain_str: Chain spec string representation :param chain_str: Chain spec string representation
@@ -63,7 +65,7 @@ def unlock(chained_input, chain_spec_dict, address=ZERO_ADDRESS_NORMAL, flags=Lo
:returns: New lock state for address :returns: New lock state for address
:rtype: number :rtype: number
""" """
address = tx_normalize.wallet_address(address) address = normalize_address(address)
chain_str = '::' chain_str = '::'
if chain_spec_dict != None: if chain_spec_dict != None:
chain_str = str(ChainSpec.from_dict(chain_spec_dict)) chain_str = str(ChainSpec.from_dict(chain_spec_dict))
@@ -73,7 +75,7 @@ def unlock(chained_input, chain_spec_dict, address=ZERO_ADDRESS_NORMAL, flags=Lo
@celery_app.task(base=CriticalSQLAlchemyTask) @celery_app.task(base=CriticalSQLAlchemyTask)
def lock_send(chained_input, chain_spec_dict, address=ZERO_ADDRESS_NORMAL, tx_hash=None): def lock_send(chained_input, chain_spec_dict, address=ZERO_ADDRESS, tx_hash=None):
"""Task wrapper to set send lock """Task wrapper to set send lock
:param chain_str: Chain spec string representation :param chain_str: Chain spec string representation
@@ -83,7 +85,7 @@ def lock_send(chained_input, chain_spec_dict, address=ZERO_ADDRESS_NORMAL, tx_ha
:returns: New lock state for address :returns: New lock state for address
:rtype: number :rtype: number
""" """
address = tx_normalize.wallet_address(address) address = normalize_address(address)
chain_str = str(ChainSpec.from_dict(chain_spec_dict)) chain_str = str(ChainSpec.from_dict(chain_spec_dict))
r = Lock.set(chain_str, LockEnum.SEND, address=address, tx_hash=tx_hash) r = Lock.set(chain_str, LockEnum.SEND, address=address, tx_hash=tx_hash)
logg.debug('Send locked for {}, flag now {}'.format(address, r)) logg.debug('Send locked for {}, flag now {}'.format(address, r))
@@ -91,7 +93,7 @@ def lock_send(chained_input, chain_spec_dict, address=ZERO_ADDRESS_NORMAL, tx_ha
@celery_app.task(base=CriticalSQLAlchemyTask) @celery_app.task(base=CriticalSQLAlchemyTask)
def unlock_send(chained_input, chain_spec_dict, address=ZERO_ADDRESS_NORMAL): def unlock_send(chained_input, chain_spec_dict, address=ZERO_ADDRESS):
"""Task wrapper to reset send lock """Task wrapper to reset send lock
:param chain_str: Chain spec string representation :param chain_str: Chain spec string representation
@@ -101,7 +103,7 @@ def unlock_send(chained_input, chain_spec_dict, address=ZERO_ADDRESS_NORMAL):
:returns: New lock state for address :returns: New lock state for address
:rtype: number :rtype: number
""" """
address = tx_normalize.wallet_address(address) address = normalize_address(address)
chain_str = str(ChainSpec.from_dict(chain_spec_dict)) chain_str = str(ChainSpec.from_dict(chain_spec_dict))
r = Lock.reset(chain_str, LockEnum.SEND, address=address) r = Lock.reset(chain_str, LockEnum.SEND, address=address)
logg.debug('Send unlocked for {}, flag now {}'.format(address, r)) logg.debug('Send unlocked for {}, flag now {}'.format(address, r))
@@ -109,7 +111,7 @@ def unlock_send(chained_input, chain_spec_dict, address=ZERO_ADDRESS_NORMAL):
@celery_app.task(base=CriticalSQLAlchemyTask) @celery_app.task(base=CriticalSQLAlchemyTask)
def lock_queue(chained_input, chain_spec_dict, address=ZERO_ADDRESS_NORMAL, tx_hash=None): def lock_queue(chained_input, chain_spec_dict, address=ZERO_ADDRESS, tx_hash=None):
"""Task wrapper to set queue direct lock """Task wrapper to set queue direct lock
:param chain_str: Chain spec string representation :param chain_str: Chain spec string representation
@@ -119,7 +121,7 @@ def lock_queue(chained_input, chain_spec_dict, address=ZERO_ADDRESS_NORMAL, tx_h
:returns: New lock state for address :returns: New lock state for address
:rtype: number :rtype: number
""" """
address = tx_normalize.wallet_address(address) address = normalize_address(address)
chain_str = str(ChainSpec.from_dict(chain_spec_dict)) chain_str = str(ChainSpec.from_dict(chain_spec_dict))
r = Lock.set(chain_str, LockEnum.QUEUE, address=address, tx_hash=tx_hash) r = Lock.set(chain_str, LockEnum.QUEUE, address=address, tx_hash=tx_hash)
logg.debug('Queue direct locked for {}, flag now {}'.format(address, r)) logg.debug('Queue direct locked for {}, flag now {}'.format(address, r))
@@ -127,7 +129,7 @@ def lock_queue(chained_input, chain_spec_dict, address=ZERO_ADDRESS_NORMAL, tx_h
@celery_app.task(base=CriticalSQLAlchemyTask) @celery_app.task(base=CriticalSQLAlchemyTask)
def unlock_queue(chained_input, chain_spec_dict, address=ZERO_ADDRESS_NORMAL): def unlock_queue(chained_input, chain_spec_dict, address=ZERO_ADDRESS):
"""Task wrapper to reset queue direct lock """Task wrapper to reset queue direct lock
:param chain_str: Chain spec string representation :param chain_str: Chain spec string representation
@@ -137,7 +139,7 @@ def unlock_queue(chained_input, chain_spec_dict, address=ZERO_ADDRESS_NORMAL):
:returns: New lock state for address :returns: New lock state for address
:rtype: number :rtype: number
""" """
address = tx_normalize.wallet_address(address) address = normalize_address(address)
chain_str = str(ChainSpec.from_dict(chain_spec_dict)) chain_str = str(ChainSpec.from_dict(chain_spec_dict))
r = Lock.reset(chain_str, LockEnum.QUEUE, address=address) r = Lock.reset(chain_str, LockEnum.QUEUE, address=address)
logg.debug('Queue direct unlocked for {}, flag now {}'.format(address, r)) logg.debug('Queue direct unlocked for {}, flag now {}'.format(address, r))
@@ -146,13 +148,12 @@ def unlock_queue(chained_input, chain_spec_dict, address=ZERO_ADDRESS_NORMAL):
@celery_app.task(base=CriticalSQLAlchemyTask) @celery_app.task(base=CriticalSQLAlchemyTask)
def check_lock(chained_input, chain_spec_dict, lock_flags, address=None): def check_lock(chained_input, chain_spec_dict, lock_flags, address=None):
if address != None: address = normalize_address(address)
address = tx_normalize.wallet_address(address)
chain_str = '::' chain_str = '::'
if chain_spec_dict != None: if chain_spec_dict != None:
chain_str = str(ChainSpec.from_dict(chain_spec_dict)) chain_str = str(ChainSpec.from_dict(chain_spec_dict))
session = SessionBase.create_session() session = SessionBase.create_session()
r = Lock.check(chain_str, lock_flags, address=ZERO_ADDRESS_NORMAL, session=session) r = Lock.check(chain_str, lock_flags, address=ZERO_ADDRESS, session=session)
if address != None: if address != None:
r |= Lock.check(chain_str, lock_flags, address=address, session=session) r |= Lock.check(chain_str, lock_flags, address=address, session=session)
if r > 0: if r > 0:

View File

@@ -33,7 +33,6 @@ from cic_eth.admin.ctrl import (
from cic_eth.queue.tx import queue_create from cic_eth.queue.tx import queue_create
from cic_eth.eth.gas import create_check_gas_task from cic_eth.eth.gas import create_check_gas_task
from cic_eth.task import BaseTask from cic_eth.task import BaseTask
from cic_eth.encode import tx_normalize
celery_app = celery.current_app celery_app = celery.current_app
logg = logging.getLogger() logg = logging.getLogger()
@@ -74,7 +73,7 @@ def shift_nonce(self, chainspec_dict, tx_hash_orig_hex, delta=1):
set_cancel(chain_spec, strip_0x(tx['hash']), manual=True, session=session) set_cancel(chain_spec, strip_0x(tx['hash']), manual=True, session=session)
query_address = tx_normalize.wallet_address(address) query_address = add_0x(hex_uniform(strip_0x(address))) # aaaaargh
q = session.query(Otx) q = session.query(Otx)
q = q.join(TxCache) q = q.join(TxCache)
q = q.filter(TxCache.sender==query_address) q = q.filter(TxCache.sender==query_address)

View File

@@ -32,6 +32,7 @@ from chainqueue.db.enum import (
status_str, status_str,
) )
from chainqueue.error import TxStateChangeError from chainqueue.error import TxStateChangeError
from chainqueue.sql.query import get_tx
from eth_erc20 import ERC20 from eth_erc20 import ERC20
# local imports # local imports
@@ -39,7 +40,6 @@ from cic_eth.db.models.base import SessionBase
from cic_eth.db.models.role import AccountRole from cic_eth.db.models.role import AccountRole
from cic_eth.db.models.nonce import Nonce from cic_eth.db.models.nonce import Nonce
from cic_eth.error import InitializationError from cic_eth.error import InitializationError
from cic_eth.queue.query import get_tx_local
app = celery.current_app app = celery.current_app
@@ -284,7 +284,7 @@ class AdminApi:
tx_hash_hex = None tx_hash_hex = None
session = SessionBase.create_session() session = SessionBase.create_session()
for k in txs.keys(): for k in txs.keys():
tx_dict = get_tx_local(chain_spec, k, session=session) tx_dict = get_tx(chain_spec, k, session=session)
if tx_dict['nonce'] == nonce: if tx_dict['nonce'] == nonce:
tx_hash_hex = k tx_hash_hex = k
session.close() session.close()

View File

@@ -35,14 +35,14 @@ class RPC:
def from_config(config, use_signer=False, default_label='default', signer_label='signer'): def from_config(config, use_signer=False, default_label='default', signer_label='signer'):
chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC')) chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
RPCConnection.register_location(config.get('RPC_PROVIDER'), chain_spec, default_label) RPCConnection.register_location(config.get('RPC_HTTP_PROVIDER'), chain_spec, default_label)
if use_signer: if use_signer:
RPCConnection.register_constructor(ConnType.UNIX, EthUnixSignerConnection, signer_label) RPCConnection.register_constructor(ConnType.UNIX, EthUnixSignerConnection, signer_label)
RPCConnection.register_constructor(ConnType.HTTP, EthHTTPSignerConnection, signer_label) RPCConnection.register_constructor(ConnType.HTTP, EthHTTPSignerConnection, signer_label)
RPCConnection.register_constructor(ConnType.HTTP_SSL, EthHTTPSignerConnection, signer_label) RPCConnection.register_constructor(ConnType.HTTP_SSL, EthHTTPSignerConnection, signer_label)
RPCConnection.register_location(config.get('SIGNER_PROVIDER'), chain_spec, signer_label) RPCConnection.register_location(config.get('SIGNER_PROVIDER'), chain_spec, signer_label)
rpc = RPC(chain_spec, config.get('RPC_PROVIDER'), signer_provider=config.get('SIGNER_PROVIDER')) rpc = RPC(chain_spec, config.get('RPC_HTTP_PROVIDER'), signer_provider=config.get('SIGNER_PROVIDER'))
logg.info('set up rpc: {}'.format(rpc)) logg.info('set up rpc: {}'.format(rpc))
return rpc return rpc

View File

@@ -8,8 +8,8 @@ Create Date: 2021-04-02 18:41:20.864265
import datetime import datetime
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
from chainlib.eth.constant import ZERO_ADDRESS
from cic_eth.db.enum import LockEnum from cic_eth.db.enum import LockEnum
from cic_eth.encode import ZERO_ADDRESS_NORMAL
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
@@ -30,7 +30,7 @@ def upgrade():
sa.Column("otx_id", sa.Integer, sa.ForeignKey('otx.id'), nullable=True), sa.Column("otx_id", sa.Integer, sa.ForeignKey('otx.id'), nullable=True),
) )
op.create_index('idx_chain_address', 'lock', ['blockchain', 'address'], unique=True) op.create_index('idx_chain_address', 'lock', ['blockchain', 'address'], unique=True)
op.execute("INSERT INTO lock (address, date_created, blockchain, flags) VALUES('{}', '{}', '::', {})".format(ZERO_ADDRESS_NORMAL, datetime.datetime.utcnow(), LockEnum.INIT | LockEnum.SEND | LockEnum.QUEUE)) op.execute("INSERT INTO lock (address, date_created, blockchain, flags) VALUES('{}', '{}', '::', {})".format(ZERO_ADDRESS, datetime.datetime.utcnow(), LockEnum.INIT | LockEnum.SEND | LockEnum.QUEUE))
def downgrade(): def downgrade():

View File

@@ -4,12 +4,12 @@ import logging
# third-party imports # third-party imports
from sqlalchemy import Column, String, Integer, DateTime, ForeignKey from sqlalchemy import Column, String, Integer, DateTime, ForeignKey
from chainlib.eth.constant import ZERO_ADDRESS
from chainqueue.db.models.tx import TxCache from chainqueue.db.models.tx import TxCache
from chainqueue.db.models.otx import Otx from chainqueue.db.models.otx import Otx
# local imports # local imports
from cic_eth.db.models.base import SessionBase from cic_eth.db.models.base import SessionBase
from cic_eth.encode import ZERO_ADDRESS_NORMAL
logg = logging.getLogger() logg = logging.getLogger()
@@ -37,7 +37,7 @@ class Lock(SessionBase):
@staticmethod @staticmethod
def set(chain_str, flags, address=ZERO_ADDRESS_NORMAL, session=None, tx_hash=None): def set(chain_str, flags, address=ZERO_ADDRESS, session=None, tx_hash=None):
"""Sets flags associated with the given address and chain. """Sets flags associated with the given address and chain.
If a flags entry does not exist it is created. If a flags entry does not exist it is created.
@@ -90,7 +90,7 @@ class Lock(SessionBase):
@staticmethod @staticmethod
def reset(chain_str, flags, address=ZERO_ADDRESS_NORMAL, session=None): def reset(chain_str, flags, address=ZERO_ADDRESS, session=None):
"""Resets flags associated with the given address and chain. """Resets flags associated with the given address and chain.
If the resulting flags entry value is 0, the entry will be deleted. If the resulting flags entry value is 0, the entry will be deleted.
@@ -134,7 +134,7 @@ class Lock(SessionBase):
@staticmethod @staticmethod
def check(chain_str, flags, address=ZERO_ADDRESS_NORMAL, session=None): def check(chain_str, flags, address=ZERO_ADDRESS, session=None):
"""Checks whether all given flags are set for given address and chain. """Checks whether all given flags are set for given address and chain.
Does not validate the address against any other tables or components. Does not validate the address against any other tables or components.

View File

@@ -1,16 +0,0 @@
# external imports
from chainlib.eth.constant import ZERO_ADDRESS
from chainqueue.encode import TxHexNormalizer
from chainlib.eth.tx import unpack
tx_normalize = TxHexNormalizer()
ZERO_ADDRESS_NORMAL = tx_normalize.wallet_address(ZERO_ADDRESS)
def unpack_normal(signed_tx_bytes, chain_spec):
tx = unpack(signed_tx_bytes, chain_spec)
tx['hash'] = tx_normalize.tx_hash(tx['hash'])
tx['from'] = tx_normalize.wallet_address(tx['from'])
tx['to'] = tx_normalize.wallet_address(tx['to'])
return tx

View File

@@ -14,7 +14,10 @@ from chainlib.eth.sign import (
sign_message, sign_message,
) )
from chainlib.eth.address import to_checksum_address from chainlib.eth.address import to_checksum_address
from chainlib.eth.tx import TxFormat from chainlib.eth.tx import (
TxFormat,
unpack,
)
from chainlib.chain import ChainSpec from chainlib.chain import ChainSpec
from chainlib.error import JSONRPCException from chainlib.error import JSONRPCException
from eth_accounts_index.registry import AccountRegistry from eth_accounts_index.registry import AccountRegistry
@@ -46,10 +49,6 @@ from cic_eth.eth.nonce import (
from cic_eth.queue.tx import ( from cic_eth.queue.tx import (
register_tx, register_tx,
) )
from cic_eth.encode import (
unpack_normal,
ZERO_ADDRESS_NORMAL,
)
logg = logging.getLogger() logg = logging.getLogger()
celery_app = celery.current_app celery_app = celery.current_app
@@ -296,17 +295,17 @@ def cache_gift_data(
chain_spec = ChainSpec.from_dict(chain_spec_dict) chain_spec = ChainSpec.from_dict(chain_spec_dict)
tx_signed_raw_bytes = bytes.fromhex(strip_0x(tx_signed_raw_hex)) tx_signed_raw_bytes = bytes.fromhex(strip_0x(tx_signed_raw_hex))
tx = unpack_normal(tx_signed_raw_bytes, chain_spec) tx = unpack(tx_signed_raw_bytes, chain_spec)
tx_data = Faucet.parse_give_to_request(tx['data']) tx_data = Faucet.parse_give_to_request(tx['data'])
session = self.create_session() session = self.create_session()
tx_dict = { tx_dict = {
'hash': tx['hash'], 'hash': tx_hash_hex,
'from': tx['from'], 'from': tx['from'],
'to': tx['to'], 'to': tx['to'],
'source_token': ZERO_ADDRESS_NORMAL, 'source_token': ZERO_ADDRESS,
'destination_token': ZERO_ADDRESS_NORMAL, 'destination_token': ZERO_ADDRESS,
'from_value': 0, 'from_value': 0,
'to_value': 0, 'to_value': 0,
} }
@@ -335,17 +334,17 @@ def cache_account_data(
:rtype: tuple :rtype: tuple
""" """
chain_spec = ChainSpec.from_dict(chain_spec_dict) chain_spec = ChainSpec.from_dict(chain_spec_dict)
tx_signed_raw_bytes = bytes.fromhex(strip_0x(tx_signed_raw_hex)) tx_signed_raw_bytes = bytes.fromhex(tx_signed_raw_hex[2:])
tx = unpack_normal(tx_signed_raw_bytes, chain_spec) tx = unpack(tx_signed_raw_bytes, chain_spec)
tx_data = AccountsIndex.parse_add_request(tx['data']) tx_data = AccountsIndex.parse_add_request(tx['data'])
session = SessionBase.create_session() session = SessionBase.create_session()
tx_dict = { tx_dict = {
'hash': tx['hash'], 'hash': tx_hash_hex,
'from': tx['from'], 'from': tx['from'],
'to': tx['to'], 'to': tx['to'],
'source_token': ZERO_ADDRESS_NORMAL, 'source_token': ZERO_ADDRESS,
'destination_token': ZERO_ADDRESS_NORMAL, 'destination_token': ZERO_ADDRESS,
'from_value': 0, 'from_value': 0,
'to_value': 0, 'to_value': 0,
} }

View File

@@ -3,11 +3,8 @@ import logging
# external imports # external imports
import celery import celery
from hexathon import ( from hexathon import strip_0x
strip_0x, from chainlib.eth.constant import ZERO_ADDRESS
add_0x,
)
#from chainlib.eth.constant import ZERO_ADDRESS
from chainlib.chain import ChainSpec from chainlib.chain import ChainSpec
from chainlib.eth.address import is_checksum_address from chainlib.eth.address import is_checksum_address
from chainlib.connection import RPCConnection from chainlib.connection import RPCConnection
@@ -24,6 +21,7 @@ from chainlib.eth.error import (
from chainlib.eth.tx import ( from chainlib.eth.tx import (
TxFactory, TxFactory,
TxFormat, TxFormat,
unpack,
) )
from chainlib.eth.contract import ( from chainlib.eth.contract import (
abi_decode_single, abi_decode_single,
@@ -47,7 +45,6 @@ from cic_eth.eth.nonce import CustodialTaskNonceOracle
from cic_eth.queue.tx import ( from cic_eth.queue.tx import (
queue_create, queue_create,
register_tx, register_tx,
unpack,
) )
from cic_eth.queue.query import get_tx from cic_eth.queue.query import get_tx
from cic_eth.task import ( from cic_eth.task import (
@@ -56,11 +53,6 @@ from cic_eth.task import (
CriticalSQLAlchemyAndSignerTask, CriticalSQLAlchemyAndSignerTask,
CriticalWeb3AndSignerTask, CriticalWeb3AndSignerTask,
) )
from cic_eth.encode import (
tx_normalize,
ZERO_ADDRESS_NORMAL,
unpack_normal,
)
celery_app = celery.current_app celery_app = celery.current_app
logg = logging.getLogger() logg = logging.getLogger()
@@ -74,7 +66,6 @@ class MaxGasOracle:
return MAXIMUM_FEE_UNITS return MAXIMUM_FEE_UNITS
#def create_check_gas_task(tx_signed_raws_hex, chain_spec, holder_address, gas=None, tx_hashes_hex=None, queue=None):
def create_check_gas_task(tx_signed_raws_hex, chain_spec, holder_address, gas=None, tx_hashes_hex=None, queue=None): def create_check_gas_task(tx_signed_raws_hex, chain_spec, holder_address, gas=None, tx_hashes_hex=None, queue=None):
"""Creates a celery task signature for a check_gas task that adds the task to the outgoing queue to be processed by the dispatcher. """Creates a celery task signature for a check_gas task that adds the task to the outgoing queue to be processed by the dispatcher.
@@ -139,16 +130,16 @@ def cache_gas_data(
""" """
chain_spec = ChainSpec.from_dict(chain_spec_dict) chain_spec = ChainSpec.from_dict(chain_spec_dict)
tx_signed_raw_bytes = bytes.fromhex(strip_0x(tx_signed_raw_hex)) tx_signed_raw_bytes = bytes.fromhex(strip_0x(tx_signed_raw_hex))
tx = unpack_normal(tx_signed_raw_bytes, chain_spec) tx = unpack(tx_signed_raw_bytes, chain_spec)
session = SessionBase.create_session() session = SessionBase.create_session()
tx_dict = { tx_dict = {
'hash': tx['hash'], 'hash': tx_hash_hex,
'from': tx['from'], 'from': tx['from'],
'to': tx['to'], 'to': tx['to'],
'source_token': ZERO_ADDRESS_NORMAL, 'source_token': ZERO_ADDRESS,
'destination_token': ZERO_ADDRESS_NORMAL, 'destination_token': ZERO_ADDRESS,
'from_value': tx['value'], 'from_value': tx['value'],
'to_value': tx['value'], 'to_value': tx['value'],
} }
@@ -159,7 +150,7 @@ def cache_gas_data(
@celery_app.task(bind=True, throws=(OutOfGasError), base=CriticalSQLAlchemyAndWeb3Task) @celery_app.task(bind=True, throws=(OutOfGasError), base=CriticalSQLAlchemyAndWeb3Task)
def check_gas(self, tx_hashes_hex, chain_spec_dict, txs_hex=[], address=None, gas_required=MAXIMUM_FEE_UNITS): def check_gas(self, tx_hashes, chain_spec_dict, txs=[], address=None, gas_required=MAXIMUM_FEE_UNITS):
"""Check the gas level of the sender address of a transaction. """Check the gas level of the sender address of a transaction.
If the account balance is not sufficient for the required gas, gas refill is requested and OutOfGasError raiser. If the account balance is not sufficient for the required gas, gas refill is requested and OutOfGasError raiser.
@@ -179,21 +170,6 @@ def check_gas(self, tx_hashes_hex, chain_spec_dict, txs_hex=[], address=None, ga
:return: Signed raw transaction data list :return: Signed raw transaction data list
:rtype: param txs, unchanged :rtype: param txs, unchanged
""" """
if address != None:
if not is_checksum_address(address):
raise ValueError('invalid address {}'.format(address))
address = tx_normalize.wallet_address(address)
address = add_0x(address)
tx_hashes = []
txs = []
for tx_hash in tx_hashes_hex:
tx_hash = tx_normalize.tx_hash(tx_hash)
tx_hashes.append(tx_hash)
for tx in txs_hex:
tx = tx_normalize.tx_wire(tx)
txs.append(tx)
chain_spec = ChainSpec.from_dict(chain_spec_dict) chain_spec = ChainSpec.from_dict(chain_spec_dict)
logg.debug('txs {} tx_hashes {}'.format(txs, tx_hashes)) logg.debug('txs {} tx_hashes {}'.format(txs, tx_hashes))
@@ -211,6 +187,9 @@ def check_gas(self, tx_hashes_hex, chain_spec_dict, txs_hex=[], address=None, ga
raise ValueError('txs passed to check gas must all have same sender; had {} got {}'.format(address, tx['from'])) raise ValueError('txs passed to check gas must all have same sender; had {} got {}'.format(address, tx['from']))
addresspass.append(address) addresspass.append(address)
if not is_checksum_address(address):
raise ValueError('invalid address {}'.format(address))
queue = self.request.delivery_info.get('routing_key') queue = self.request.delivery_info.get('routing_key')
conn = RPCConnection.connect(chain_spec) conn = RPCConnection.connect(chain_spec)
@@ -325,7 +304,6 @@ def refill_gas(self, recipient_address, chain_spec_dict):
# Determine value of gas tokens to send # Determine value of gas tokens to send
# if an uncompleted gas refill for the same recipient already exists, we still need to spend the nonce # if an uncompleted gas refill for the same recipient already exists, we still need to spend the nonce
# however, we will perform a 0-value transaction instead # however, we will perform a 0-value transaction instead
recipient_address = tx_normalize.wallet_address(recipient_address)
zero_amount = False zero_amount = False
session = SessionBase.create_session() session = SessionBase.create_session()
status_filter = StatusBits.FINAL | StatusBits.NODE_ERROR | StatusBits.NETWORK_ERROR | StatusBits.UNKNOWN_ERROR status_filter = StatusBits.FINAL | StatusBits.NODE_ERROR | StatusBits.NETWORK_ERROR | StatusBits.UNKNOWN_ERROR
@@ -400,7 +378,6 @@ def resend_with_higher_gas(self, txold_hash_hex, chain_spec_dict, gas=None, defa
:returns: Transaction hash :returns: Transaction hash
:rtype: str, 0x-hex :rtype: str, 0x-hex
""" """
txold_hash_hex = tx_normalize.tx_hash(txold_hash_hex)
session = SessionBase.create_session() session = SessionBase.create_session()
otx = Otx.load(txold_hash_hex, session) otx = Otx.load(txold_hash_hex, session)

View File

@@ -15,7 +15,6 @@ from chainqueue.db.enum import (
# local imports # local imports
from cic_eth.db import SessionBase from cic_eth.db import SessionBase
from cic_eth.task import CriticalSQLAlchemyTask from cic_eth.task import CriticalSQLAlchemyTask
from cic_eth.encode import tx_normalize
celery_app = celery.current_app celery_app = celery.current_app
@@ -23,9 +22,6 @@ logg = logging.getLogger()
def __balance_outgoing_compatible(token_address, holder_address): def __balance_outgoing_compatible(token_address, holder_address):
token_address = tx_normalize.executable_address(token_address)
holder_address = tx_normalize.wallet_address(holder_address)
session = SessionBase.create_session() session = SessionBase.create_session()
q = session.query(TxCache.from_value) q = session.query(TxCache.from_value)
q = q.join(Otx) q = q.join(Otx)
@@ -62,9 +58,6 @@ def balance_outgoing(tokens, holder_address, chain_spec_dict):
def __balance_incoming_compatible(token_address, receiver_address): def __balance_incoming_compatible(token_address, receiver_address):
token_address = tx_normalize.executable_address(token_address)
receiver_address = tx_normalize.wallet_address(receiver_address)
session = SessionBase.create_session() session = SessionBase.create_session()
q = session.query(TxCache.to_value) q = session.query(TxCache.to_value)
q = q.join(Otx) q = q.join(Otx)
@@ -117,7 +110,7 @@ def assemble_balances(balances_collection):
logg.debug('received collection {}'.format(balances_collection)) logg.debug('received collection {}'.format(balances_collection))
for c in balances_collection: for c in balances_collection:
for b in c: for b in c:
address = tx_normalize.executable_address(b['address']) address = b['address']
if tokens.get(address) == None: if tokens.get(address) == None:
tokens[address] = { tokens[address] = {
'address': address, 'address': address,

View File

@@ -6,7 +6,6 @@ import celery
from cic_eth.task import CriticalSQLAlchemyTask from cic_eth.task import CriticalSQLAlchemyTask
from cic_eth.db import SessionBase from cic_eth.db import SessionBase
from cic_eth.db.models.lock import Lock from cic_eth.db.models.lock import Lock
from cic_eth.encode import tx_normalize
celery_app = celery.current_app celery_app = celery.current_app
@@ -22,9 +21,6 @@ def get_lock(address=None):
:returns: List of locks :returns: List of locks
:rtype: list of dicts :rtype: list of dicts
""" """
if address != None:
address = tx_normalize.wallet_address(address)
session = SessionBase.create_session() session = SessionBase.create_session()
q = session.query( q = session.query(
Lock.date_created, Lock.date_created,

View File

@@ -4,8 +4,8 @@ import datetime
# external imports # external imports
import celery import celery
from chainlib.chain import ChainSpec from chainlib.chain import ChainSpec
import chainqueue.sql.query
from chainlib.eth.tx import unpack from chainlib.eth.tx import unpack
import chainqueue.sql.query
from chainqueue.db.enum import ( from chainqueue.db.enum import (
StatusEnum, StatusEnum,
is_alive, is_alive,
@@ -20,10 +20,6 @@ from cic_eth.db.enum import LockEnum
from cic_eth.task import CriticalSQLAlchemyTask from cic_eth.task import CriticalSQLAlchemyTask
from cic_eth.db.models.lock import Lock from cic_eth.db.models.lock import Lock
from cic_eth.db.models.base import SessionBase from cic_eth.db.models.base import SessionBase
from cic_eth.encode import (
tx_normalize,
unpack_normal,
)
celery_app = celery.current_app celery_app = celery.current_app
@@ -31,76 +27,49 @@ celery_app = celery.current_app
@celery_app.task(base=CriticalSQLAlchemyTask) @celery_app.task(base=CriticalSQLAlchemyTask)
def get_tx_cache(chain_spec_dict, tx_hash): def get_tx_cache(chain_spec_dict, tx_hash):
chain_spec = ChainSpec.from_dict(chain_spec_dict) chain_spec = ChainSpec.from_dict(chain_spec_dict)
return get_tx_cache_local(chain_spec, tx_hash) session = SessionBase.create_session()
def get_tx_cache_local(chain_spec, tx_hash, session=None):
tx_hash = tx_normalize.tx_hash(tx_hash)
session = SessionBase.bind_session(session)
r = chainqueue.sql.query.get_tx_cache(chain_spec, tx_hash, session=session) r = chainqueue.sql.query.get_tx_cache(chain_spec, tx_hash, session=session)
SessionBase.release_session(session) session.close()
return r return r
@celery_app.task(base=CriticalSQLAlchemyTask) @celery_app.task(base=CriticalSQLAlchemyTask)
def get_tx(chain_spec_dict, tx_hash): def get_tx(chain_spec_dict, tx_hash):
chain_spec = ChainSpec.from_dict(chain_spec_dict) chain_spec = ChainSpec.from_dict(chain_spec_dict)
return get_tx_local(chain_spec, tx_hash) session = SessionBase.create_session()
def get_tx_local(chain_spec, tx_hash, session=None):
tx_hash = tx_normalize.tx_hash(tx_hash)
session = SessionBase.bind_session(session)
r = chainqueue.sql.query.get_tx(chain_spec, tx_hash, session=session) r = chainqueue.sql.query.get_tx(chain_spec, tx_hash, session=session)
SessionBase.release_session(session) session.close()
return r return r
@celery_app.task(base=CriticalSQLAlchemyTask) @celery_app.task(base=CriticalSQLAlchemyTask)
def get_account_tx(chain_spec_dict, address, as_sender=True, as_recipient=True, counterpart=None): def get_account_tx(chain_spec_dict, address, as_sender=True, as_recipient=True, counterpart=None):
chain_spec = ChainSpec.from_dict(chain_spec_dict) chain_spec = ChainSpec.from_dict(chain_spec_dict)
return get_account_tx_local(chain_spec, address, as_sender=as_sender, as_recipient=as_recipient, counterpart=counterpart) session = SessionBase.create_session()
def get_account_tx_local(chain_spec, address, as_sender=True, as_recipient=True, counterpart=None, session=None):
address = tx_normalize.wallet_address(address)
session = SessionBase.bind_session(session)
r = chainqueue.sql.query.get_account_tx(chain_spec, address, as_sender=True, as_recipient=True, counterpart=None, session=session) r = chainqueue.sql.query.get_account_tx(chain_spec, address, as_sender=True, as_recipient=True, counterpart=None, session=session)
SessionBase.release_session(session) session.close()
return r return r
@celery_app.task(base=CriticalSQLAlchemyTask) @celery_app.task(base=CriticalSQLAlchemyTask)
def get_upcoming_tx_nolock(chain_spec_dict, status=StatusEnum.READYSEND, not_status=None, recipient=None, before=None, limit=0): def get_upcoming_tx_nolock(chain_spec_dict, status=StatusEnum.READYSEND, not_status=None, recipient=None, before=None, limit=0, session=None):
chain_spec = ChainSpec.from_dict(chain_spec_dict) chain_spec = ChainSpec.from_dict(chain_spec_dict)
return get_upcoming_tx_nolock_local(chain_spec, status=status, not_status=not_status, recipient=recipient, before=before, limit=limit)
def get_upcoming_tx_nolock_local(chain_spec, status=StatusEnum.READYSEND, not_status=None, recipient=None, before=None, limit=0, session=None):
recipient = tx_normalize.wallet_address(recipient)
session = SessionBase.create_session() session = SessionBase.create_session()
r = chainqueue.sql.query.get_upcoming_tx(chain_spec, status, not_status=not_status, recipient=recipient, before=before, limit=limit, session=session, decoder=unpack_normal) r = chainqueue.sql.query.get_upcoming_tx(chain_spec, status, not_status=not_status, recipient=recipient, before=before, limit=limit, session=session, decoder=unpack)
session.close() session.close()
return r return r
def get_status_tx(chain_spec, status, not_status=None, before=None, exact=False, limit=0, session=None): def get_status_tx(chain_spec, status, not_status=None, before=None, exact=False, limit=0, session=None):
return chainqueue.sql.query.get_status_tx_cache(chain_spec, status, not_status=not_status, before=before, exact=exact, limit=limit, session=session, decoder=unpack_normal) return chainqueue.sql.query.get_status_tx_cache(chain_spec, status, not_status=not_status, before=before, exact=exact, limit=limit, session=session, decoder=unpack)
def get_paused_tx(chain_spec, status=None, sender=None, session=None, decoder=None): def get_paused_tx(chain_spec, status=None, sender=None, session=None, decoder=None):
sender = tx_normalize.wallet_address(sender) return chainqueue.sql.query.get_paused_tx_cache(chain_spec, status=status, sender=sender, session=session, decoder=unpack)
return chainqueue.sql.query.get_paused_tx_cache(chain_spec, status=status, sender=sender, session=session, decoder=unpack_normal)
def get_nonce_tx(chain_spec, nonce, sender): def get_nonce_tx(chain_spec, nonce, sender):
sender = tx_normalize.wallet_address(sender) return get_nonce_tx_cache(chain_spec, nonce, sender, decoder=unpack)
return get_nonce_tx_local(chain_spec, nonce, sender)
def get_nonce_tx_local(chain_spec, nonce, sender, session=None):
sender = tx_normalize.wallet_address(sender)
return chainqueue.sql.query.get_nonce_tx_cache(chain_spec, nonce, sender, decoder=unpack_normal, session=session)
def get_upcoming_tx(chain_spec, status=StatusEnum.READYSEND, not_status=None, recipient=None, before=None, limit=0, session=None): def get_upcoming_tx(chain_spec, status=StatusEnum.READYSEND, not_status=None, recipient=None, before=None, limit=0, session=None):
@@ -122,8 +91,6 @@ def get_upcoming_tx(chain_spec, status=StatusEnum.READYSEND, not_status=None, re
:returns: Transactions :returns: Transactions
:rtype: dict, with transaction hash as key, signed raw transaction as value :rtype: dict, with transaction hash as key, signed raw transaction as value
""" """
if recipient != None:
recipient = tx_normalize.wallet_address(recipient)
session = SessionBase.bind_session(session) session = SessionBase.bind_session(session)
q_outer = session.query( q_outer = session.query(
TxCache.sender, TxCache.sender,

View File

@@ -6,14 +6,12 @@ import chainqueue.sql.state
import celery import celery
from cic_eth.task import CriticalSQLAlchemyTask from cic_eth.task import CriticalSQLAlchemyTask
from cic_eth.db.models.base import SessionBase from cic_eth.db.models.base import SessionBase
from cic_eth.encode import tx_normalize
celery_app = celery.current_app celery_app = celery.current_app
@celery_app.task(base=CriticalSQLAlchemyTask) @celery_app.task(base=CriticalSQLAlchemyTask)
def set_sent(chain_spec_dict, tx_hash, fail=False): def set_sent(chain_spec_dict, tx_hash, fail=False):
tx_hash = tx_normalize.tx_hash(tx_hash)
chain_spec = ChainSpec.from_dict(chain_spec_dict) chain_spec = ChainSpec.from_dict(chain_spec_dict)
session = SessionBase.create_session() session = SessionBase.create_session()
r = chainqueue.sql.state.set_sent(chain_spec, tx_hash, fail, session=session) r = chainqueue.sql.state.set_sent(chain_spec, tx_hash, fail, session=session)
@@ -23,7 +21,6 @@ def set_sent(chain_spec_dict, tx_hash, fail=False):
@celery_app.task(base=CriticalSQLAlchemyTask) @celery_app.task(base=CriticalSQLAlchemyTask)
def set_final(chain_spec_dict, tx_hash, block=None, tx_index=None, fail=False): def set_final(chain_spec_dict, tx_hash, block=None, tx_index=None, fail=False):
tx_hash = tx_normalize.tx_hash(tx_hash)
chain_spec = ChainSpec.from_dict(chain_spec_dict) chain_spec = ChainSpec.from_dict(chain_spec_dict)
session = SessionBase.create_session() session = SessionBase.create_session()
r = chainqueue.sql.state.set_final(chain_spec, tx_hash, block=block, tx_index=tx_index, fail=fail, session=session) r = chainqueue.sql.state.set_final(chain_spec, tx_hash, block=block, tx_index=tx_index, fail=fail, session=session)
@@ -33,7 +30,6 @@ def set_final(chain_spec_dict, tx_hash, block=None, tx_index=None, fail=False):
@celery_app.task(base=CriticalSQLAlchemyTask) @celery_app.task(base=CriticalSQLAlchemyTask)
def set_cancel(chain_spec_dict, tx_hash, manual=False): def set_cancel(chain_spec_dict, tx_hash, manual=False):
tx_hash = tx_normalize.tx_hash(tx_hash)
chain_spec = ChainSpec.from_dict(chain_spec_dict) chain_spec = ChainSpec.from_dict(chain_spec_dict)
session = SessionBase.create_session() session = SessionBase.create_session()
r = chainqueue.sql.state.set_cancel(chain_spec, tx_hash, manual, session=session) r = chainqueue.sql.state.set_cancel(chain_spec, tx_hash, manual, session=session)
@@ -43,7 +39,6 @@ def set_cancel(chain_spec_dict, tx_hash, manual=False):
@celery_app.task(base=CriticalSQLAlchemyTask) @celery_app.task(base=CriticalSQLAlchemyTask)
def set_rejected(chain_spec_dict, tx_hash): def set_rejected(chain_spec_dict, tx_hash):
tx_hash = tx_normalize.tx_hash(tx_hash)
chain_spec = ChainSpec.from_dict(chain_spec_dict) chain_spec = ChainSpec.from_dict(chain_spec_dict)
session = SessionBase.create_session() session = SessionBase.create_session()
r = chainqueue.sql.state.set_rejected(chain_spec, tx_hash, session=session) r = chainqueue.sql.state.set_rejected(chain_spec, tx_hash, session=session)
@@ -53,7 +48,6 @@ def set_rejected(chain_spec_dict, tx_hash):
@celery_app.task(base=CriticalSQLAlchemyTask) @celery_app.task(base=CriticalSQLAlchemyTask)
def set_fubar(chain_spec_dict, tx_hash): def set_fubar(chain_spec_dict, tx_hash):
tx_hash = tx_normalize.tx_hash(tx_hash)
chain_spec = ChainSpec.from_dict(chain_spec_dict) chain_spec = ChainSpec.from_dict(chain_spec_dict)
session = SessionBase.create_session() session = SessionBase.create_session()
r = chainqueue.sql.state.set_fubar(chain_spec, tx_hash, session=session) r = chainqueue.sql.state.set_fubar(chain_spec, tx_hash, session=session)
@@ -63,7 +57,6 @@ def set_fubar(chain_spec_dict, tx_hash):
@celery_app.task(base=CriticalSQLAlchemyTask) @celery_app.task(base=CriticalSQLAlchemyTask)
def set_manual(chain_spec_dict, tx_hash): def set_manual(chain_spec_dict, tx_hash):
tx_hash = tx_normalize.tx_hash(tx_hash)
chain_spec = ChainSpec.from_dict(chain_spec_dict) chain_spec = ChainSpec.from_dict(chain_spec_dict)
session = SessionBase.create_session() session = SessionBase.create_session()
r = chainqueue.sql.state.set_manual(chain_spec, tx_hash, session=session) r = chainqueue.sql.state.set_manual(chain_spec, tx_hash, session=session)
@@ -73,7 +66,6 @@ def set_manual(chain_spec_dict, tx_hash):
@celery_app.task(base=CriticalSQLAlchemyTask) @celery_app.task(base=CriticalSQLAlchemyTask)
def set_ready(chain_spec_dict, tx_hash): def set_ready(chain_spec_dict, tx_hash):
tx_hash = tx_normalize.tx_hash(tx_hash)
chain_spec = ChainSpec.from_dict(chain_spec_dict) chain_spec = ChainSpec.from_dict(chain_spec_dict)
session = SessionBase.create_session() session = SessionBase.create_session()
r = chainqueue.sql.state.set_ready(chain_spec, tx_hash, session=session) r = chainqueue.sql.state.set_ready(chain_spec, tx_hash, session=session)
@@ -83,7 +75,6 @@ def set_ready(chain_spec_dict, tx_hash):
@celery_app.task(base=CriticalSQLAlchemyTask) @celery_app.task(base=CriticalSQLAlchemyTask)
def set_reserved(chain_spec_dict, tx_hash): def set_reserved(chain_spec_dict, tx_hash):
tx_hash = tx_normalize.tx_hash(tx_hash)
chain_spec = ChainSpec.from_dict(chain_spec_dict) chain_spec = ChainSpec.from_dict(chain_spec_dict)
session = SessionBase.create_session() session = SessionBase.create_session()
r = chainqueue.sql.state.set_reserved(chain_spec, tx_hash, session=session) r = chainqueue.sql.state.set_reserved(chain_spec, tx_hash, session=session)
@@ -93,7 +84,6 @@ def set_reserved(chain_spec_dict, tx_hash):
@celery_app.task(base=CriticalSQLAlchemyTask) @celery_app.task(base=CriticalSQLAlchemyTask)
def set_waitforgas(chain_spec_dict, tx_hash): def set_waitforgas(chain_spec_dict, tx_hash):
tx_hash = tx_normalize.tx_hash(tx_hash)
chain_spec = ChainSpec.from_dict(chain_spec_dict) chain_spec = ChainSpec.from_dict(chain_spec_dict)
session = SessionBase.create_session() session = SessionBase.create_session()
r = chainqueue.sql.state.set_waitforgas(chain_spec, tx_hash, session=session) r = chainqueue.sql.state.set_waitforgas(chain_spec, tx_hash, session=session)
@@ -103,7 +93,6 @@ def set_waitforgas(chain_spec_dict, tx_hash):
@celery_app.task(base=CriticalSQLAlchemyTask) @celery_app.task(base=CriticalSQLAlchemyTask)
def get_state_log(chain_spec_dict, tx_hash): def get_state_log(chain_spec_dict, tx_hash):
tx_hash = tx_normalize.tx_hash(tx_hash)
chain_spec = ChainSpec.from_dict(chain_spec_dict) chain_spec = ChainSpec.from_dict(chain_spec_dict)
session = SessionBase.create_session() session = SessionBase.create_session()
r = chainqueue.sql.state.get_state_log(chain_spec, tx_hash, session=session) r = chainqueue.sql.state.get_state_log(chain_spec, tx_hash, session=session)
@@ -113,7 +102,6 @@ def get_state_log(chain_spec_dict, tx_hash):
@celery_app.task(base=CriticalSQLAlchemyTask) @celery_app.task(base=CriticalSQLAlchemyTask)
def obsolete(chain_spec_dict, tx_hash, final): def obsolete(chain_spec_dict, tx_hash, final):
tx_hash = tx_normalize.tx_hash(tx_hash)
chain_spec = ChainSpec.from_dict(chain_spec_dict) chain_spec = ChainSpec.from_dict(chain_spec_dict)
session = SessionBase.create_session() session = SessionBase.create_session()
r = chainqueue.sql.state.obsolete_by_cache(chain_spec, tx_hash, final, session=session) r = chainqueue.sql.state.obsolete_by_cache(chain_spec, tx_hash, final, session=session)

View File

@@ -13,7 +13,6 @@ from chainqueue.error import NotLocalTxError
# local imports # local imports
from cic_eth.task import CriticalSQLAlchemyAndWeb3Task from cic_eth.task import CriticalSQLAlchemyAndWeb3Task
from cic_eth.db.models.base import SessionBase from cic_eth.db.models.base import SessionBase
from cic_eth.encode import tx_normalize
celery_app = celery.current_app celery_app = celery.current_app
@@ -21,7 +20,6 @@ logg = logging.getLogger()
def tx_times(tx_hash, chain_spec, session=None): def tx_times(tx_hash, chain_spec, session=None):
tx_hash = tx_normalize.tx_hash(tx_hash)
session = SessionBase.bind_session(session) session = SessionBase.bind_session(session)

View File

@@ -32,16 +32,12 @@ from cic_eth.db import SessionBase
from cic_eth.db.enum import LockEnum from cic_eth.db.enum import LockEnum
from cic_eth.task import CriticalSQLAlchemyTask from cic_eth.task import CriticalSQLAlchemyTask
from cic_eth.error import LockedError from cic_eth.error import LockedError
from cic_eth.encode import tx_normalize
celery_app = celery.current_app celery_app = celery.current_app
logg = logging.getLogger() logg = logging.getLogger()
def queue_create(chain_spec, nonce, holder_address, tx_hash, signed_tx, session=None): def queue_create(chain_spec, nonce, holder_address, tx_hash, signed_tx, session=None):
tx_hash = tx_normalize.tx_hash(tx_hash)
signed_tx = tx_normalize.tx_hash(signed_tx)
holder_address = tx_normalize.wallet_address(holder_address)
session = SessionBase.bind_session(session) session = SessionBase.bind_session(session)
lock = Lock.check_aggregate(str(chain_spec), LockEnum.QUEUE, holder_address, session=session) lock = Lock.check_aggregate(str(chain_spec), LockEnum.QUEUE, holder_address, session=session)
@@ -71,8 +67,6 @@ def register_tx(tx_hash_hex, tx_signed_raw_hex, chain_spec, queue, cache_task=No
:returns: Tuple; Transaction hash, signed raw transaction data :returns: Tuple; Transaction hash, signed raw transaction data
:rtype: tuple :rtype: tuple
""" """
tx_hash_hex = tx_normalize.tx_hash(tx_hash_hex)
tx_signed_raw_hex = tx_normalize.tx_hash(tx_signed_raw_hex)
logg.debug('adding queue tx {}:{} -> {}'.format(chain_spec, tx_hash_hex, tx_signed_raw_hex)) logg.debug('adding queue tx {}:{} -> {}'.format(chain_spec, tx_hash_hex, tx_signed_raw_hex))
tx_signed_raw = bytes.fromhex(strip_0x(tx_signed_raw_hex)) tx_signed_raw = bytes.fromhex(strip_0x(tx_signed_raw_hex))
tx = unpack(tx_signed_raw, chain_spec) tx = unpack(tx_signed_raw, chain_spec)

View File

@@ -101,14 +101,14 @@ class DispatchSyncer:
LockEnum.QUEUE, LockEnum.QUEUE,
tx['from'], tx['from'],
], ],
queue=config.get('CELERY_QUEUE'), queue=queue,
) )
s_send = celery.signature( s_send = celery.signature(
'cic_eth.eth.tx.send', 'cic_eth.eth.tx.send',
[ [
self.chain_spec.asdict(), self.chain_spec.asdict(),
], ],
queue=config.get('CELERY_QUEUE'), queue=queue,
) )
s_check.link(s_send) s_check.link(s_send)
t = s_check.apply_async() t = s_check.apply_async()

View File

@@ -10,14 +10,15 @@ from chainlib.eth.tx import unpack
from chainqueue.db.enum import StatusBits from chainqueue.db.enum import StatusBits
from chainqueue.db.models.tx import TxCache from chainqueue.db.models.tx import TxCache
from chainqueue.db.models.otx import Otx from chainqueue.db.models.otx import Otx
from chainqueue.sql.query import get_paused_tx_cache as get_paused_tx
from chainlib.eth.address import to_checksum_address from chainlib.eth.address import to_checksum_address
# local imports # local imports
from cic_eth.db.models.base import SessionBase from cic_eth.db.models.base import SessionBase
from cic_eth.eth.gas import create_check_gas_task from cic_eth.eth.gas import create_check_gas_task
from cic_eth.queue.query import get_paused_tx
from .base import SyncFilter from .base import SyncFilter
#logg = logging.getLogger().getChild(__name__)
logg = logging.getLogger() logg = logging.getLogger()

View File

@@ -69,6 +69,7 @@ from cic_eth.registry import (
) )
from cic_eth.task import BaseTask from cic_eth.task import BaseTask
logging.basicConfig(level=logging.WARNING) logging.basicConfig(level=logging.WARNING)
logg = logging.getLogger() logg = logging.getLogger()
@@ -203,9 +204,6 @@ def main():
argv.append(config.get('CELERY_QUEUE')) argv.append(config.get('CELERY_QUEUE'))
argv.append('-n') argv.append('-n')
argv.append(config.get('CELERY_QUEUE')) argv.append(config.get('CELERY_QUEUE'))
argv.append('--config')
argv.append('celeryconfig')
BaseTask.default_token_symbol = config.get('CIC_DEFAULT_TOKEN_SYMBOL') BaseTask.default_token_symbol = config.get('CIC_DEFAULT_TOKEN_SYMBOL')
BaseTask.default_token_address = registry.by_name(BaseTask.default_token_symbol) BaseTask.default_token_address = registry.by_name(BaseTask.default_token_symbol)

View File

@@ -18,7 +18,7 @@ from cic_eth.db.models.base import SessionBase
logging.basicConfig(level=logging.WARNING) logging.basicConfig(level=logging.WARNING)
logg = logging.getLogger() logg = logging.getLogger()
arg_flags = cic_eth.cli.argflag_std_base | cic_eth.cli.Flag.UNSAFE | cic_eth.cli.Flag.CHAIN_SPEC arg_flags = cic_eth.cli.argflag_std_base
local_arg_flags = cic_eth.cli.argflag_local_taskcallback local_arg_flags = cic_eth.cli.argflag_local_taskcallback
argparser = cic_eth.cli.ArgumentParser(arg_flags) argparser = cic_eth.cli.ArgumentParser(arg_flags)
argparser.add_positional('tag', type=str, help='address tag') argparser.add_positional('tag', type=str, help='address tag')

View File

@@ -13,6 +13,7 @@ from chainlib.eth.nonce import RPCNonceOracle
from chainlib.eth.gas import RPCGasOracle from chainlib.eth.gas import RPCGasOracle
from cic_eth_registry import CICRegistry from cic_eth_registry import CICRegistry
from cic_eth_registry.error import UnknownContractError from cic_eth_registry.error import UnknownContractError
import liveness.linux
# local imports # local imports
from cic_eth.error import SeppukuError from cic_eth.error import SeppukuError
@@ -47,7 +48,6 @@ class BaseTask(celery.Task):
def on_failure(self, exc, task_id, args, kwargs, einfo): def on_failure(self, exc, task_id, args, kwargs, einfo):
if isinstance(exc, SeppukuError): if isinstance(exc, SeppukuError):
import liveness.linux
liveness.linux.reset(rundir=self.run_dir) liveness.linux.reset(rundir=self.run_dir)
logg.critical(einfo) logg.critical(einfo)
msg = 'received critical exception {}, calling shutdown'.format(str(exc)) msg = 'received critical exception {}, calling shutdown'.format(str(exc))

View File

@@ -9,8 +9,8 @@ import semver
version = ( version = (
0, 0,
12, 12,
4, 2,
'alpha.8', 'alpha.4',
) )
version_object = semver.VersionInfo( version_object = semver.VersionInfo(

View File

@@ -0,0 +1,4 @@
[signer]
secret = deadbeef
#database = crypto-dev-signer
socket_path = ipc:///run/crypto-dev-signer/jsonrpc.ipc

View File

@@ -0,0 +1,8 @@
[database]
NAME=cic-signer
USER=
PASSWORD=
HOST=
PORT=
ENGINE=
DRIVER=

View File

@@ -1,6 +1,8 @@
@node cic-eth configuration @node cic-eth configuration
@section Configuration @section Configuration
(refer to @code{cic-base} for a general overview of the config pipeline)
Configuration parameters are grouped by configuration filename. Configuration parameters are grouped by configuration filename.
@@ -38,26 +40,7 @@ Boolean value. If set, the amount of available context for a task in the result
@subsection database @subsection database
@table @var See ref cic-base when ready
@item host
Database host
@item port
Database port
@item name
Database name
@item user
Database user
@item password
Database password
@item engine
The engine part of the dsn connection string (@code{postgresql} in @code{postgresql+psycopg2})
@item driver
The driver part of the dsn connection string (@code{psycopg2} in @code{postgresql+psycopg2})
@item pool_size
Connection pool size for database drivers that provide connection pooling
@item debug
Output actual sql queries to logs. Potentially very verbose
@end table
@subsection eth @subsection eth

View File

@@ -8,44 +8,30 @@ FROM registry.gitlab.com/grassrootseconomics/cic-base-images:python-3.8.6-dev-55
ARG EXTRA_INDEX_URL="https://pip.grassrootseconomics.net:8433" ARG EXTRA_INDEX_URL="https://pip.grassrootseconomics.net:8433"
ARG GITLAB_PYTHON_REGISTRY="https://gitlab.com/api/v4/projects/27624814/packages/pypi/simple" 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 \ #RUN --mount=type=cache,mode=0755,target=/root/.cache/pip \
# pip install --index-url https://pypi.org/simple \ # pip install --index-url https://pypi.org/simple \
# --force-reinstall \ # --force-reinstall \
# --extra-index-url $GITLAB_PYTHON_REGISTRY --extra-index-url $EXTRA_INDEX_URL \ # --extra-index-url $GITLAB_PYTHON_REGISTRY --extra-index-url $EXTRA_INDEX_URL \
# -r requirements.txt # -r requirements.txt
RUN --mount=type=cache,mode=0755,target=/root/.cache/pip \
pip install --index-url https://pypi.org/simple \
--extra-index-url $GITLAB_PYTHON_REGISTRY \
--extra-index-url $EXTRA_INDEX_URL \
$EXTRA_PIP_ARGS \
cic-eth-aux-erc20-demurrage-token~=0.0.2a6
COPY *requirements.txt ./ COPY *requirements.txt ./
RUN --mount=type=cache,mode=0755,target=/root/.cache/pip \ RUN --mount=type=cache,mode=0755,target=/root/.cache/pip \
pip install --index-url https://pypi.org/simple \ pip install --index-url https://pypi.org/simple \
--extra-index-url $GITLAB_PYTHON_REGISTRY \ --extra-index-url $GITLAB_PYTHON_REGISTRY \
--extra-index-url $EXTRA_INDEX_URL \ --extra-index-url $EXTRA_INDEX_URL \
$EXTRA_PIP_ARGS \
-r requirements.txt \ -r requirements.txt \
-r services_requirements.txt \ -r services_requirements.txt \
-r admin_requirements.txt -r admin_requirements.txt
# always install the latest signer
RUN --mount=type=cache,mode=0755,target=/root/.cache/pip \
pip install --index-url https://pypi.org/simple \
--extra-index-url $GITLAB_PYTHON_REGISTRY \
--extra-index-url $EXTRA_INDEX_URL \
$EXTRA_PIP_ARGS \
crypto-dev-signer
COPY . . COPY . .
RUN python setup.py install RUN python setup.py install
ENV PYTHONPATH . ENV PYTHONPATH .
RUN --mount=type=cache,mode=0755,target=/root/.cache/pip \
pip install --index-url https://pypi.org/simple \
--extra-index-url $GITLAB_PYTHON_REGISTRY \
--extra-index-url $EXTRA_INDEX_URL \
cic-eth-aux-erc20-demurrage-token~=0.0.2a6
COPY docker/entrypoints/* ./ COPY docker/entrypoints/* ./
RUN chmod 755 *.sh RUN chmod 755 *.sh

View File

@@ -0,0 +1,69 @@
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"
#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 \
-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 <dir> (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 <dir> (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 []
#

View File

@@ -5,27 +5,27 @@ set -e
# set CONFINI_ENV_PREFIX to override the env prefix to override env vars # set CONFINI_ENV_PREFIX to override the env prefix to override env vars
#echo "!!! starting signer" echo "!!! starting signer"
#python /usr/local/bin/crypto-dev-daemon -c /usr/local/etc/crypto-dev-signer -vv 2> /tmp/signer.log & python /usr/local/bin/crypto-dev-daemon -c /usr/local/etc/crypto-dev-signer -vv 2> /tmp/signer.log &
echo "!!! starting taskerd" echo "!!! starting taskerd"
/usr/local/bin/cic-eth-taskerd $@ /usr/local/bin/cic-eth-taskerd $@
# thanks! https://docs.docker.com/config/containers/multi-service_container/ # thanks! https://docs.docker.com/config/containers/multi-service_container/
#sleep 1; sleep 1;
#echo "!!! entering monitor loop" echo "!!! entering monitor loop"
#while true; do while true; do
# ps aux | grep crypto-dev-daemon | grep -q -v grep ps aux | grep crypto-dev-daemon | grep -q -v grep
# PROCESS_1_STATUS=$? PROCESS_1_STATUS=$?
# ps aux | grep cic-eth-tasker |grep -q -v grep ps aux | grep cic-eth-tasker |grep -q -v grep
# PROCESS_2_STATUS=$? PROCESS_2_STATUS=$?
# # If the greps above find anything, they exit with 0 status # If the greps above find anything, they exit with 0 status
# # If they are not both 0, then something is wrong # If they are not both 0, then something is wrong
# if [ $PROCESS_1_STATUS -ne 0 -o $PROCESS_2_STATUS -ne 0 ]; then if [ $PROCESS_1_STATUS -ne 0 -o $PROCESS_2_STATUS -ne 0 ]; then
# echo "One of the processes has already exited." echo "One of the processes has already exited."
# exit 1 exit 1
# fi fi
# sleep 15; sleep 15;
#done done
#
set +e set +e

View File

@@ -1,11 +0,0 @@
#! /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

View File

@@ -1,3 +1,3 @@
celery==4.4.7 celery==4.4.7
chainlib-eth>=0.0.9a14,<0.1.0 chainlib-eth>=0.0.7a7,<0.1.0
semver==2.13.0 semver==2.13.0

View File

@@ -1,15 +1,15 @@
chainqueue>=0.0.5a1,<0.1.0 chainqueue>=0.0.3a2,<0.1.0
chainsyncer[sql]>=0.0.6a3,<0.1.0 chainsyncer[sql]>=0.0.6a1,<0.1.0
alembic==1.4.2 alembic==1.4.2
confini>=0.3.6rc4,<0.5.0 confini>=0.3.6rc4,<0.5.0
redis==3.5.3 redis==3.5.3
hexathon~=0.0.1a8 hexathon~=0.0.1a7
pycryptodome==3.10.1 pycryptodome==3.10.1
liveness~=0.0.1a7 liveness~=0.0.1a7
eth-address-index>=0.2.3a4,<0.3.0 eth-address-index>=0.1.4a1,<0.2.0
eth-accounts-index>=0.1.2a3,<0.2.0 eth-accounts-index>=0.0.14a1,<0.1.0
cic-eth-registry>=0.6.1a2,<0.7.0 cic-eth-registry>=0.5.8a1,<0.6.0
erc20-faucet>=0.3.2a2,<0.4.0 erc20-faucet>=0.2.4a2,<0.3.0
erc20-transfer-authorization>=0.3.5a2,<0.4.0 erc20-transfer-authorization>=0.3.4a1,<0.4.0
sarafu-faucet>=0.0.7a2,<0.1.0 sarafu-faucet>=0.0.5a2,<0.1.0
moolb~=0.1.1b2 moolb~=0.1.1b2

View File

@@ -6,4 +6,4 @@ pytest-redis==2.0.0
redis==3.5.3 redis==3.5.3
eth-tester==0.5.0b3 eth-tester==0.5.0b3
py-evm==0.3.0a20 py-evm==0.3.0a20
eth-erc20~=0.1.2a2 eth-erc20~=0.0.12a1

View File

@@ -200,7 +200,6 @@ def test_callback_filter(
assert r['status'] == 1 assert r['status'] == 1
rcpt = snake_and_camel(r) rcpt = snake_and_camel(r)
tx.block.hash = rcpt['block_hash']
tx.apply_receipt(rcpt) tx.apply_receipt(rcpt)
fltr = CallbackFilter(default_chain_spec, None, None, caller_address=contract_roles['CONTRACT_DEPLOYER']) fltr = CallbackFilter(default_chain_spec, None, None, caller_address=contract_roles['CONTRACT_DEPLOYER'])

View File

@@ -1,6 +1,7 @@
# external imports # external imports
from chainlib.connection import RPCConnection from chainlib.connection import RPCConnection
from chainlib.eth.nonce import OverrideNonceOracle from chainlib.eth.nonce import OverrideNonceOracle
from chainqueue.sql.tx import create as queue_create
from chainlib.eth.tx import ( from chainlib.eth.tx import (
TxFormat, TxFormat,
unpack, unpack,
@@ -25,8 +26,6 @@ from chainqueue.db.enum import StatusBits
# local imports # local imports
from cic_eth.runnable.daemons.filters.gas import GasFilter from cic_eth.runnable.daemons.filters.gas import GasFilter
from cic_eth.eth.gas import cache_gas_data from cic_eth.eth.gas import cache_gas_data
from cic_eth.encode import tx_normalize
from cic_eth.queue.tx import queue_create
def test_filter_gas( def test_filter_gas(

View File

@@ -22,11 +22,10 @@ from hexathon import (
strip_0x, strip_0x,
add_0x, add_0x,
) )
from chainqueue.sql.query import get_account_tx
# local imports # local imports
from cic_eth.runnable.daemons.filters.register import RegistrationFilter from cic_eth.runnable.daemons.filters.register import RegistrationFilter
from cic_eth.encode import tx_normalize
from cic_eth.queue.query import get_account_tx_local
logg = logging.getLogger() logg = logging.getLogger()
@@ -80,7 +79,7 @@ def test_register_filter(
t.get_leaf() t.get_leaf()
assert t.successful() assert t.successful()
gift_txs = get_account_tx_local(default_chain_spec, agent_roles['ALICE'], as_sender=True, session=init_database) gift_txs = get_account_tx(default_chain_spec.asdict(), agent_roles['ALICE'], as_sender=True, session=init_database)
ks = list(gift_txs.keys()) ks = list(gift_txs.keys())
assert len(ks) == 1 assert len(ks) == 1

View File

@@ -1,10 +0,0 @@
#! /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

View File

@@ -34,6 +34,10 @@ from chainqueue.sql.state import (
set_ready, set_ready,
set_reserved, set_reserved,
) )
from chainqueue.sql.query import (
get_tx,
get_nonce_tx_cache,
)
# local imports # local imports
from cic_eth.api.admin import AdminApi from cic_eth.api.admin import AdminApi
@@ -42,11 +46,6 @@ from cic_eth.db.enum import LockEnum
from cic_eth.error import InitializationError from cic_eth.error import InitializationError
from cic_eth.eth.gas import cache_gas_data from cic_eth.eth.gas import cache_gas_data
from cic_eth.queue.tx import queue_create from cic_eth.queue.tx import queue_create
from cic_eth.queue.query import (
get_tx,
get_nonce_tx_local,
)
from cic_eth.encode import tx_normalize
logg = logging.getLogger() logg = logging.getLogger()
@@ -287,15 +286,13 @@ def test_fix_nonce(
assert t.successful() assert t.successful()
init_database.commit() init_database.commit()
logg.debug('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!') txs = get_nonce_tx_cache(default_chain_spec, 3, agent_roles['ALICE'], session=init_database)
txs = get_nonce_tx_local(default_chain_spec, 3, agent_roles['ALICE'], session=init_database)
ks = txs.keys() ks = txs.keys()
assert len(ks) == 2 assert len(ks) == 2
for k in ks: for k in ks:
#hsh = add_0x(k) hsh = add_0x(k)
hsh = tx_normalize.tx_hash(k)
otx = Otx.load(hsh, session=init_database) otx = Otx.load(hsh, session=init_database)
init_database.refresh(otx) init_database.refresh(otx)
logg.debug('checking nonce {} tx {} status {}'.format(3, otx.tx_hash, otx.status)) logg.debug('checking nonce {} tx {} status {}'.format(3, otx.tx_hash, otx.status))

View File

@@ -30,6 +30,7 @@ from chainqueue.sql.state import (
) )
from chainqueue.db.models.otx import Otx from chainqueue.db.models.otx import Otx
from chainqueue.db.enum import StatusBits from chainqueue.db.enum import StatusBits
from chainqueue.sql.query import get_nonce_tx_cache
from eth_erc20 import ERC20 from eth_erc20 import ERC20
from cic_eth_registry import CICRegistry from cic_eth_registry import CICRegistry
@@ -37,7 +38,6 @@ from cic_eth_registry import CICRegistry
from cic_eth.api.admin import AdminApi from cic_eth.api.admin import AdminApi
from cic_eth.eth.gas import cache_gas_data from cic_eth.eth.gas import cache_gas_data
from cic_eth.eth.erc20 import cache_transfer_data from cic_eth.eth.erc20 import cache_transfer_data
from cic_eth.queue.query import get_nonce_tx_local
logg = logging.getLogger() logg = logging.getLogger()
@@ -312,7 +312,7 @@ def test_resend_inplace(
otx = Otx.load(tx_hash_hex, session=init_database) otx = Otx.load(tx_hash_hex, session=init_database)
assert otx.status & StatusBits.OBSOLETE == StatusBits.OBSOLETE assert otx.status & StatusBits.OBSOLETE == StatusBits.OBSOLETE
txs = get_nonce_tx_local(default_chain_spec, otx.nonce, agent_roles['ALICE'], session=init_database) txs = get_nonce_tx_cache(default_chain_spec, otx.nonce, agent_roles['ALICE'], session=init_database)
assert len(txs) == 2 assert len(txs) == 2
@@ -363,10 +363,10 @@ def test_resend_clone(
assert otx.status & StatusBits.IN_NETWORK == StatusBits.IN_NETWORK assert otx.status & StatusBits.IN_NETWORK == StatusBits.IN_NETWORK
assert otx.status & StatusBits.OBSOLETE == StatusBits.OBSOLETE assert otx.status & StatusBits.OBSOLETE == StatusBits.OBSOLETE
txs = get_nonce_tx_local(default_chain_spec, otx.nonce, agent_roles['ALICE'], session=init_database) txs = get_nonce_tx_cache(default_chain_spec, otx.nonce, agent_roles['ALICE'], session=init_database)
assert len(txs) == 1 assert len(txs) == 1
txs = get_nonce_tx_local(default_chain_spec, otx.nonce + 1, agent_roles['ALICE'], session=init_database) txs = get_nonce_tx_cache(default_chain_spec, otx.nonce + 1, agent_roles['ALICE'], session=init_database)
assert len(txs) == 1 assert len(txs) == 1
otx = Otx.load(txs[0], session=init_database) otx = Otx.load(txs[0], session=init_database)

View File

@@ -21,6 +21,7 @@ from chainlib.eth.constant import (
MINIMUM_FEE_UNITS, MINIMUM_FEE_UNITS,
MINIMUM_FEE_PRICE, MINIMUM_FEE_PRICE,
) )
from chainqueue.sql.tx import create as queue_create
from chainqueue.sql.query import get_tx from chainqueue.sql.query import get_tx
from chainqueue.db.enum import StatusBits from chainqueue.db.enum import StatusBits
from chainqueue.sql.state import ( from chainqueue.sql.state import (
@@ -34,7 +35,6 @@ from hexathon import strip_0x
# local imports # local imports
from cic_eth.eth.gas import cache_gas_data from cic_eth.eth.gas import cache_gas_data
from cic_eth.error import OutOfGasError from cic_eth.error import OutOfGasError
from cic_eth.queue.tx import queue_create
logg = logging.getLogger() logg = logging.getLogger()
@@ -75,7 +75,7 @@ def test_task_check_gas_ok(
'cic_eth.eth.gas.check_gas', 'cic_eth.eth.gas.check_gas',
[ [
[ [
strip_0x(tx_hash_hex), tx_hash_hex,
], ],
default_chain_spec.asdict(), default_chain_spec.asdict(),
[], [],
@@ -283,3 +283,4 @@ def test_task_resend_explicit(
tx_after = unpack(bytes.fromhex(strip_0x(otx.signed_tx)), default_chain_spec) tx_after = unpack(bytes.fromhex(strip_0x(otx.signed_tx)), default_chain_spec)
logg.debug('gasprices before {} after {}'.format(tx_before['gasPrice'], tx_after['gasPrice'])) logg.debug('gasprices before {} after {}'.format(tx_before['gasPrice'], tx_after['gasPrice']))
assert tx_after['gasPrice'] > tx_before['gasPrice'] assert tx_after['gasPrice'] > tx_before['gasPrice']

View File

@@ -51,7 +51,6 @@ def test_ext_tx_collate(
tx_hash_hex, tx_hash_hex,
tx_signed_raw_hex, tx_signed_raw_hex,
) )
otx.block = 666
init_database.add(otx) init_database.add(otx)
init_database.commit() init_database.commit()

View File

@@ -46,7 +46,6 @@ def test_set(
tx_hash_hex, tx_hash_hex,
tx_signed_raw_hex, tx_signed_raw_hex,
) )
otx.block = 666
init_database.add(otx) init_database.add(otx)
init_database.commit() init_database.commit()
@@ -75,6 +74,7 @@ def test_set(
assert (tx_stored.destination_token_address == ZERO_ADDRESS) assert (tx_stored.destination_token_address == ZERO_ADDRESS)
assert (tx_stored.from_value == tx['value']) assert (tx_stored.from_value == tx['value'])
assert (tx_stored.to_value == to_value) assert (tx_stored.to_value == to_value)
assert (tx_stored.block_number == 666)
assert (tx_stored.tx_index == 13) assert (tx_stored.tx_index == 13)

View File

@@ -13,7 +13,6 @@ from cic_eth.queue.balance import (
balance_incoming, balance_incoming,
assemble_balances, assemble_balances,
) )
from cic_eth.encode import tx_normalize
logg = logging.getLogger() logg = logging.getLogger()
@@ -52,8 +51,8 @@ def test_assemble():
r = assemble_balances(b) r = assemble_balances(b)
logg.debug('r {}'.format(r)) logg.debug('r {}'.format(r))
assert r[0]['address'] == tx_normalize.executable_address(token_foo) assert r[0]['address'] == token_foo
assert r[1]['address'] == tx_normalize.executable_address(token_bar) assert r[1]['address'] == token_bar
assert r[0].get('balance_foo') != None assert r[0].get('balance_foo') != None
assert r[0].get('balance_bar') != None assert r[0].get('balance_bar') != None
assert r[1].get('balance_baz') != None assert r[1].get('balance_baz') != None
@@ -75,11 +74,11 @@ def test_outgoing_balance(
token_address = '0x' + os.urandom(20).hex() token_address = '0x' + os.urandom(20).hex()
sender = '0x' + os.urandom(20).hex() sender = '0x' + os.urandom(20).hex()
txc = TxCache( txc = TxCache(
tx_normalize.tx_hash(tx_hash), tx_hash,
tx_normalize.wallet_address(sender), sender,
tx_normalize.wallet_address(recipient), recipient,
tx_normalize.executable_address(token_address), token_address,
tx_normalize.executable_address(token_address), token_address,
1000, 1000,
1000, 1000,
session=init_database, session=init_database,
@@ -126,11 +125,11 @@ def test_incoming_balance(
token_address = '0x' + os.urandom(20).hex() token_address = '0x' + os.urandom(20).hex()
sender = '0x' + os.urandom(20).hex() sender = '0x' + os.urandom(20).hex()
txc = TxCache( txc = TxCache(
tx_normalize.tx_hash(tx_hash), tx_hash,
tx_normalize.wallet_address(sender), sender,
tx_normalize.wallet_address(recipient), recipient,
tx_normalize.executable_address(token_address), token_address,
tx_normalize.executable_address(token_address), token_address,
1000, 1000,
1000, 1000,
session=init_database, session=init_database,

View File

@@ -21,7 +21,6 @@ from cic_eth.db.models.lock import Lock
from cic_eth.queue.query import get_upcoming_tx from cic_eth.queue.query import get_upcoming_tx
from cic_eth.queue.tx import register_tx from cic_eth.queue.tx import register_tx
from cic_eth.eth.gas import cache_gas_data from cic_eth.eth.gas import cache_gas_data
from cic_eth.encode import tx_normalize
# test imports # test imports
from tests.util.nonce import StaticNonceOracle from tests.util.nonce import StaticNonceOracle
@@ -40,8 +39,8 @@ def test_upcoming_with_lock(
gas_oracle = RPCGasOracle(eth_rpc) gas_oracle = RPCGasOracle(eth_rpc)
c = Gas(default_chain_spec, signer=eth_signer, nonce_oracle=nonce_oracle, gas_oracle=gas_oracle) c = Gas(default_chain_spec, signer=eth_signer, nonce_oracle=nonce_oracle, gas_oracle=gas_oracle)
alice_normal = tx_normalize.wallet_address(agent_roles['ALICE']) alice_normal = add_0x(hex_uniform(strip_0x(agent_roles['ALICE'])))
bob_normal = tx_normalize.wallet_address(agent_roles['BOB']) bob_normal = add_0x(hex_uniform(strip_0x(agent_roles['BOB'])))
(tx_hash_hex, tx_rpc) = c.create(alice_normal, bob_normal, 100 * (10 ** 6)) (tx_hash_hex, tx_rpc) = c.create(alice_normal, bob_normal, 100 * (10 ** 6))
tx_signed_raw_hex = tx_rpc['params'][0] tx_signed_raw_hex = tx_rpc['params'][0]

View File

@@ -9,7 +9,7 @@ from cic_eth.db.models.lock import Lock
from cic_eth.db.enum import LockEnum from cic_eth.db.enum import LockEnum
from cic_eth.error import LockedError from cic_eth.error import LockedError
from cic_eth.queue.tx import queue_create from cic_eth.queue.tx import queue_create
from cic_eth.encode import tx_normalize
def test_queue_lock( def test_queue_lock(
init_database, init_database,
@@ -21,8 +21,6 @@ def test_queue_lock(
address = '0x' + os.urandom(20).hex() address = '0x' + os.urandom(20).hex()
tx_hash = '0x' + os.urandom(32).hex() tx_hash = '0x' + os.urandom(32).hex()
tx_raw = '0x' + os.urandom(128).hex() tx_raw = '0x' + os.urandom(128).hex()
address_normal = tx_normalize.wallet_address(address)
tx_hash_normal = tx_normalize.tx_hash(tx_hash)
Lock.set(chain_str, LockEnum.QUEUE) Lock.set(chain_str, LockEnum.QUEUE)
with pytest.raises(LockedError): with pytest.raises(LockedError):
@@ -34,7 +32,7 @@ def test_queue_lock(
tx_raw, tx_raw,
) )
Lock.set(chain_str, LockEnum.QUEUE, address=address_normal) Lock.set(chain_str, LockEnum.QUEUE, address=address)
with pytest.raises(LockedError): with pytest.raises(LockedError):
queue_create( queue_create(
default_chain_spec, default_chain_spec,
@@ -54,7 +52,7 @@ def test_queue_lock(
tx_raw, tx_raw,
) )
Lock.set(chain_str, LockEnum.QUEUE, address=address_normal, tx_hash=tx_hash_normal) Lock.set(chain_str, LockEnum.QUEUE, address=address, tx_hash=tx_hash)
with pytest.raises(LockedError): with pytest.raises(LockedError):
queue_create( queue_create(
default_chain_spec, default_chain_spec,
@@ -63,3 +61,5 @@ def test_queue_lock(
tx_hash, tx_hash,
tx_raw, tx_raw,
) )

View File

@@ -1,7 +1,8 @@
crypto-dev-signer>=0.4.15a7,<=0.4.15 crypto-dev-signer>=0.4.14b7,<=0.4.14
chainqueue>=0.0.5a1,<0.1.0 chainqueue>=0.0.3a1,<0.1.0
cic-eth-registry>=0.6.1a2,<0.7.0 confini>=0.3.6rc4,<0.5.0
cic-eth-registry>=0.5.8a1,<0.6.0
redis==3.5.3 redis==3.5.3
hexathon~=0.0.1a8 hexathon~=0.0.1a7
pycryptodome==3.10.1 pycryptodome==3.10.1
pyxdg==0.27 pyxdg==0.27

View File

@@ -1,16 +1,43 @@
build-test-cic-meta:
stage: test .cic_meta_variables:
tags: variables:
- integration APP_NAME: cic-meta
variables: DOCKERFILE_PATH: docker/Dockerfile_ci
APP_NAME: cic-meta CONTEXT: apps/$APP_NAME
MR_IMAGE_TAG: mr-$APP_NAME-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA
script: build-mr-cic-meta:
- cd apps/cic-meta extends:
- docker build -t $MR_IMAGE_TAG -f docker/Dockerfile . - .py_build_merge_request
- docker run --entrypoint=sh $MR_IMAGE_TAG docker/run_tests.sh - .cic_meta_variables
rules: rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_PIPELINE_SOURCE == "merge_request_event"
changes: changes:
- apps/$APP_NAME/**/* - 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 when: always

View File

@@ -1 +0,0 @@
# CIC-Meta

View File

@@ -15,10 +15,11 @@ RUN --mount=type=cache,mode=0755,target=/root/.npm \
COPY webpack.config.js . COPY webpack.config.js .
COPY tsconfig.json . COPY tsconfig.json .
## required to build the cic-client-meta module ## required to build the cic-client-meta module
COPY . . COPY src/ src/
COPY scripts/ scripts/
COPY tests/ tests/
COPY tests/*.asc /root/pgp/ COPY tests/*.asc /root/pgp/
## copy runtime configs ## copy runtime configs
COPY .config/ /usr/local/etc/cic-meta/ COPY .config/ /usr/local/etc/cic-meta/
# #

View File

@@ -0,0 +1,32 @@
# 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"]

View File

@@ -1,7 +0,0 @@
#! /bin/bash
set -e
npm install --dev
npm run test
npm run test:coverage

View File

@@ -1,17 +1,52 @@
build-test-cic-notify: .cic_notify_variables:
stage: test variables:
tags: APP_NAME: cic-notify
- integration DOCKERFILE_PATH: docker/Dockerfile_ci
variables: CONTEXT: apps/$APP_NAME
APP_NAME: cic-notify
MR_IMAGE_TAG: mr-$APP_NAME-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA build-mr-cic-notify:
script: extends:
- cd apps/cic-notify - .py_build_merge_request
- docker build -t $MR_IMAGE_TAG -f docker/Dockerfile . - .cic_notify_variables
- docker run $MR_IMAGE_TAG sh docker/run_tests.sh rules:
allow_failure: true - if: $CI_PIPELINE_SOURCE == "merge_request_event"
rules: changes:
- if: $CI_PIPELINE_SOURCE == "merge_request_event" - apps/cic-notify/**/*
changes: when: always
- apps/$APP_NAME/**/*
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

View File

@@ -11,12 +11,12 @@ celery_app = celery.current_app
@celery_app.task @celery_app.task
def persist_notification(message, recipient): def persist_notification(recipient, message):
""" """
:param message:
:type message:
:param recipient: :param recipient:
:type recipient: :type recipient:
:param message:
:type message:
:return: :return:
:rtype: :rtype:
""" """

View File

@@ -11,13 +11,12 @@ local_logg = logging.getLogger(__name__)
@celery_app.task @celery_app.task
def log(message, recipient): def log(recipient, message):
""" """
:param message:
:type message:
:param recipient: :param recipient:
:type recipient: :type recipient:
:param message:
:type message:
:return: :return:
:rtype: :rtype:
""" """

View File

@@ -0,0 +1,27 @@
# 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 <dir> (executable provided by confini package)
COPY .config/ /usr/local/etc/cic-notify/
COPY cic_notify/db/migrations/ /usr/local/share/cic-notify/alembic/
ENTRYPOINT []

View File

@@ -1,9 +0,0 @@
#! /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

View File

@@ -1,16 +1,52 @@
build-test-cic-ussd: .cic_ussd_variables:
stage: test variables:
tags: APP_NAME: cic-ussd
- integration DOCKERFILE_PATH: docker/Dockerfile_ci
variables: CONTEXT: apps/$APP_NAME
APP_NAME: cic-ussd
MR_IMAGE_TAG: mr-$APP_NAME-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA build-mr-cic-ussd:
script: extends:
- cd apps/cic-ussd - .py_build_merge_request
- docker build -t $MR_IMAGE_TAG -f docker/Dockerfile . - .cic_ussd_variables
- docker run $MR_IMAGE_TAG sh docker/run_tests.sh rules:
rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_PIPELINE_SOURCE == "merge_request_event" changes:
changes: - apps/cic-ussd/**/*
- apps/$APP_NAME/**/* when: always
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

View File

@@ -1,12 +1,10 @@
# standard imports # standard imports
import json import json
import logging import logging
from typing import Optional from typing import Optional
# third-party imports # third-party imports
from cic_eth.api import Api from cic_eth.api import Api
from cic_eth_aux.erc20_demurrage_token.api import Api as DemurrageApi
# local imports # local imports
from cic_ussd.account.transaction import from_wei from cic_ussd.account.transaction import from_wei
@@ -22,7 +20,7 @@ def get_balances(address: str,
asynchronous: bool = False, asynchronous: bool = False,
callback_param: any = None, callback_param: any = None,
callback_queue='cic-ussd', callback_queue='cic-ussd',
callback_task='cic_ussd.tasks.callback_handler.balances_callback') -> Optional[list]: callback_task='cic_ussd.tasks.callback_handler.process_balances_callback') -> Optional[list]:
"""This function queries cic-eth for an account's balances, It provides a means to receive the balance either """This function queries cic-eth for an account's balances, It provides a means to receive the balance either
asynchronously or synchronously.. It returns a dictionary containing the network, outgoing and incoming balances. asynchronously or synchronously.. It returns a dictionary containing the network, outgoing and incoming balances.
:param address: Ethereum address of an account. :param address: Ethereum address of an account.
@@ -75,24 +73,6 @@ def calculate_available_balance(balances: dict) -> float:
return from_wei(value=available_balance) return from_wei(value=available_balance)
def get_adjusted_balance(balance: int, chain_str: str, timestamp: int, token_symbol: str):
"""
:param balance:
:type balance:
:param chain_str:
:type chain_str:
:param timestamp:
:type timestamp:
:param token_symbol:
:type token_symbol:
:return:
:rtype:
"""
logg.debug(f'retrieving adjusted balance on chain: {chain_str}')
demurrage_api = DemurrageApi(chain_str=chain_str)
return demurrage_api.get_adjusted_balance(token_symbol, balance, timestamp).result
def get_cached_available_balance(blockchain_address: str) -> float: def get_cached_available_balance(blockchain_address: str) -> float:
"""This function attempts to retrieve balance data from the redis cache. """This function attempts to retrieve balance data from the redis cache.
:param blockchain_address: Ethereum address of an account. :param blockchain_address: Ethereum address of an account.
@@ -108,14 +88,3 @@ def get_cached_available_balance(blockchain_address: str) -> float:
return calculate_available_balance(json.loads(cached_balances)) return calculate_available_balance(json.loads(cached_balances))
else: else:
raise CachedDataNotFoundError(f'No cached available balance for address: {blockchain_address}') raise CachedDataNotFoundError(f'No cached available balance for address: {blockchain_address}')
def get_cached_adjusted_balance(identifier: bytes):
"""
:param identifier:
:type identifier:
:return:
:rtype:
"""
key = cache_data_key(identifier, ':cic.adjusted_balance')
return get_cached_data(key)

View File

@@ -1,6 +1,5 @@
# standard import # standard import
import decimal import decimal
import json
import logging import logging
from typing import Dict, Tuple from typing import Dict, Tuple
@@ -9,8 +8,6 @@ from cic_eth.api import Api
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
# local import # 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.account import Account
from cic_ussd.db.models.base import SessionBase from cic_ussd.db.models.base import SessionBase
from cic_ussd.error import UnknownUssdRecipient from cic_ussd.error import UnknownUssdRecipient
@@ -62,9 +59,7 @@ def from_wei(value: int) -> float:
:return: SRF equivalent of value in Wei :return: SRF equivalent of value in Wei
:rtype: float :rtype: float
""" """
cached_token_data = json.loads(get_cached_default_token(Chain.spec.__str__())) value = float(value) / 1e+6
token_decimals: int = cached_token_data.get('decimals')
value = float(value) / (10**token_decimals)
return truncate(value=value, decimals=2) return truncate(value=value, decimals=2)
@@ -75,9 +70,7 @@ def to_wei(value: int) -> int:
:return: Wei equivalent of value in SRF :return: Wei equivalent of value in SRF
:rtype: int :rtype: int
""" """
cached_token_data = json.loads(get_cached_default_token(Chain.spec.__str__())) return int(value * 1e+6)
token_decimals: int = cached_token_data.get('decimals')
return int(value * (10**token_decimals))
def truncate(value: float, decimals: int): def truncate(value: float, decimals: int):
@@ -124,18 +117,18 @@ def transaction_actors(transaction: dict) -> Tuple[Dict, Dict]:
return recipient_transaction_data, sender_transaction_data return recipient_transaction_data, sender_transaction_data
def validate_transaction_account(blockchain_address: str, role: str, session: Session) -> Account: def validate_transaction_account(session: Session, transaction: dict) -> Account:
"""This function checks whether the blockchain address specified in a parsed transaction object resolves to an """This function checks whether the blockchain address specified in a parsed transaction object resolves to an
account object in the ussd system. account object in the ussd system.
:param blockchain_address: :param session: Database session object.
:type blockchain_address: :type session: Session
:param role: :param transaction: Parsed transaction data object.
:type role: :type transaction: dict
:param session:
:type session:
:return: :return:
:rtype: :rtype:
""" """
blockchain_address = transaction.get('blockchain_address')
role = transaction.get('role')
session = SessionBase.bind_session(session) session = SessionBase.bind_session(session)
account = session.query(Account).filter_by(blockchain_address=blockchain_address).first() account = session.query(Account).filter_by(blockchain_address=blockchain_address).first()
if not account: if not account:

View File

@@ -44,7 +44,7 @@ class MetadataRequestsHandler(Metadata):
def create(self, data: Union[Dict, str]): def create(self, data: Union[Dict, str]):
"""""" """"""
data = json.dumps(data).encode('utf-8') data = json.dumps(data)
result = make_request(method='POST', url=self.url, data=data, headers=self.headers) result = make_request(method='POST', url=self.url, data=data, headers=self.headers)
error_handler(result=result) error_handler(result=result)

View File

@@ -1,17 +1,13 @@
# standard imports # standard imports
import json import json
import logging import logging
from datetime import datetime, timedelta
# external imports # external imports
import i18n.config import i18n.config
from sqlalchemy.orm.session import Session
# local imports # local imports
from cic_ussd.account.balance import (calculate_available_balance, from cic_ussd.account.balance import calculate_available_balance, get_balances, get_cached_available_balance
get_adjusted_balance,
get_balances,
get_cached_adjusted_balance,
get_cached_available_balance)
from cic_ussd.account.chain import Chain from cic_ussd.account.chain import Chain
from cic_ussd.account.metadata import get_cached_preferred_language from cic_ussd.account.metadata import get_cached_preferred_language
from cic_ussd.account.statement import ( from cic_ussd.account.statement import (
@@ -20,15 +16,14 @@ from cic_ussd.account.statement import (
query_statement, query_statement,
statement_transaction_set statement_transaction_set
) )
from cic_ussd.account.tokens import get_default_token_symbol
from cic_ussd.account.transaction import from_wei, to_wei from cic_ussd.account.transaction import from_wei, to_wei
from cic_ussd.account.tokens import get_default_token_symbol
from cic_ussd.cache import cache_data_key, cache_data from cic_ussd.cache import cache_data_key, cache_data
from cic_ussd.db.models.account import Account from cic_ussd.db.models.account import Account
from cic_ussd.metadata import PersonMetadata from cic_ussd.metadata import PersonMetadata
from cic_ussd.phone_number import Support from cic_ussd.phone_number import Support
from cic_ussd.processor.util import parse_person_metadata from cic_ussd.processor.util import latest_input, parse_person_metadata
from cic_ussd.translation import translation_for from cic_ussd.translation import translation_for
from sqlalchemy.orm.session import Session
logg = logging.getLogger(__name__) logg = logging.getLogger(__name__)
@@ -48,26 +43,21 @@ class MenuProcessor:
:rtype: :rtype:
""" """
available_balance = get_cached_available_balance(self.account.blockchain_address) available_balance = get_cached_available_balance(self.account.blockchain_address)
adjusted_balance = get_cached_adjusted_balance(self.identifier) logg.debug('Requires call to retrieve tax and bonus amounts')
tax = ''
bonus = ''
token_symbol = get_default_token_symbol() token_symbol = get_default_token_symbol()
preferred_language = get_cached_preferred_language(self.account.blockchain_address) preferred_language = get_cached_preferred_language(self.account.blockchain_address)
if not preferred_language: if not preferred_language:
preferred_language = i18n.config.get('fallback') preferred_language = i18n.config.get('fallback')
with_available_balance = f'{self.display_key}.available_balance' return translation_for(
with_fees = f'{self.display_key}.with_fees' key=self.display_key,
if not adjusted_balance: preferred_language=preferred_language,
return translation_for(key=with_available_balance, available_balance=available_balance,
preferred_language=preferred_language, tax=tax,
available_balance=available_balance, bonus=bonus,
token_symbol=token_symbol) token_symbol=token_symbol
adjusted_balance = json.loads(adjusted_balance) )
tax_wei = to_wei(int(available_balance)) - int(adjusted_balance)
tax = from_wei(int(tax_wei))
return translation_for(key=with_fees,
preferred_language=preferred_language,
available_balance=available_balance,
tax=tax,
token_symbol=token_symbol)
def account_statement(self) -> str: def account_statement(self) -> str:
""" """
@@ -77,7 +67,7 @@ class MenuProcessor:
cached_statement = get_cached_statement(self.account.blockchain_address) cached_statement = get_cached_statement(self.account.blockchain_address)
statement = json.loads(cached_statement) statement = json.loads(cached_statement)
statement_transactions = parse_statement_transactions(statement) statement_transactions = parse_statement_transactions(statement)
transaction_sets = [statement_transactions[tx:tx + 3] for tx in range(0, len(statement_transactions), 3)] transaction_sets = [statement_transactions[tx:tx+3] for tx in range(0, len(statement_transactions), 3)]
preferred_language = get_cached_preferred_language(self.account.blockchain_address) preferred_language = get_cached_preferred_language(self.account.blockchain_address)
if not preferred_language: if not preferred_language:
preferred_language = i18n.config.get('fallback') preferred_language = i18n.config.get('fallback')
@@ -159,22 +149,12 @@ class MenuProcessor:
:return: :return:
:rtype: :rtype:
""" """
chain_str = Chain.spec.__str__()
token_symbol = get_default_token_symbol() token_symbol = get_default_token_symbol()
blockchain_address = self.account.blockchain_address blockchain_address = self.account.blockchain_address
balances = get_balances(blockchain_address, chain_str, token_symbol, False)[0] balances = get_balances(blockchain_address, Chain.spec.__str__(), token_symbol, False)[0]
key = cache_data_key(self.identifier, ':cic.balances') key = cache_data_key(self.identifier, ':cic.balances')
cache_data(key, json.dumps(balances)) cache_data(key, json.dumps(balances))
available_balance = calculate_available_balance(balances) available_balance = calculate_available_balance(balances)
now = datetime.now()
if (now - self.account.created).days >= 30:
if available_balance <= 0:
logg.info(f'Not retrieving adjusted balance, available balance: {available_balance} is insufficient.')
else:
timestamp = int((now - timedelta(30)).timestamp())
adjusted_balance = get_adjusted_balance(to_wei(int(available_balance)), chain_str, timestamp, token_symbol)
key = cache_data_key(self.identifier, ':cic.adjusted_balance')
cache_data(key, json.dumps(adjusted_balance))
query_statement(blockchain_address) query_statement(blockchain_address)

View File

@@ -67,7 +67,6 @@ def resume_last_ussd_session(last_state: str) -> Document:
'exit', 'exit',
'exit_invalid_pin', 'exit_invalid_pin',
'exit_invalid_new_pin', 'exit_invalid_new_pin',
'exit_invalid_recipient',
'exit_invalid_request', 'exit_invalid_request',
'exit_pin_blocked', 'exit_pin_blocked',
'exit_pin_mismatch', 'exit_pin_mismatch',

View File

@@ -63,7 +63,7 @@ elif ssl == 0:
else: else:
ssl = True ssl = True
valid_service_codes = config.get('USSD_SERVICE_CODE').split(",")
def main(): def main():
# TODO: improve url building # TODO: improve url building
@@ -79,9 +79,9 @@ def main():
session = uuid.uuid4().hex session = uuid.uuid4().hex
data = { data = {
'sessionId': session, 'sessionId': session,
'serviceCode': valid_service_codes[0], 'serviceCode': config.get('APP_SERVICE_CODE'),
'phoneNumber': args.phone, 'phoneNumber': args.phone,
'text': "", 'text': config.get('APP_SERVICE_CODE'),
} }
state = "_BEGIN" state = "_BEGIN"

View File

@@ -146,7 +146,7 @@ def create_ussd_session(
) )
def update_ussd_session(ussd_session: DbUssdSession, def update_ussd_session(ussd_session: UssdSession,
user_input: str, user_input: str,
state: str, state: str,
data: Optional[dict] = None) -> UssdSession: data: Optional[dict] = None) -> UssdSession:

View File

@@ -154,14 +154,15 @@ def parse_person_metadata(account: Account, metadata: dict):
phone_number = account.phone_number phone_number = account.phone_number
date_registered = int(account.created.replace().timestamp()) date_registered = int(account.created.replace().timestamp())
blockchain_address = account.blockchain_address blockchain_address = account.blockchain_address
chain_str = Chain.spec.__str__() chain_spec = f'{Chain.spec.common_name()}:{Chain.spec.engine()}: {Chain.spec.chain_id()}'
if isinstance(metadata.get('identities'), dict): if isinstance(metadata.get('identities'), dict):
identities = metadata.get('identities') identities = metadata.get('identities')
else: else:
identities = manage_identity_data( identities = manage_identity_data(
blockchain_address=blockchain_address, blockchain_address=blockchain_address,
chain_str=chain_str blockchain_type=Chain.spec.engine(),
chain_spec=chain_spec
) )
return { return {

View File

@@ -4,7 +4,6 @@ from typing import Tuple
# third party imports # third party imports
import celery import celery
from phonenumbers.phonenumberutil import NumberParseException
# local imports # local imports
from cic_ussd.account.balance import get_cached_available_balance from cic_ussd.account.balance import get_cached_available_balance
@@ -22,21 +21,23 @@ logg = logging.getLogger(__file__)
def is_valid_recipient(state_machine_data: Tuple[str, dict, Account, Session]) -> bool: def is_valid_recipient(state_machine_data: Tuple[str, dict, Account, Session]) -> bool:
"""This function checks that a phone number provided as the recipient of a transaction does not match the sending """This function checks that a user exists, is not the initiator of the transaction, has an active account status
party's own phone number. and is authorized to perform standard transactions.
:param state_machine_data: A tuple containing user input, a ussd session and user object. :param state_machine_data: A tuple containing user input, a ussd session and user object.
:type state_machine_data: tuple :type state_machine_data: tuple
:return: A recipient account's validity for a transaction :return: A user's validity
:rtype: bool :rtype: bool
""" """
user_input, ussd_session, account, session = state_machine_data user_input, ussd_session, account, session = state_machine_data
try: phone_number = process_phone_number(user_input, E164Format.region)
phone_number = process_phone_number(user_input, E164Format.region) session = SessionBase.bind_session(session=session)
except NumberParseException: recipient = Account.get_by_phone_number(phone_number=phone_number, session=session)
phone_number = None SessionBase.release_session(session=session)
is_not_initiator = phone_number != account.phone_number is_not_initiator = phone_number != account.phone_number
is_present = Account.get_by_phone_number(phone_number, session) is not None has_active_account_status = False
return phone_number is not None and phone_number.startswith('+') and is_present and is_not_initiator if recipient:
has_active_account_status = recipient.get_status(session) == AccountStatus.ACTIVE.name
return is_not_initiator and has_active_account_status and recipient is not None
def is_valid_transaction_amount(state_machine_data: Tuple[str, dict, Account, Session]) -> bool: def is_valid_transaction_amount(state_machine_data: Tuple[str, dict, Account, Session]) -> bool:

View File

@@ -138,14 +138,26 @@ def transaction_balances_callback(self, result: list, param: dict, status_code:
balances_data = result[0] balances_data = result[0]
available_balance = calculate_available_balance(balances_data) available_balance = calculate_available_balance(balances_data)
transaction = param transaction = param
blockchain_address = param.get('blockchain_address')
transaction['available_balance'] = available_balance transaction['available_balance'] = available_balance
queue = self.request.delivery_info.get('routing_key') 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( s_process_account_metadata = celery.signature(
'cic_ussd.tasks.processor.parse_transaction', [transaction], queue=queue 'cic_ussd.tasks.processor.parse_transaction', [transaction], queue=queue
) )
s_notify_account = celery.signature('cic_ussd.tasks.notifications.transaction', queue=queue) s_notify_account = celery.signature('cic_ussd.tasks.notifications.transaction', queue=queue)
celery.chain(s_process_account_metadata, s_notify_account).apply_async()
if param.get('transaction_type') == 'transfer':
celery.chain(s_preferences_metadata, s_process_account_metadata, s_notify_account).apply_async()
if param.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_app.task @celery_app.task
@@ -172,11 +184,10 @@ def transaction_callback(result: dict, param: str, status_code: int):
source_token_value = result.get('source_token_value') source_token_value = result.get('source_token_value')
recipient_metadata = { recipient_metadata = {
"alt_blockchain_address": sender_blockchain_address,
"blockchain_address": recipient_blockchain_address,
"role": "recipient",
"token_symbol": destination_token_symbol, "token_symbol": destination_token_symbol,
"token_value": destination_token_value, "token_value": destination_token_value,
"blockchain_address": recipient_blockchain_address,
"role": "recipient",
"transaction_type": param "transaction_type": param
} }
@@ -190,11 +201,10 @@ def transaction_callback(result: dict, param: str, status_code: int):
if param == 'transfer': if param == 'transfer':
sender_metadata = { sender_metadata = {
"alt_blockchain_address": recipient_blockchain_address,
"blockchain_address": sender_blockchain_address, "blockchain_address": sender_blockchain_address,
"role": "sender",
"token_symbol": source_token_symbol, "token_symbol": source_token_symbol,
"token_value": source_token_value, "token_value": source_token_value,
"role": "sender",
"transaction_type": param "transaction_type": param
} }

View File

@@ -24,14 +24,12 @@ def transaction(notification_data: dict):
:rtype: :rtype:
""" """
role = notification_data.get('role') role = notification_data.get('role')
token_value = notification_data.get('token_value') amount = from_wei(notification_data.get('token_value'))
amount = token_value if token_value == 0 else from_wei(token_value)
balance = notification_data.get('available_balance') balance = notification_data.get('available_balance')
phone_number = notification_data.get('phone_number') phone_number = notification_data.get('phone_number')
preferred_language = notification_data.get('preferred_language') preferred_language = notification_data.get('preferred_language')
token_symbol = notification_data.get('token_symbol') token_symbol = notification_data.get('token_symbol')
alt_metadata_id = notification_data.get('alt_metadata_id') transaction_account_metadata = notification_data.get('metadata_id')
metadata_id = notification_data.get('metadata_id')
transaction_type = notification_data.get('transaction_type') transaction_type = notification_data.get('transaction_type')
timestamp = datetime.datetime.now().strftime('%d-%m-%y, %H:%M %p') timestamp = datetime.datetime.now().strftime('%d-%m-%y, %H:%M %p')
@@ -49,8 +47,7 @@ def transaction(notification_data: dict):
preferred_language=preferred_language, preferred_language=preferred_language,
amount=amount, amount=amount,
token_symbol=token_symbol, token_symbol=token_symbol,
tx_recipient_information=metadata_id, tx_sender_information=transaction_account_metadata,
tx_sender_information=alt_metadata_id,
timestamp=timestamp, timestamp=timestamp,
balance=balance) balance=balance)
if role == 'sender': if role == 'sender':
@@ -59,7 +56,6 @@ def transaction(notification_data: dict):
preferred_language=preferred_language, preferred_language=preferred_language,
amount=amount, amount=amount,
token_symbol=token_symbol, token_symbol=token_symbol,
tx_recipient_information=alt_metadata_id, tx_recipient_information=transaction_account_metadata,
tx_sender_information=metadata_id,
timestamp=timestamp, timestamp=timestamp,
balance=balance) balance=balance)

View File

@@ -8,11 +8,9 @@ import i18n
from chainlib.hash import strip_0x from chainlib.hash import strip_0x
# local imports # 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.statement import get_cached_statement
from cic_ussd.account.transaction import aux_transaction_data, validate_transaction_account from cic_ussd.account.transaction import aux_transaction_data, validate_transaction_account
from cic_ussd.cache import cache_data, cache_data_key from cic_ussd.cache import cache_data, cache_data_key
from cic_ussd.db.models.account import Account
from cic_ussd.db.models.base import SessionBase from cic_ussd.db.models.base import SessionBase
@@ -59,32 +57,28 @@ def cache_statement(parsed_transaction: dict, querying_party: str):
@celery_app.task @celery_app.task
def parse_transaction(transaction: dict) -> dict: def parse_transaction(preferences: dict, transaction: dict) -> dict:
"""This function parses transaction objects and collates all relevant data for system use i.e: """This function parses transaction objects and collates all relevant data for system use i.e:
- An account's set preferred language. - An account's set preferred language.
- Account identifier that facilitates notification. - Account identifier that facilitates notification.
- Contextual tags i.e action and direction tags. - Contextual tags i.e action and direction tags.
:param preferences: An account's set preferences.
:type preferences: dict
:param transaction: Transaction object. :param transaction: Transaction object.
:type transaction: dict :type transaction: dict
:return: Transaction object with contextual data for use in the system. :return: Transaction object with contextual data for use in the system.
:rtype: dict :rtype: dict
""" """
preferred_language = get_cached_preferred_language(transaction.get('blockchain_address')) preferred_language = preferences.get('preferred_language')
if not preferred_language: if not preferred_language:
preferred_language = i18n.config.get('fallback') preferred_language = i18n.config.get('fallback')
transaction['preferred_language'] = preferred_language
transaction = aux_transaction_data(preferred_language, transaction) transaction = aux_transaction_data(preferred_language, transaction)
session = SessionBase.create_session() session = SessionBase.create_session()
role = transaction.get('role') account = validate_transaction_account(session, transaction)
alt_blockchain_address = transaction.get('alt_blockchain_address') metadata_id = account.standard_metadata_id()
blockchain_address = transaction.get('blockchain_address') transaction['metadata_id'] = metadata_id
account = validate_transaction_account(blockchain_address, role, session)
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 transaction['phone_number'] = account.phone_number
session.commit()
session.close() session.close()
return transaction return transaction

View File

@@ -1,7 +1,7 @@
# standard imports # standard imports
import semver import semver
version = (0, 3, 1, 'alpha.5') version = (0, 3, 0, 'alpha.10')
version_object = semver.VersionInfo( version_object = semver.VersionInfo(
major=version[0], major=version[0],

View File

@@ -0,0 +1,2 @@
[app]
service_code = *483*46#

View File

@@ -1,4 +1,3 @@
[ussd] [ussd]
service_code = *483*46#
user = user =
pass = pass =

View File

@@ -10,13 +10,6 @@ RUN mkdir -vp data
ARG EXTRA_INDEX_URL="https://pip.grassrootseconomics.net:8433" ARG EXTRA_INDEX_URL="https://pip.grassrootseconomics.net:8433"
ARG GITLAB_PYTHON_REGISTRY="https://gitlab.com/api/v4/projects/27624814/packages/pypi/simple" ARG GITLAB_PYTHON_REGISTRY="https://gitlab.com/api/v4/projects/27624814/packages/pypi/simple"
RUN --mount=type=cache,mode=0755,target=/root/.cache/pip \
pip install --index-url https://pypi.org/simple \
--extra-index-url $GITLAB_PYTHON_REGISTRY \
--extra-index-url $EXTRA_INDEX_URL \
cic-eth-aux-erc20-demurrage-token~=0.0.2a6
COPY requirements.txt . COPY requirements.txt .
RUN --mount=type=cache,mode=0755,target=/root/.cache/pip \ RUN --mount=type=cache,mode=0755,target=/root/.cache/pip \

View File

@@ -0,0 +1,32 @@
# 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 []

View File

@@ -1,10 +0,0 @@
#! /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

View File

@@ -1,13 +1,10 @@
alembic==1.4.2 alembic==1.4.2
attrs==21.2.0
billiard==3.6.4.0
bcrypt==3.2.0 bcrypt==3.2.0
celery==4.4.7 celery==4.4.7
cffi==1.14.6 cic-eth[services]==0.12.2a3
cic-eth[services]~=0.12.4a7
cic-notify~=0.4.0a10 cic-notify~=0.4.0a10
cic-types~=0.1.0a15 cic-types~=0.1.0a14
confini>=0.4.1a1,<0.5.0 confini~=0.4.1a1
phonenumbers==8.12.12 phonenumbers==8.12.12
psycopg2==2.8.6 psycopg2==2.8.6
python-i18n[YAML]==0.3.9 python-i18n[YAML]==0.3.9
@@ -16,5 +13,11 @@ redis==3.5.3
semver==2.13.0 semver==2.13.0
SQLAlchemy==1.3.20 SQLAlchemy==1.3.20
tinydb==4.2.0 tinydb==4.2.0
transitions==0.8.4 phonenumbers==8.12.12
redis==3.5.3
celery==4.4.7
python-i18n[YAML]==0.3.9
pyxdg==0.27
bcrypt==3.2.0
uWSGI==2.0.19.1 uWSGI==2.0.19.1
transitions==0.8.4

View File

@@ -43,13 +43,10 @@ def test_sync_get_balances(activated_account,
(5000000, 89000000, 67000000, 27.00) (5000000, 89000000, 67000000, 27.00)
]) ])
def test_calculate_available_balance(activated_account, def test_calculate_available_balance(activated_account,
available_balance,
balance_incoming, balance_incoming,
balance_network, balance_network,
balance_outgoing, balance_outgoing,
cache_balances, available_balance):
cache_default_token_data,
load_chain_spec):
balances = { balances = {
'address': activated_account.blockchain_address, 'address': activated_account.blockchain_address,
'converters': [], 'converters': [],
@@ -60,11 +57,7 @@ def test_calculate_available_balance(activated_account,
assert calculate_available_balance(balances) == available_balance assert calculate_available_balance(balances) == available_balance
def test_get_cached_available_balance(activated_account, def test_get_cached_available_balance(activated_account, cache_balances, balances):
balances,
cache_balances,
cache_default_token_data,
load_chain_spec):
cached_available_balance = get_cached_available_balance(activated_account.blockchain_address) cached_available_balance = get_cached_available_balance(activated_account.blockchain_address)
available_balance = calculate_available_balance(balances[0]) available_balance = calculate_available_balance(balances[0])
assert cached_available_balance == available_balance assert cached_available_balance == available_balance

View File

@@ -27,8 +27,6 @@ def test_filter_statement_transactions(transactions_list):
def test_generate(activated_account, def test_generate(activated_account,
cache_default_token_data,
cache_statement,
cache_preferences, cache_preferences,
celery_session_worker, celery_session_worker,
init_cache, init_cache,
@@ -62,7 +60,7 @@ def test_get_cached_statement(activated_account, cache_statement, statement):
assert cached_statement[0].get('blockchain_address') == statement[0].get('blockchain_address') assert cached_statement[0].get('blockchain_address') == statement[0].get('blockchain_address')
def test_parse_statement_transactions(cache_default_token_data, statement): def test_parse_statement_transactions(statement):
parsed_transactions = parse_statement_transactions(statement) parsed_transactions = parse_statement_transactions(statement)
parsed_transaction = parsed_transactions[0] parsed_transaction = parsed_transactions[0]
parsed_transaction.startswith('Sent') parsed_transaction.startswith('Sent')
@@ -78,7 +76,7 @@ def test_query_statement(blockchain_address, limit, load_chain_spec, activated_a
assert mock_transaction_list_query.get('limit') == limit assert mock_transaction_list_query.get('limit') == limit
def test_statement_transaction_set(cache_default_token_data, load_chain_spec, preferences, set_locale_files, statement): def test_statement_transaction_set(preferences, set_locale_files, statement):
parsed_transactions = parse_statement_transactions(statement) parsed_transactions = parse_statement_transactions(statement)
preferred_language = preferences.get('preferred_language') preferred_language = preferences.get('preferred_language')
transaction_set = statement_transaction_set(preferred_language, parsed_transactions) transaction_set = statement_transaction_set(preferred_language, parsed_transactions)

View File

@@ -36,19 +36,19 @@ def test_aux_transaction_data(preferences, set_locale_files, transactions_list):
check_aux_data('helpers.sent', 'helpers.to', preferred_language, sender_tx_aux_data) check_aux_data('helpers.sent', 'helpers.to', preferred_language, sender_tx_aux_data)
@pytest.mark.parametrize("value, expected_result", [ @pytest.mark.parametrize("wei, expected_result", [
(50000000, Decimal('50.00')), (50000000, Decimal('50.00')),
(100000, Decimal('0.10')) (100000, Decimal('0.10'))
]) ])
def test_from_wei(cache_default_token_data, expected_result, value): def test_from_wei(wei, expected_result):
assert from_wei(value) == expected_result assert from_wei(wei) == expected_result
@pytest.mark.parametrize("value, expected_result", [ @pytest.mark.parametrize("value, expected_result", [
(50, 50000000), (50, 50000000),
(0.10, 100000) (0.10, 100000)
]) ])
def test_to_wei(cache_default_token_data, expected_result, value): def test_to_wei(value, expected_result):
assert to_wei(value) == expected_result assert to_wei(value) == expected_result
@@ -75,28 +75,23 @@ def test_transaction_actors(activated_account, transaction_result, valid_recipie
def test_validate_transaction_account(activated_account, init_database, transactions_list): def test_validate_transaction_account(activated_account, init_database, transactions_list):
sample_transaction = transactions_list[0] sample_transaction = transactions_list[0]
recipient_transaction, sender_transaction = transaction_actors(sample_transaction) recipient_transaction, sender_transaction = transaction_actors(sample_transaction)
recipient_account = validate_transaction_account( recipient_account = validate_transaction_account(init_database, recipient_transaction)
recipient_transaction.get('blockchain_address'), recipient_transaction.get('role'), init_database) sender_account = validate_transaction_account(init_database, sender_transaction)
sender_account = validate_transaction_account(
sender_transaction.get('blockchain_address'), sender_transaction.get('role'), init_database)
assert isinstance(recipient_account, Account) assert isinstance(recipient_account, Account)
assert isinstance(sender_account, Account) assert isinstance(sender_account, Account)
sample_transaction = transactions_list[1] sample_transaction = transactions_list[1]
recipient_transaction, sender_transaction = transaction_actors(sample_transaction) recipient_transaction, sender_transaction = transaction_actors(sample_transaction)
with pytest.raises(UnknownUssdRecipient) as error: with pytest.raises(UnknownUssdRecipient) as error:
validate_transaction_account( validate_transaction_account(init_database, recipient_transaction)
recipient_transaction.get('blockchain_address'), recipient_transaction.get('role'), init_database)
assert str( assert str(
error.value) == f'Tx for recipient: {recipient_transaction.get("blockchain_address")} has no matching account in the system.' error.value) == f'Tx for recipient: {recipient_transaction.get("blockchain_address")} has no matching account in the system.'
validate_transaction_account( validate_transaction_account(init_database, sender_transaction)
sender_transaction.get('blockchain_address'), sender_transaction.get('role'), init_database)
assert f'Tx from sender: {sender_transaction.get("blockchain_address")} has no matching account in system.' assert f'Tx from sender: {sender_transaction.get("blockchain_address")} has no matching account in system.'
@pytest.mark.parametrize("amount", [50, 0.10]) @pytest.mark.parametrize("amount", [50, 0.10])
def test_outgoing_transaction_processor(activated_account, def test_outgoing_transaction_processor(activated_account,
amount, amount,
cache_default_token_data,
celery_session_worker, celery_session_worker,
load_config, load_config,
load_chain_spec, load_chain_spec,

View File

@@ -1,6 +1,5 @@
# standard imports # standard imports
import json import json
import datetime
# external imports # external imports
from chainlib.hash import strip_0x from chainlib.hash import strip_0x
@@ -15,7 +14,6 @@ from cic_ussd.account.statement import (
) )
from cic_ussd.account.tokens import get_default_token_symbol from cic_ussd.account.tokens import get_default_token_symbol
from cic_ussd.account.transaction import from_wei, to_wei from cic_ussd.account.transaction import from_wei, to_wei
from cic_ussd.cache import cache_data, cache_data_key
from cic_ussd.menu.ussd_menu import UssdMenu from cic_ussd.menu.ussd_menu import UssdMenu
from cic_ussd.metadata import PersonMetadata from cic_ussd.metadata import PersonMetadata
from cic_ussd.phone_number import Support from cic_ussd.phone_number import Support
@@ -40,34 +38,24 @@ def test_menu_processor(activated_account,
load_chain_spec, load_chain_spec,
load_support_phone, load_support_phone,
load_ussd_menu, load_ussd_menu,
mock_get_adjusted_balance,
mock_sync_balance_api_query, mock_sync_balance_api_query,
mock_transaction_list_query, mock_transaction_list_query,
valid_recipient): valid_recipient):
preferred_language = get_cached_preferred_language(activated_account.blockchain_address) preferred_language = get_cached_preferred_language(activated_account.blockchain_address)
available_balance = get_cached_available_balance(activated_account.blockchain_address) available_balance = get_cached_available_balance(activated_account.blockchain_address)
token_symbol = get_default_token_symbol() token_symbol = get_default_token_symbol()
with_available_balance = 'ussd.kenya.account_balances.available_balance'
with_fees = 'ussd.kenya.account_balances.with_fees' tax = ''
bonus = ''
display_key = 'ussd.kenya.account_balances'
ussd_menu = UssdMenu.find_by_name('account_balances') ussd_menu = UssdMenu.find_by_name('account_balances')
name = ussd_menu.get('name') name = ussd_menu.get('name')
resp = response(activated_account, 'ussd.kenya.account_balances', name, init_database, generic_ussd_session) resp = response(activated_account, display_key, name, init_database, generic_ussd_session)
assert resp == translation_for(with_available_balance, assert resp == translation_for(display_key,
preferred_language, preferred_language,
available_balance=available_balance, available_balance=available_balance,
token_symbol=token_symbol)
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
key = cache_data_key(identifier, ':cic.adjusted_balance')
adjusted_balance = 45931650.64654012
cache_data(key, json.dumps(adjusted_balance))
resp = response(activated_account, 'ussd.kenya.account_balances', name, init_database, generic_ussd_session)
tax_wei = to_wei(int(available_balance)) - int(adjusted_balance)
tax = from_wei(int(tax_wei))
assert resp == translation_for(key=with_fees,
preferred_language=preferred_language,
available_balance=available_balance,
tax=tax, tax=tax,
bonus=bonus,
token_symbol=token_symbol) token_symbol=token_symbol)
cached_statement = get_cached_statement(activated_account.blockchain_address) cached_statement = get_cached_statement(activated_account.blockchain_address)
@@ -135,15 +123,6 @@ def test_menu_processor(activated_account,
account_balance=available_balance, account_balance=available_balance,
account_token_name=token_symbol) account_token_name=token_symbol)
display_key = 'ussd.kenya.start'
ussd_menu = UssdMenu.find_by_name('start')
name = ussd_menu.get('name')
older_timestamp = (activated_account.created - datetime.timedelta(days=35))
activated_account.created = older_timestamp
init_database.flush()
response(activated_account, display_key, name, init_database, generic_ussd_session)
assert mock_get_adjusted_balance['timestamp'] == int((datetime.datetime.now() - datetime.timedelta(days=30)).timestamp())
display_key = 'ussd.kenya.transaction_pin_authorization' display_key = 'ussd.kenya.transaction_pin_authorization'
ussd_menu = UssdMenu.find_by_name('transaction_pin_authorization') ussd_menu = UssdMenu.find_by_name('transaction_pin_authorization')
name = ussd_menu.get('name') name = ussd_menu.get('name')

View File

@@ -30,6 +30,8 @@ def test_is_valid_recipient(activated_account,
valid_recipient): valid_recipient):
state_machine = ('0112365478', generic_ussd_session, valid_recipient, init_database) state_machine = ('0112365478', generic_ussd_session, valid_recipient, init_database)
assert is_valid_recipient(state_machine) is False assert is_valid_recipient(state_machine) is False
state_machine = (pending_account.phone_number, generic_ussd_session, valid_recipient, init_database)
assert is_valid_recipient(state_machine) is False
state_machine = (valid_recipient.phone_number, generic_ussd_session, activated_account, init_database) state_machine = (valid_recipient.phone_number, generic_ussd_session, activated_account, init_database)
assert is_valid_recipient(state_machine) is True assert is_valid_recipient(state_machine) is True
@@ -49,7 +51,6 @@ def test_is_valid_transaction_amount(activated_account, amount, expected_result,
]) ])
def test_has_sufficient_balance(activated_account, def test_has_sufficient_balance(activated_account,
cache_balances, cache_balances,
cache_default_token_data,
expected_result, expected_result,
generic_ussd_session, generic_ussd_session,
init_database, init_database,

View File

@@ -121,7 +121,6 @@ def test_statement_callback(activated_account, mocker, transactions_list):
def test_transaction_balances_callback(activated_account, def test_transaction_balances_callback(activated_account,
balances, balances,
cache_balances, cache_balances,
cache_default_token_data,
cache_person_metadata, cache_person_metadata,
cache_preferences, cache_preferences,
load_chain_spec, load_chain_spec,

View File

@@ -13,8 +13,7 @@ from cic_ussd.translation import translation_for
# tests imports # tests imports
def test_transaction(cache_default_token_data, def test_transaction(celery_session_worker,
celery_session_worker,
load_support_phone, load_support_phone,
mock_notifier_api, mock_notifier_api,
notification_data, notification_data,
@@ -25,8 +24,7 @@ def test_transaction(cache_default_token_data,
phone_number = notification_data.get('phone_number') phone_number = notification_data.get('phone_number')
preferred_language = notification_data.get('preferred_language') preferred_language = notification_data.get('preferred_language')
token_symbol = notification_data.get('token_symbol') token_symbol = notification_data.get('token_symbol')
alt_metadata_id = notification_data.get('alt_metadata_id') transaction_account_metadata = notification_data.get('metadata_id')
metadata_id = notification_data.get('metadata_id')
timestamp = datetime.datetime.now().strftime('%d-%m-%y, %H:%M %p') timestamp = datetime.datetime.now().strftime('%d-%m-%y, %H:%M %p')
s_transaction = celery.signature( s_transaction = celery.signature(
'cic_ussd.tasks.notifications.transaction', [notification_data] 'cic_ussd.tasks.notifications.transaction', [notification_data]
@@ -38,8 +36,7 @@ def test_transaction(cache_default_token_data,
preferred_language=preferred_language, preferred_language=preferred_language,
amount=amount, amount=amount,
token_symbol=token_symbol, token_symbol=token_symbol,
tx_recipient_information=alt_metadata_id, tx_recipient_information=transaction_account_metadata,
tx_sender_information=metadata_id,
timestamp=timestamp, timestamp=timestamp,
balance=balance) balance=balance)
assert mock_notifier_api.get('message') == message assert mock_notifier_api.get('message') == message
@@ -55,8 +52,7 @@ def test_transaction(cache_default_token_data,
preferred_language=preferred_language, preferred_language=preferred_language,
amount=amount, amount=amount,
token_symbol=token_symbol, token_symbol=token_symbol,
tx_recipient_information=metadata_id, tx_sender_information=transaction_account_metadata,
tx_sender_information=alt_metadata_id,
timestamp=timestamp, timestamp=timestamp,
balance=balance) balance=balance)
assert mock_notifier_api.get('message') == message assert mock_notifier_api.get('message') == message

View File

@@ -30,11 +30,10 @@ def test_generate_statement(activated_account,
def test_cache_statement(activated_account, def test_cache_statement(activated_account,
cache_default_token_data,
cache_person_metadata, cache_person_metadata,
cache_preferences,
celery_session_worker, celery_session_worker,
init_database, init_database,
preferences,
transaction_result): transaction_result):
recipient_transaction, sender_transaction = transaction_actors(transaction_result) recipient_transaction, sender_transaction = transaction_actors(transaction_result)
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address)) identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
@@ -42,7 +41,7 @@ def test_cache_statement(activated_account,
cached_statement = get_cached_data(key) cached_statement = get_cached_data(key)
assert cached_statement is None assert cached_statement is None
s_parse_transaction = celery.signature( s_parse_transaction = celery.signature(
'cic_ussd.tasks.processor.parse_transaction', [sender_transaction]) 'cic_ussd.tasks.processor.parse_transaction', [preferences, sender_transaction])
result = s_parse_transaction.apply_async().get() result = s_parse_transaction.apply_async().get()
s_cache_statement = celery.signature( s_cache_statement = celery.signature(
'cic_ussd.tasks.processor.cache_statement', [result, activated_account.blockchain_address] 'cic_ussd.tasks.processor.cache_statement', [result, activated_account.blockchain_address]
@@ -62,15 +61,15 @@ def test_cache_statement(activated_account,
def test_parse_transaction(activated_account, def test_parse_transaction(activated_account,
cache_person_metadata, cache_person_metadata,
cache_preferences,
celery_session_worker, celery_session_worker,
init_database, init_database,
preferences,
transaction_result): transaction_result):
recipient_transaction, sender_transaction = transaction_actors(transaction_result) recipient_transaction, sender_transaction = transaction_actors(transaction_result)
assert sender_transaction.get('metadata_id') is None assert sender_transaction.get('metadata_id') is None
assert sender_transaction.get('phone_number') is None assert sender_transaction.get('phone_number') is None
s_parse_transaction = celery.signature( s_parse_transaction = celery.signature(
'cic_ussd.tasks.processor.parse_transaction', [sender_transaction]) 'cic_ussd.tasks.processor.parse_transaction', [preferences, sender_transaction])
result = s_parse_transaction.apply_async().get() result = s_parse_transaction.apply_async().get()
assert result.get('metadata_id') == activated_account.standard_metadata_id() assert result.get('metadata_id') == activated_account.standard_metadata_id()
assert result.get('phone_number') == activated_account.phone_number assert result.get('phone_number') == activated_account.phone_number

View File

@@ -41,22 +41,6 @@ def mock_async_balance_api_query(mocker):
return query_args return query_args
@pytest.fixture(scope='function')
def mock_get_adjusted_balance(mocker, task_uuid):
query_args = {}
def get_adjusted_balance(self, token_symbol, balance, timestamp):
sync_res = mocker.patch('celery.result.AsyncResult')
sync_res.id = task_uuid
sync_res.result = 45931650.64654012
query_args['balance'] = balance
query_args['timestamp'] = timestamp
query_args['token_symbol'] = token_symbol
return sync_res
mocker.patch('cic_eth_aux.erc20_demurrage_token.api.Api.get_adjusted_balance', get_adjusted_balance)
return query_args
@pytest.fixture(scope='function') @pytest.fixture(scope='function')
def mock_notifier_api(mocker): def mock_notifier_api(mocker):
sms = {} sms = {}

Some files were not shown because too many files have changed in this diff Show More