Compare commits
51 Commits
bvander/de
...
lash/gas-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc3b4facfb
|
||
|
|
6b59c87f68 | ||
|
|
9ee42b908d | ||
| 3343c4163e | |||
|
|
f5bfc8ace2 | ||
| 8a9d2ee0be | |||
| 3608fd1fc7 | |||
| 0d275f358b | |||
|
3aef2aa65f
|
|||
| 5644baefb2 | |||
|
1a7c4deab6
|
|||
| 0389d8623d | |||
|
cf64387d81
|
|||
| 79bcc8a9f1 | |||
| 7b57f1b4c2 | |||
| 76b8519637 | |||
| e89aec76fa | |||
| a138a0ec75 | |||
| 5128c7828c | |||
| 2f005195e5 | |||
| fb8db3ffd2 | |||
| b5f647c4aa | |||
| 6019143ba1 | |||
| 610440b722 | |||
|
|
d65455fc29 | ||
| 43f8d1c30c | |||
| b855211eed | |||
| 1e0c475f39 | |||
|
|
3e6cf594e3 | ||
| b8f79a2dd1 | |||
| 540c2fd950 | |||
| b9b06eced8 | |||
| 949bb29379 | |||
|
|
0468906601 | ||
|
|
471243488e
|
||
| 3c4acd82ff | |||
| e07f992c5a | |||
| 17e95cb19c | |||
| 3c3a97ce15 | |||
| a492be4927 | |||
|
|
1f555748b0 | ||
| 8aa4d20eea | |||
|
|
90cf24dcee | ||
|
|
75b711dbd5 | ||
| c21c1eb2ef | |||
| eb5e612105 | |||
| e017d11770 | |||
| e327af68e1 | |||
| 92cc6a3f27 | |||
| f42bf7754a | |||
|
|
7342927e91
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -14,3 +14,4 @@ build/
|
||||
**/.venv
|
||||
.idea
|
||||
**/.vim
|
||||
**/*secret.yaml
|
||||
|
||||
@@ -12,13 +12,13 @@ include:
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
- deploy
|
||||
- deploy
|
||||
|
||||
image: registry.gitlab.com/grassrootseconomics/cic-internal-integration/docker-with-compose:latest
|
||||
image: registry.gitlab.com/grassrootseconomics/cic-internal-integration/docker-with-compose:latest
|
||||
|
||||
variables:
|
||||
DOCKER_BUILDKIT: "1"
|
||||
COMPOSE_DOCKER_CLI_BUILD: "1"
|
||||
COMPOSE_DOCKER_CLI_BUILD: "1"
|
||||
CI_DEBUG_TRACE: "true"
|
||||
|
||||
before_script:
|
||||
@@ -29,8 +29,15 @@ build-push:
|
||||
stage: build
|
||||
tags:
|
||||
- integration
|
||||
#script:
|
||||
# - TAG=$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA sh ./scripts/build-push.sh
|
||||
script:
|
||||
- TAG=$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA sh ./scripts/build-push.sh
|
||||
- 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
|
||||
|
||||
32
README.md
32
README.md
@@ -2,25 +2,21 @@
|
||||
|
||||
## Getting started
|
||||
|
||||
## Make some keys
|
||||
This repo uses docker-compose and docker buildkit. Set the following environment variables to get started:
|
||||
|
||||
```
|
||||
docker build -t bloxie . && docker run -v "$(pwd)/keys:/root/keys" --rm -it -t bloxie account new --chain /root/bloxberg.json --keys-path /root/keys
|
||||
export COMPOSE_DOCKER_CLI_BUILD=1
|
||||
export DOCKER_BUILDKIT=1
|
||||
```
|
||||
|
||||
|
||||
### Prepare the repo
|
||||
|
||||
This is stuff we need to put in makefile but for now...
|
||||
|
||||
File mounts and permisssions need to be set
|
||||
start services, database, redis and local ethereum node
|
||||
```
|
||||
chmod -R 755 scripts/initdb apps/cic-meta/scripts/initdb
|
||||
````
|
||||
|
||||
start cluster
|
||||
docker-compose up -d
|
||||
```
|
||||
docker-compose up
|
||||
|
||||
Run app/contract-migration to deploy contracts
|
||||
```
|
||||
RUN_MASK=3 docker-compose up contract-migration
|
||||
```
|
||||
|
||||
stop cluster
|
||||
@@ -28,9 +24,9 @@ stop cluster
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
delete data
|
||||
stop cluster and delete data
|
||||
```
|
||||
docker-compose down -v
|
||||
docker-compose down -v --remove-orphans
|
||||
```
|
||||
|
||||
rebuild an images
|
||||
@@ -38,5 +34,7 @@ rebuild an images
|
||||
docker-compose up --build <service_name>
|
||||
```
|
||||
|
||||
Deployment variables are writtend to service-configs/.env after everthing is up.
|
||||
|
||||
to delete the buildkit cache
|
||||
```
|
||||
docker builder prune --filter type=exec.cachemount
|
||||
```
|
||||
|
||||
@@ -27,11 +27,11 @@ class RPC:
|
||||
@staticmethod
|
||||
def from_config(config):
|
||||
chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
|
||||
RPCConnection.register_location(config.get('RPC_HTTP_PROVIDER'), chain_spec, 'default')
|
||||
RPCConnection.register_location(config.get('RPC_PROVIDER'), chain_spec, 'default')
|
||||
if config.get('SIGNER_PROVIDER'):
|
||||
RPCConnection.register_constructor(ConnType.UNIX, EthUnixSignerConnection, tag='signer')
|
||||
RPCConnection.register_location(config.get('SIGNER_PROVIDER'), chain_spec, 'signer')
|
||||
rpc = RPC(chain_spec, config.get('RPC_HTTP_PROVIDER'), signer_provider=config.get('SIGNER_PROVIDER'))
|
||||
rpc = RPC(chain_spec, config.get('RPC_PROVIDER'), signer_provider=config.get('SIGNER_PROVIDER'))
|
||||
logg.info('set up rpc: {}'.format(rpc))
|
||||
return rpc
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ psycopg2==2.8.6
|
||||
celery==4.4.7
|
||||
redis==3.5.3
|
||||
chainsyncer[sql]>=0.0.6a3,<0.1.0
|
||||
erc20-faucet>=0.3.2a1, <0.4.0
|
||||
chainlib-eth>=0.0.9a7,<0.1.0
|
||||
chainlib>=0.0.9a3,<0.1.0
|
||||
eth-address-index>=0.2.3a1,<0.3.0
|
||||
erc20-faucet>=0.3.2a2, <0.4.0
|
||||
chainlib-eth>=0.0.9a14,<0.1.0
|
||||
eth-address-index>=0.2.3a4,<0.3.0
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
celery==4.4.7
|
||||
erc20-demurrage-token~=0.0.3a1
|
||||
cic-eth-registry~=0.5.8a1
|
||||
chainlib~=0.0.7a1
|
||||
cic_eth~=0.12.2a4
|
||||
cic-eth-registry>=0.6.1a2,<0.7.0
|
||||
cic-eth[services]~=0.12.4a8
|
||||
|
||||
@@ -9,8 +9,8 @@ build-test-cic-eth:
|
||||
- cd apps/cic-eth
|
||||
- docker build -t $MR_IMAGE_TAG -f docker/Dockerfile .
|
||||
- docker run $MR_IMAGE_TAG sh docker/run_tests.sh
|
||||
#rules:
|
||||
#- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
# changes:
|
||||
# - apps/$APP_NAME/**/*
|
||||
# when: always
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
changes:
|
||||
- apps/$APP_NAME/**/*
|
||||
when: always
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
SQLAlchemy==1.3.20
|
||||
cic-eth-registry>=0.6.1a2,<0.7.0
|
||||
cic-eth-registry>=0.6.1a3,<0.7.0
|
||||
hexathon~=0.0.1a8
|
||||
chainqueue>=0.0.4a6,<0.1.0
|
||||
eth-erc20>=0.1.2a2,<0.2.0
|
||||
|
||||
@@ -9,6 +9,7 @@ import logging
|
||||
# external imports
|
||||
import celery
|
||||
from chainlib.chain import ChainSpec
|
||||
from hexathon import strip_0x
|
||||
|
||||
# local imports
|
||||
from cic_eth.api.base import ApiBase
|
||||
@@ -254,6 +255,8 @@ class Api(ApiBase):
|
||||
:returns: uuid of root task
|
||||
:rtype: celery.Task
|
||||
"""
|
||||
#from_address = strip_0x(from_address)
|
||||
#to_address = strip_0x(to_address)
|
||||
s_check = celery.signature(
|
||||
'cic_eth.admin.ctrl.check_lock',
|
||||
[
|
||||
|
||||
@@ -35,14 +35,14 @@ class RPC:
|
||||
def from_config(config, use_signer=False, default_label='default', signer_label='signer'):
|
||||
chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
|
||||
|
||||
RPCConnection.register_location(config.get('RPC_HTTP_PROVIDER'), chain_spec, default_label)
|
||||
RPCConnection.register_location(config.get('RPC_PROVIDER'), chain_spec, default_label)
|
||||
if use_signer:
|
||||
|
||||
RPCConnection.register_constructor(ConnType.UNIX, EthUnixSignerConnection, signer_label)
|
||||
RPCConnection.register_constructor(ConnType.HTTP, EthHTTPSignerConnection, signer_label)
|
||||
RPCConnection.register_constructor(ConnType.HTTP_SSL, EthHTTPSignerConnection, signer_label)
|
||||
RPCConnection.register_location(config.get('SIGNER_PROVIDER'), chain_spec, signer_label)
|
||||
rpc = RPC(chain_spec, config.get('RPC_HTTP_PROVIDER'), signer_provider=config.get('SIGNER_PROVIDER'))
|
||||
rpc = RPC(chain_spec, config.get('RPC_PROVIDER'), signer_provider=config.get('SIGNER_PROVIDER'))
|
||||
logg.info('set up rpc: {}'.format(rpc))
|
||||
return rpc
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ Create Date: 2021-04-02 18:41:20.864265
|
||||
import datetime
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from chainlib.eth.constant import ZERO_ADDRESS
|
||||
from cic_eth.db.enum import LockEnum
|
||||
from cic_eth.encode import ZERO_ADDRESS_NORMAL
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
@@ -30,7 +30,7 @@ def upgrade():
|
||||
sa.Column("otx_id", sa.Integer, sa.ForeignKey('otx.id'), nullable=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, datetime.datetime.utcnow(), LockEnum.INIT | LockEnum.SEND | LockEnum.QUEUE))
|
||||
op.execute("INSERT INTO lock (address, date_created, blockchain, flags) VALUES('{}', '{}', '::', {})".format(ZERO_ADDRESS_NORMAL, datetime.datetime.utcnow(), LockEnum.INIT | LockEnum.SEND | LockEnum.QUEUE))
|
||||
|
||||
|
||||
def downgrade():
|
||||
|
||||
@@ -13,7 +13,7 @@ from chainlib.eth.sign import (
|
||||
new_account,
|
||||
sign_message,
|
||||
)
|
||||
from chainlib.eth.address import to_checksum_address
|
||||
from chainlib.eth.address import to_checksum_address, is_address
|
||||
from chainlib.eth.tx import TxFormat
|
||||
from chainlib.chain import ChainSpec
|
||||
from chainlib.error import JSONRPCException
|
||||
@@ -31,6 +31,7 @@ from cic_eth.eth.gas import (
|
||||
from cic_eth.db.models.nonce import Nonce
|
||||
from cic_eth.db.models.base import SessionBase
|
||||
from cic_eth.db.models.role import AccountRole
|
||||
from cic_eth.encode import tx_normalize
|
||||
from cic_eth.error import (
|
||||
RoleMissingError,
|
||||
SignerError,
|
||||
@@ -49,6 +50,7 @@ from cic_eth.queue.tx import (
|
||||
from cic_eth.encode import (
|
||||
unpack_normal,
|
||||
ZERO_ADDRESS_NORMAL,
|
||||
tx_normalize,
|
||||
)
|
||||
|
||||
logg = logging.getLogger()
|
||||
@@ -84,7 +86,7 @@ def create(self, password, chain_spec_dict):
|
||||
# TODO: It seems infeasible that a can be None in any case, verify
|
||||
if a == None:
|
||||
raise SignerError('create account')
|
||||
|
||||
a = tx_normalize.wallet_address(a)
|
||||
logg.debug('created account {}'.format(a))
|
||||
|
||||
# Initialize nonce provider record for account
|
||||
@@ -175,6 +177,9 @@ def gift(self, account_address, chain_spec_dict):
|
||||
"""
|
||||
chain_spec = ChainSpec.from_dict(chain_spec_dict)
|
||||
|
||||
if is_address(account_address):
|
||||
account_address = tx_normalize.wallet_address(account_address)
|
||||
|
||||
logg.debug('gift account address {} to index'.format(account_address))
|
||||
queue = self.request.delivery_info.get('routing_key')
|
||||
|
||||
@@ -248,8 +253,9 @@ def have(self, account, chain_spec_dict):
|
||||
|
||||
@celery_app.task(bind=True, base=CriticalSQLAlchemyTask)
|
||||
def set_role(self, tag, address, chain_spec_dict):
|
||||
if not to_checksum_address(address):
|
||||
raise ValueError('invalid checksum address {}'.format(address))
|
||||
if not is_address(address):
|
||||
raise ValueError('invalid address {}'.format(address))
|
||||
address = tx_normalize.wallet_address(address)
|
||||
session = SessionBase.create_session()
|
||||
role = AccountRole.set(tag, address, session=session)
|
||||
session.add(role)
|
||||
@@ -298,13 +304,15 @@ def cache_gift_data(
|
||||
tx_signed_raw_bytes = bytes.fromhex(strip_0x(tx_signed_raw_hex))
|
||||
tx = unpack_normal(tx_signed_raw_bytes, chain_spec)
|
||||
tx_data = Faucet.parse_give_to_request(tx['data'])
|
||||
sender_address = tx_normalize.wallet_address(tx['from'])
|
||||
recipient_address = tx_normalize.wallet_address(tx['to'])
|
||||
|
||||
session = self.create_session()
|
||||
|
||||
tx_dict = {
|
||||
'hash': tx['hash'],
|
||||
'from': tx['from'],
|
||||
'to': tx['to'],
|
||||
'from': sender_address,
|
||||
'to': recipient_address,
|
||||
'source_token': ZERO_ADDRESS_NORMAL,
|
||||
'destination_token': ZERO_ADDRESS_NORMAL,
|
||||
'from_value': 0,
|
||||
@@ -338,12 +346,14 @@ def cache_account_data(
|
||||
tx_signed_raw_bytes = bytes.fromhex(strip_0x(tx_signed_raw_hex))
|
||||
tx = unpack_normal(tx_signed_raw_bytes, chain_spec)
|
||||
tx_data = AccountsIndex.parse_add_request(tx['data'])
|
||||
sender_address = tx_normalize.wallet_address(tx['from'])
|
||||
recipient_address = tx_normalize.wallet_address(tx['to'])
|
||||
|
||||
session = SessionBase.create_session()
|
||||
tx_dict = {
|
||||
'hash': tx['hash'],
|
||||
'from': tx['from'],
|
||||
'to': tx['to'],
|
||||
'from': sender_address,
|
||||
'to': recipient_address,
|
||||
'source_token': ZERO_ADDRESS_NORMAL,
|
||||
'destination_token': ZERO_ADDRESS_NORMAL,
|
||||
'from_value': 0,
|
||||
|
||||
@@ -12,7 +12,10 @@ from chainlib.eth.tx import (
|
||||
)
|
||||
from cic_eth_registry import CICRegistry
|
||||
from cic_eth_registry.erc20 import ERC20Token
|
||||
from hexathon import strip_0x
|
||||
from hexathon import (
|
||||
strip_0x,
|
||||
add_0x,
|
||||
)
|
||||
from chainqueue.error import NotLocalTxError
|
||||
from eth_erc20 import ERC20
|
||||
from chainqueue.sql.tx import cache_tx_dict
|
||||
@@ -38,6 +41,7 @@ from cic_eth.task import (
|
||||
CriticalSQLAlchemyAndSignerTask,
|
||||
)
|
||||
from cic_eth.eth.nonce import CustodialTaskNonceOracle
|
||||
from cic_eth.encode import tx_normalize
|
||||
|
||||
celery_app = celery.current_app
|
||||
logg = logging.getLogger()
|
||||
@@ -62,7 +66,8 @@ def balance(tokens, holder_address, chain_spec_dict):
|
||||
|
||||
for t in tokens:
|
||||
address = t['address']
|
||||
token = ERC20Token(chain_spec, rpc, address)
|
||||
logg.debug('address {} {}'.format(address, holder_address))
|
||||
token = ERC20Token(chain_spec, rpc, add_0x(address))
|
||||
c = ERC20(chain_spec)
|
||||
o = c.balance_of(address, holder_address, sender_address=caller_address)
|
||||
r = rpc.do(o)
|
||||
@@ -371,13 +376,15 @@ def cache_transfer_data(
|
||||
tx = unpack(tx_signed_raw_bytes, chain_spec)
|
||||
|
||||
tx_data = ERC20.parse_transfer_request(tx['data'])
|
||||
recipient_address = tx_data[0]
|
||||
sender_address = tx_normalize.wallet_address(tx['from'])
|
||||
recipient_address = tx_normalize.wallet_address(tx_data[0])
|
||||
token_value = tx_data[1]
|
||||
|
||||
|
||||
session = SessionBase.create_session()
|
||||
tx_dict = {
|
||||
'hash': tx_hash_hex,
|
||||
'from': tx['from'],
|
||||
'from': sender_address,
|
||||
'to': recipient_address,
|
||||
'source_token': tx['to'],
|
||||
'destination_token': tx['to'],
|
||||
@@ -448,13 +455,14 @@ def cache_approve_data(
|
||||
tx = unpack(tx_signed_raw_bytes, chain_spec)
|
||||
|
||||
tx_data = ERC20.parse_approve_request(tx['data'])
|
||||
recipient_address = tx_data[0]
|
||||
sender_address = tx_normalize.wallet_address(tx['from'])
|
||||
recipient_address = tx_normalize.wallet_address(tx_data[0])
|
||||
token_value = tx_data[1]
|
||||
|
||||
session = SessionBase.create_session()
|
||||
tx_dict = {
|
||||
'hash': tx_hash_hex,
|
||||
'from': tx['from'],
|
||||
'from': sender_address,
|
||||
'to': recipient_address,
|
||||
'source_token': tx['to'],
|
||||
'destination_token': tx['to'],
|
||||
|
||||
@@ -3,10 +3,17 @@ import logging
|
||||
|
||||
# external imports
|
||||
import celery
|
||||
from hexathon import strip_0x
|
||||
from hexathon import (
|
||||
strip_0x,
|
||||
add_0x,
|
||||
)
|
||||
#from chainlib.eth.constant import ZERO_ADDRESS
|
||||
from chainlib.chain import ChainSpec
|
||||
from chainlib.eth.address import is_checksum_address
|
||||
from chainlib.eth.address import (
|
||||
is_checksum_address,
|
||||
to_checksum_address,
|
||||
is_address
|
||||
)
|
||||
from chainlib.connection import RPCConnection
|
||||
from chainqueue.db.enum import StatusBits
|
||||
from chainqueue.sql.tx import cache_tx_dict
|
||||
@@ -71,7 +78,6 @@ class MaxGasOracle:
|
||||
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):
|
||||
"""Creates a celery task signature for a check_gas task that adds the task to the outgoing queue to be processed by the dispatcher.
|
||||
|
||||
@@ -176,10 +182,12 @@ def check_gas(self, tx_hashes_hex, chain_spec_dict, txs_hex=[], address=None, ga
|
||||
:return: Signed raw transaction data list
|
||||
:rtype: param txs, unchanged
|
||||
"""
|
||||
rpc_format_address = None
|
||||
if address != None:
|
||||
if not is_checksum_address(address):
|
||||
if not is_address(address):
|
||||
raise ValueError('invalid address {}'.format(address))
|
||||
address = tx_normalize.wallet_address(address)
|
||||
address = add_0x(address)
|
||||
|
||||
tx_hashes = []
|
||||
txs = []
|
||||
@@ -191,7 +199,6 @@ def check_gas(self, tx_hashes_hex, chain_spec_dict, txs_hex=[], address=None, ga
|
||||
txs.append(tx)
|
||||
|
||||
chain_spec = ChainSpec.from_dict(chain_spec_dict)
|
||||
logg.debug('txs {} tx_hashes {}'.format(txs, tx_hashes))
|
||||
|
||||
addresspass = None
|
||||
if len(txs) == 0:
|
||||
@@ -207,13 +214,15 @@ 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']))
|
||||
addresspass.append(address)
|
||||
|
||||
rpc_format_address = add_0x(to_checksum_address(address))
|
||||
|
||||
queue = self.request.delivery_info.get('routing_key')
|
||||
|
||||
conn = RPCConnection.connect(chain_spec)
|
||||
|
||||
gas_balance = 0
|
||||
try:
|
||||
o = balance(address)
|
||||
o = balance(rpc_format_address)
|
||||
r = conn.do(o)
|
||||
conn.disconnect()
|
||||
gas_balance = abi_decode_single(ABIContractType.UINT256, r)
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
from chainlib.eth.constant import ZERO_ADDRESS
|
||||
from chainlib.status import Status as TxStatus
|
||||
from cic_eth_registry.erc20 import ERC20Token
|
||||
from hexathon import add_0x
|
||||
|
||||
# local imports
|
||||
# local impor:ts
|
||||
from cic_eth.ext.address import translate_address
|
||||
|
||||
|
||||
@@ -44,8 +45,8 @@ class ExtendedTx:
|
||||
destination = source
|
||||
if destination_value == None:
|
||||
destination_value = source_value
|
||||
st = ERC20Token(self.chain_spec, self.rpc, source)
|
||||
dt = ERC20Token(self.chain_spec, self.rpc, destination)
|
||||
st = ERC20Token(self.chain_spec, self.rpc, add_0x(source))
|
||||
dt = ERC20Token(self.chain_spec, self.rpc, add_0x(destination))
|
||||
self.source_token = source
|
||||
self.source_token_symbol = st.symbol
|
||||
self.source_token_name = st.name
|
||||
|
||||
@@ -3,11 +3,12 @@ import logging
|
||||
|
||||
# external imports
|
||||
import celery
|
||||
from chainlib.eth.address import is_checksum_address
|
||||
from chainlib.eth.address import is_checksum_address, is_address, strip_0x
|
||||
|
||||
# local imports
|
||||
from cic_eth.db.models.role import AccountRole
|
||||
from cic_eth.db.models.base import SessionBase
|
||||
from cic_eth.encode import tx_normalize
|
||||
from cic_eth.task import CriticalSQLAlchemyTask
|
||||
from cic_eth.db.models.nonce import (
|
||||
Nonce,
|
||||
@@ -42,7 +43,8 @@ class CustodialTaskNonceOracle():
|
||||
:returns: Nonce
|
||||
:rtype: number
|
||||
"""
|
||||
r = NonceReservation.release(self.address, self.uuid, session=self.session)
|
||||
address = tx_normalize.wallet_address(self.address)
|
||||
r = NonceReservation.release(address, self.uuid, session=self.session)
|
||||
return r[1]
|
||||
|
||||
|
||||
@@ -58,17 +60,18 @@ def reserve_nonce(self, chained_input, chain_spec_dict, signer_address=None):
|
||||
address = chained_input
|
||||
logg.debug('non-explicit address for reserve nonce, using arg head {}'.format(chained_input))
|
||||
else:
|
||||
if is_checksum_address(signer_address):
|
||||
if is_address(signer_address):
|
||||
address = signer_address
|
||||
logg.debug('explicit address for reserve nonce {}'.format(signer_address))
|
||||
else:
|
||||
address = AccountRole.get_address(signer_address, session=session)
|
||||
logg.debug('role for reserve nonce {} -> {}'.format(signer_address, address))
|
||||
|
||||
if not is_checksum_address(address):
|
||||
if not is_address(address):
|
||||
raise ValueError('invalid result when resolving address for nonce {}'.format(address))
|
||||
|
||||
root_id = self.request.root_id
|
||||
address = tx_normalize.wallet_address(address)
|
||||
r = NonceReservation.next(address, root_id, session=session)
|
||||
logg.debug('nonce {} reserved for address {} task {}'.format(r[1], address, r[0]))
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ from potaahto.symbols import snake_and_camel
|
||||
from cic_eth.queue.time import tx_times
|
||||
from cic_eth.task import BaseTask
|
||||
from cic_eth.db.models.base import SessionBase
|
||||
from cic_eth.encode import tx_normalize
|
||||
|
||||
celery_app = celery.current_app
|
||||
logg = logging.getLogger()
|
||||
@@ -134,7 +135,7 @@ def list_tx_by_bloom(self, bloomspec, address, chain_spec_dict):
|
||||
tx_address = transfer_data[0]
|
||||
tx_token_value = transfer_data[1]
|
||||
|
||||
if address == tx_address:
|
||||
if tx_normalize.wallet_address(address) == tx_normalize.wallet_address(tx_address):
|
||||
status = StatusEnum.SENT
|
||||
try:
|
||||
o = receipt(tx['hash'])
|
||||
@@ -152,8 +153,8 @@ def list_tx_by_bloom(self, bloomspec, address, chain_spec_dict):
|
||||
times = tx_times(tx['hash'], chain_spec)
|
||||
tx_r = {
|
||||
'hash': tx['hash'],
|
||||
'sender': tx['from'],
|
||||
'recipient': tx_address,
|
||||
'sender': tx_normalize.wallet_address(tx['from']),
|
||||
'recipient': tx_normalize.wallet_address(tx_address),
|
||||
'source_value': tx_token_value,
|
||||
'destination_value': tx_token_value,
|
||||
'source_token': tx['to'],
|
||||
@@ -164,10 +165,10 @@ def list_tx_by_bloom(self, bloomspec, address, chain_spec_dict):
|
||||
tx_r['date_created'] = times['queue']
|
||||
else:
|
||||
tx_r['date_created'] = times['network']
|
||||
txs[tx['hash']] = tx_r
|
||||
txs[strip_0x(tx['hash'])] = tx_r
|
||||
break
|
||||
return txs
|
||||
|
||||
return txs
|
||||
|
||||
|
||||
# TODO: Surely it must be possible to optimize this
|
||||
@@ -230,6 +231,8 @@ def tx_collate(self, tx_batches, chain_spec_dict, offset, limit, newest_first=Tr
|
||||
except UnknownContractError:
|
||||
logg.error('verify failed on tx {}, skipping'.format(tx['hash']))
|
||||
continue
|
||||
tx['recipient'] = tx_normalize.wallet_address(tx['recipient'])
|
||||
tx['sender'] = tx_normalize.wallet_address(tx['sender'])
|
||||
txs.append(tx)
|
||||
|
||||
return txs
|
||||
|
||||
@@ -58,6 +58,7 @@ def get_tx_local(chain_spec, tx_hash, session=None):
|
||||
|
||||
@celery_app.task(base=CriticalSQLAlchemyTask)
|
||||
def get_account_tx(chain_spec_dict, address, as_sender=True, as_recipient=True, counterpart=None):
|
||||
address = tx_normalize.wallet_address(address)
|
||||
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)
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ from erc20_faucet import Faucet
|
||||
# local imports
|
||||
from .base import SyncFilter
|
||||
from cic_eth.eth.meta import ExtendedTx
|
||||
from cic_eth.encode import tx_normalize
|
||||
|
||||
logg = logging.getLogger().getChild(__name__)
|
||||
|
||||
@@ -42,9 +43,9 @@ class CallbackFilter(SyncFilter):
|
||||
return (None, None)
|
||||
r = ERC20.parse_transfer_request(tx.payload)
|
||||
transfer_data = {}
|
||||
transfer_data['to'] = r[0]
|
||||
transfer_data['to'] = tx_normalize.wallet_address(r[0])
|
||||
transfer_data['value'] = r[1]
|
||||
transfer_data['from'] = tx.outputs[0]
|
||||
transfer_data['from'] = tx_normalize.wallet_address(tx.outputs[0])
|
||||
transfer_data['token_address'] = tx.inputs[0]
|
||||
return ('transfer', transfer_data)
|
||||
|
||||
@@ -54,8 +55,8 @@ class CallbackFilter(SyncFilter):
|
||||
return (None, None)
|
||||
r = ERC20.parse_transfer_from_request(tx.payload)
|
||||
transfer_data = {}
|
||||
transfer_data['from'] = r[0]
|
||||
transfer_data['to'] = r[1]
|
||||
transfer_data['from'] = tx_normalize.wallet_address(r[0])
|
||||
transfer_data['to'] = tx_normalize.wallet_address(r[1])
|
||||
transfer_data['value'] = r[2]
|
||||
transfer_data['token_address'] = tx.inputs[0]
|
||||
return ('transferfrom', transfer_data)
|
||||
@@ -66,9 +67,9 @@ class CallbackFilter(SyncFilter):
|
||||
return (None, None)
|
||||
r = Faucet.parse_give_to_request(tx.payload)
|
||||
transfer_data = {}
|
||||
transfer_data['to'] = r[0]
|
||||
transfer_data['to'] = tx_normalize.wallet_address(r[0])
|
||||
transfer_data['value'] = tx.value
|
||||
transfer_data['from'] = tx.outputs[0]
|
||||
transfer_data['from'] = tx_normalize.wallet_address(tx.outputs[0])
|
||||
#transfer_data['token_address'] = tx.inputs[0]
|
||||
faucet_contract = tx.inputs[0]
|
||||
|
||||
|
||||
@@ -12,7 +12,8 @@ from hexathon import (
|
||||
# local imports
|
||||
from .base import SyncFilter
|
||||
|
||||
logg = logging.getLogger(__name__)
|
||||
#logg = logging.getLogger(__name__)
|
||||
logg = logging.getLogger()
|
||||
|
||||
account_registry_add_log_hash = '0x9cc987676e7d63379f176ea50df0ae8d2d9d1141d1231d4ce15b5965f73c9430'
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ from cic_eth_registry import CICRegistry
|
||||
from erc20_transfer_authorization import TransferAuthorization
|
||||
|
||||
# local imports
|
||||
from cic_eth.encode import tx_normalize
|
||||
from .base import SyncFilter
|
||||
|
||||
|
||||
@@ -52,9 +53,9 @@ class TransferAuthFilter(SyncFilter):
|
||||
|
||||
r = TransferAuthorization.parse_create_request_request(tx.payload)
|
||||
|
||||
sender = r[0]
|
||||
recipient = r[1]
|
||||
token = r[2]
|
||||
sender = tx_normalize.wallet_address(r[0])
|
||||
recipient = tx_normalize.wallet_address(r[1])
|
||||
token = tx_normalize.executable_address(r[2])
|
||||
value = r[3]
|
||||
|
||||
token_data = {
|
||||
|
||||
@@ -69,7 +69,6 @@ from cic_eth.registry import (
|
||||
)
|
||||
from cic_eth.task import BaseTask
|
||||
|
||||
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
logg = logging.getLogger()
|
||||
|
||||
@@ -207,7 +206,7 @@ def main():
|
||||
|
||||
BaseTask.default_token_symbol = config.get('CIC_DEFAULT_TOKEN_SYMBOL')
|
||||
BaseTask.default_token_address = registry.by_name(BaseTask.default_token_symbol)
|
||||
default_token = ERC20Token(chain_spec, conn, BaseTask.default_token_address)
|
||||
default_token = ERC20Token(chain_spec, conn, add_0x(BaseTask.default_token_address))
|
||||
default_token.load(conn)
|
||||
BaseTask.default_token_decimals = default_token.decimals
|
||||
BaseTask.default_token_name = default_token.name
|
||||
|
||||
@@ -13,7 +13,6 @@ from chainlib.eth.nonce import RPCNonceOracle
|
||||
from chainlib.eth.gas import RPCGasOracle
|
||||
from cic_eth_registry import CICRegistry
|
||||
from cic_eth_registry.error import UnknownContractError
|
||||
import liveness.linux
|
||||
|
||||
# local imports
|
||||
from cic_eth.error import SeppukuError
|
||||
@@ -48,6 +47,7 @@ class BaseTask(celery.Task):
|
||||
|
||||
def on_failure(self, exc, task_id, args, kwargs, einfo):
|
||||
if isinstance(exc, SeppukuError):
|
||||
import liveness.linux
|
||||
liveness.linux.reset(rundir=self.run_dir)
|
||||
logg.critical(einfo)
|
||||
msg = 'received critical exception {}, calling shutdown'.format(str(exc))
|
||||
|
||||
@@ -10,7 +10,7 @@ version = (
|
||||
0,
|
||||
12,
|
||||
4,
|
||||
'alpha.7',
|
||||
'alpha.8',
|
||||
)
|
||||
|
||||
version_object = semver.VersionInfo(
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
@node cic-eth configuration
|
||||
@section Configuration
|
||||
|
||||
(refer to @code{cic-base} for a general overview of the config pipeline)
|
||||
|
||||
Configuration parameters are grouped by configuration filename.
|
||||
|
||||
|
||||
@@ -40,7 +38,26 @@ Boolean value. If set, the amount of available context for a task in the result
|
||||
|
||||
@subsection database
|
||||
|
||||
See ref cic-base when ready
|
||||
@table @var
|
||||
@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
|
||||
|
||||
@@ -13,7 +13,16 @@ ARG EXTRA_PIP_ARGS=""
|
||||
# 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
|
||||
# -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 ./
|
||||
RUN --mount=type=cache,mode=0755,target=/root/.cache/pip \
|
||||
pip install --index-url https://pypi.org/simple \
|
||||
@@ -22,19 +31,21 @@ RUN --mount=type=cache,mode=0755,target=/root/.cache/pip \
|
||||
$EXTRA_PIP_ARGS \
|
||||
-r requirements.txt \
|
||||
-r services_requirements.txt \
|
||||
-r admin_requirements.txt
|
||||
|
||||
COPY . .
|
||||
RUN python setup.py install
|
||||
|
||||
ENV PYTHONPATH .
|
||||
|
||||
-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 \
|
||||
cic-eth-aux-erc20-demurrage-token~=0.0.2a6
|
||||
crypto-dev-signer
|
||||
|
||||
COPY . .
|
||||
RUN python setup.py install
|
||||
|
||||
ENV PYTHONPATH .
|
||||
|
||||
|
||||
COPY docker/entrypoints/* ./
|
||||
RUN chmod 755 *.sh
|
||||
|
||||
@@ -5,27 +5,27 @@ set -e
|
||||
|
||||
# set CONFINI_ENV_PREFIX to override the env prefix to override env vars
|
||||
|
||||
echo "!!! starting signer"
|
||||
python /usr/local/bin/crypto-dev-daemon -c /usr/local/etc/crypto-dev-signer -vv 2> /tmp/signer.log &
|
||||
#echo "!!! starting signer"
|
||||
#python /usr/local/bin/crypto-dev-daemon -c /usr/local/etc/crypto-dev-signer -vv 2> /tmp/signer.log &
|
||||
|
||||
echo "!!! starting taskerd"
|
||||
/usr/local/bin/cic-eth-taskerd $@
|
||||
|
||||
# thanks! https://docs.docker.com/config/containers/multi-service_container/
|
||||
sleep 1;
|
||||
echo "!!! entering monitor loop"
|
||||
while true; do
|
||||
ps aux | grep crypto-dev-daemon | grep -q -v grep
|
||||
PROCESS_1_STATUS=$?
|
||||
ps aux | grep cic-eth-tasker |grep -q -v grep
|
||||
PROCESS_2_STATUS=$?
|
||||
# If the greps above find anything, they exit with 0 status
|
||||
# If they are not both 0, then something is wrong
|
||||
if [ $PROCESS_1_STATUS -ne 0 -o $PROCESS_2_STATUS -ne 0 ]; then
|
||||
echo "One of the processes has already exited."
|
||||
exit 1
|
||||
fi
|
||||
sleep 15;
|
||||
done
|
||||
|
||||
#sleep 1;
|
||||
#echo "!!! entering monitor loop"
|
||||
#while true; do
|
||||
# ps aux | grep crypto-dev-daemon | grep -q -v grep
|
||||
# PROCESS_1_STATUS=$?
|
||||
# ps aux | grep cic-eth-tasker |grep -q -v grep
|
||||
# PROCESS_2_STATUS=$?
|
||||
# # If the greps above find anything, they exit with 0 status
|
||||
# # If they are not both 0, then something is wrong
|
||||
# if [ $PROCESS_1_STATUS -ne 0 -o $PROCESS_2_STATUS -ne 0 ]; then
|
||||
# echo "One of the processes has already exited."
|
||||
# exit 1
|
||||
# fi
|
||||
# sleep 15;
|
||||
#done
|
||||
#
|
||||
set +e
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
celery==4.4.7
|
||||
chainlib-eth>=0.0.9a7,<0.1.0
|
||||
chainlib-eth>=0.0.9rc2,<0.1.0
|
||||
semver==2.13.0
|
||||
crypto-dev-signer>=0.4.15rc2,<0.5.0
|
||||
|
||||
@@ -8,7 +8,7 @@ pycryptodome==3.10.1
|
||||
liveness~=0.0.1a7
|
||||
eth-address-index>=0.2.3a4,<0.3.0
|
||||
eth-accounts-index>=0.1.2a3,<0.2.0
|
||||
cic-eth-registry>=0.6.1a2,<0.7.0
|
||||
cic-eth-registry>=0.6.1a3,<0.7.0
|
||||
erc20-faucet>=0.3.2a2,<0.4.0
|
||||
erc20-transfer-authorization>=0.3.5a2,<0.4.0
|
||||
sarafu-faucet>=0.0.7a2,<0.1.0
|
||||
|
||||
@@ -18,7 +18,10 @@ from eth_erc20 import ERC20
|
||||
from sarafu_faucet import MinterFaucet
|
||||
from eth_accounts_index.registry import AccountRegistry
|
||||
from potaahto.symbols import snake_and_camel
|
||||
from hexathon import add_0x
|
||||
from hexathon import (
|
||||
add_0x,
|
||||
strip_0x,
|
||||
)
|
||||
|
||||
# local imports
|
||||
from cic_eth.runnable.daemons.filters.callback import CallbackFilter
|
||||
@@ -160,7 +163,7 @@ def test_faucet_gift_to_tx(
|
||||
assert transfer_data['token_address'] == foo_token
|
||||
|
||||
|
||||
def test_callback_filter(
|
||||
def test_callback_filter_filter(
|
||||
default_chain_spec,
|
||||
init_database,
|
||||
eth_rpc,
|
||||
@@ -213,6 +216,7 @@ def test_callback_filter(
|
||||
|
||||
def call_back(self, transfer_type, result):
|
||||
self.results[transfer_type] = result
|
||||
logg.debug('result {}'.format(result))
|
||||
return self
|
||||
|
||||
mock = CallbackMock()
|
||||
@@ -221,4 +225,4 @@ def test_callback_filter(
|
||||
fltr.filter(eth_rpc, mockblock, tx, init_database)
|
||||
|
||||
assert mock.results.get('transfer') != None
|
||||
assert mock.results['transfer']['destination_token'] == foo_token
|
||||
assert mock.results['transfer']['destination_token'] == strip_0x(foo_token)
|
||||
|
||||
@@ -17,6 +17,9 @@ from chainlib.eth.block import (
|
||||
block_by_number,
|
||||
Block,
|
||||
)
|
||||
from chainlib.eth.address import (
|
||||
to_checksum_address,
|
||||
)
|
||||
from erc20_faucet import Faucet
|
||||
from hexathon import (
|
||||
strip_0x,
|
||||
@@ -25,7 +28,6 @@ from hexathon import (
|
||||
|
||||
# local imports
|
||||
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()
|
||||
@@ -70,12 +72,13 @@ def test_register_filter(
|
||||
tx = Tx(tx_src, block=block, rcpt=rcpt)
|
||||
tx.apply_receipt(rcpt)
|
||||
|
||||
fltr = RegistrationFilter(default_chain_spec, add_0x(os.urandom(20).hex()), queue=None)
|
||||
fltr = RegistrationFilter(default_chain_spec, to_checksum_address(os.urandom(20).hex()), queue=None)
|
||||
t = fltr.filter(eth_rpc, block, tx, db_session=init_database)
|
||||
assert t == None
|
||||
|
||||
fltr = RegistrationFilter(default_chain_spec, account_registry, queue=None)
|
||||
fltr = RegistrationFilter(default_chain_spec, to_checksum_address(account_registry), queue=None)
|
||||
t = fltr.filter(eth_rpc, block, tx, db_session=init_database)
|
||||
logg.debug('t {}'.format(t))
|
||||
|
||||
t.get_leaf()
|
||||
assert t.successful()
|
||||
@@ -89,4 +92,4 @@ def test_register_filter(
|
||||
gift_tx = unpack(tx_raw_signed_bytes, default_chain_spec)
|
||||
|
||||
gift = Faucet.parse_give_to_request(gift_tx['data'])
|
||||
assert gift[0] == agent_roles['ALICE']
|
||||
assert add_0x(gift[0]) == agent_roles['ALICE']
|
||||
|
||||
@@ -19,6 +19,7 @@ from chainqueue.sql.query import get_account_tx
|
||||
|
||||
# local imports
|
||||
from cic_eth.runnable.daemons.filters.transferauth import TransferAuthFilter
|
||||
from cic_eth.encode import tx_normalize
|
||||
|
||||
|
||||
def test_filter_transferauth(
|
||||
@@ -66,7 +67,8 @@ def test_filter_transferauth(
|
||||
t.get_leaf()
|
||||
assert t.successful()
|
||||
|
||||
approve_txs = get_account_tx(default_chain_spec.asdict(), agent_roles['ALICE'], as_sender=True, session=init_database)
|
||||
#approve_txs = get_account_tx(default_chain_spec.asdict(), agent_roles['ALICE'], as_sender=True, session=init_database)
|
||||
approve_txs = get_account_tx(default_chain_spec.asdict(), tx_normalize.wallet_address(agent_roles['ALICE']), as_sender=True, session=init_database)
|
||||
ks = list(approve_txs.keys())
|
||||
assert len(ks) == 1
|
||||
|
||||
@@ -76,4 +78,4 @@ def test_filter_transferauth(
|
||||
|
||||
c = ERC20(default_chain_spec)
|
||||
approve = c.parse_approve_request(approve_tx['data'])
|
||||
assert approve[0] == agent_roles['BOB']
|
||||
assert approve[0] == strip_0x(agent_roles['BOB'])
|
||||
|
||||
@@ -110,8 +110,8 @@ def test_tag_account(
|
||||
t = api.tag_account('bar', agent_roles['CAROL'], default_chain_spec)
|
||||
t.get()
|
||||
|
||||
assert AccountRole.get_address('foo', init_database) == agent_roles['ALICE']
|
||||
assert AccountRole.get_address('bar', init_database) == agent_roles['CAROL']
|
||||
assert AccountRole.get_address('foo', init_database) == tx_normalize.wallet_address(agent_roles['ALICE'])
|
||||
assert AccountRole.get_address('bar', init_database) == tx_normalize.wallet_address(agent_roles['CAROL'])
|
||||
|
||||
|
||||
def test_tx(
|
||||
|
||||
@@ -10,6 +10,7 @@ from cic_eth_registry.erc20 import ERC20Token
|
||||
from chainlib.chain import ChainSpec
|
||||
from eth_accounts_index import AccountsIndex
|
||||
from chainlib.eth.tx import (
|
||||
receipt,
|
||||
transaction,
|
||||
)
|
||||
from chainqueue.sql.state import (
|
||||
@@ -29,6 +30,7 @@ def test_account_api(
|
||||
init_database,
|
||||
init_eth_rpc,
|
||||
account_registry,
|
||||
cic_registry,
|
||||
custodial_roles,
|
||||
celery_session_worker,
|
||||
):
|
||||
@@ -49,6 +51,7 @@ def test_account_api_register(
|
||||
eth_rpc,
|
||||
celery_session_worker,
|
||||
):
|
||||
|
||||
api = Api(str(default_chain_spec), callback_param='accounts', callback_task='cic_eth.callbacks.noop.noop', queue=None)
|
||||
t = api.create_account('')
|
||||
register_tx_hash = t.get_leaf()
|
||||
@@ -69,12 +72,18 @@ def test_account_api_register(
|
||||
r = t.get_leaf()
|
||||
assert t.successful()
|
||||
|
||||
o = receipt(register_tx_hash)
|
||||
r = eth_rpc.do(o)
|
||||
assert r['status'] == 1
|
||||
|
||||
o = transaction(register_tx_hash)
|
||||
tx_src = eth_rpc.do(o)
|
||||
|
||||
c = AccountsIndex(default_chain_spec)
|
||||
address = c.parse_add_request(tx_src['data'])
|
||||
logg.debug('address {} '.format(address))
|
||||
o = c.have(account_registry, address[0], sender_address=custodial_roles['CONTRACT_DEPLOYER'])
|
||||
logg.debug('o {}'.format(o))
|
||||
r = eth_rpc.do(o)
|
||||
assert c.parse_have(r)
|
||||
|
||||
|
||||
@@ -3,18 +3,22 @@ import os
|
||||
import logging
|
||||
|
||||
# external imports
|
||||
import pytest
|
||||
from chainlib.eth.address import to_checksum_address
|
||||
from hexathon import add_0x
|
||||
|
||||
# local imports
|
||||
from cic_eth.api.api_task import Api
|
||||
|
||||
logg = logging.getLogger()
|
||||
|
||||
|
||||
def test_balance_simple_api(
|
||||
default_chain_spec,
|
||||
init_database,
|
||||
cic_registry,
|
||||
foo_token,
|
||||
register_lookups,
|
||||
register_tokens,
|
||||
api,
|
||||
celery_session_worker,
|
||||
@@ -22,7 +26,7 @@ def test_balance_simple_api(
|
||||
|
||||
chain_str = str(default_chain_spec)
|
||||
|
||||
a = to_checksum_address('0x' + os.urandom(20).hex())
|
||||
a = add_0x(to_checksum_address(os.urandom(20).hex()))
|
||||
t = api.balance(a, 'FOO', include_pending=False)
|
||||
r = t.get_leaf()
|
||||
assert t.successful()
|
||||
@@ -36,6 +40,7 @@ def test_balance_complex_api(
|
||||
init_database,
|
||||
cic_registry,
|
||||
foo_token,
|
||||
register_lookups,
|
||||
register_tokens,
|
||||
api,
|
||||
celery_session_worker,
|
||||
@@ -43,7 +48,7 @@ def test_balance_complex_api(
|
||||
|
||||
chain_str = str(default_chain_spec)
|
||||
|
||||
a = to_checksum_address('0x' + os.urandom(20).hex())
|
||||
a = add_0x(to_checksum_address(os.urandom(20).hex()))
|
||||
t = api.balance(a, 'FOO', include_pending=True)
|
||||
r = t.get_leaf()
|
||||
assert t.successful()
|
||||
|
||||
@@ -6,6 +6,7 @@ import pytest
|
||||
from chainlib.eth.nonce import RPCNonceOracle
|
||||
from eth_erc20 import ERC20
|
||||
from chainlib.eth.tx import receipt
|
||||
from hexathon import strip_0x
|
||||
|
||||
# local imports
|
||||
from cic_eth.api.api_task import Api
|
||||
@@ -23,7 +24,6 @@ from cic_eth.pytest.mock.filter import (
|
||||
logg = logging.getLogger()
|
||||
|
||||
|
||||
@pytest.mark.xfail()
|
||||
def test_list_tx(
|
||||
default_chain_spec,
|
||||
init_database,
|
||||
@@ -34,8 +34,10 @@ def test_list_tx(
|
||||
agent_roles,
|
||||
foo_token,
|
||||
register_tokens,
|
||||
register_lookups,
|
||||
init_eth_tester,
|
||||
celery_session_worker,
|
||||
init_celery_tasks,
|
||||
):
|
||||
|
||||
tx_hashes = []
|
||||
@@ -63,13 +65,16 @@ def test_list_tx(
|
||||
o = receipt(tx_hash_hex)
|
||||
r = eth_rpc.do(o)
|
||||
assert r['status'] == 1
|
||||
|
||||
a = r['block_number']
|
||||
block_filter.add(a.to_bytes(4, 'big'))
|
||||
ab = a.to_bytes(4, 'big')
|
||||
block_filter.add(ab)
|
||||
|
||||
a = r['block_number'] + r['transaction_index']
|
||||
tx_filter.add(a.to_bytes(4, 'big'))
|
||||
bb = r['transaction_index'].to_bytes(4, 'big')
|
||||
cb = ab + bb
|
||||
tx_filter.add(cb)
|
||||
|
||||
tx_hashes.append(tx_hash_hex)
|
||||
tx_hashes.append(strip_0x(tx_hash_hex))
|
||||
|
||||
# external tx two
|
||||
Nonce.next(agent_roles['ALICE'], 'foo', session=init_database)
|
||||
@@ -83,26 +88,29 @@ def test_list_tx(
|
||||
o = receipt(tx_hash_hex)
|
||||
r = eth_rpc.do(o)
|
||||
assert r['status'] == 1
|
||||
|
||||
a = r['block_number']
|
||||
block_filter.add(a.to_bytes(4, 'big'))
|
||||
ab = a.to_bytes(4, 'big')
|
||||
block_filter.add(ab)
|
||||
|
||||
a = r['block_number'] + r['transaction_index']
|
||||
tx_filter.add(a.to_bytes(4, 'big'))
|
||||
bb = r['transaction_index'].to_bytes(4, 'big')
|
||||
cb = ab + bb
|
||||
tx_filter.add(cb)
|
||||
|
||||
tx_hashes.append(tx_hash_hex)
|
||||
tx_hashes.append(strip_0x(tx_hash_hex))
|
||||
|
||||
init_eth_tester.mine_blocks(28)
|
||||
|
||||
# custodial tx 1
|
||||
api = Api(str(default_chain_spec), queue=None)
|
||||
t = api.transfer(agent_roles['ALICE'], agent_roles['CAROL'], 64, 'FOO') #, 'blinky')
|
||||
t = api.transfer(agent_roles['ALICE'], agent_roles['CAROL'], 64, 'FOO')
|
||||
r = t.get_leaf()
|
||||
assert t.successful()
|
||||
tx_hashes.append(r)
|
||||
|
||||
# custodial tx 2
|
||||
api = Api(str(default_chain_spec), queue=None)
|
||||
t = api.transfer(agent_roles['ALICE'], agent_roles['DAVE'], 16, 'FOO') #, 'blinky')
|
||||
t = api.transfer(agent_roles['ALICE'], agent_roles['DAVE'], 16, 'FOO')
|
||||
r = t.get_leaf()
|
||||
assert t.successful()
|
||||
tx_hashes.append(r)
|
||||
@@ -117,7 +125,8 @@ def test_list_tx(
|
||||
assert len(r) == 3
|
||||
logg.debug('rrrr {}'.format(r))
|
||||
|
||||
logg.debug('testing against hashes {}'.format(tx_hashes))
|
||||
for tx in r:
|
||||
logg.debug('have tx {}'.format(tx))
|
||||
tx_hashes.remove(tx['hash'])
|
||||
tx_hashes.remove(strip_0x(tx['hash']))
|
||||
assert len(tx_hashes) == 1
|
||||
|
||||
@@ -10,6 +10,7 @@ from chainlib.eth.tx import (
|
||||
)
|
||||
from eth_erc20 import ERC20
|
||||
from chainlib.eth.nonce import RPCNonceOracle
|
||||
from hexathon import add_0x
|
||||
|
||||
# local imports
|
||||
from cic_eth.db.models.nonce import (
|
||||
@@ -91,5 +92,5 @@ def test_filter_process(
|
||||
|
||||
assert len(r) == 2
|
||||
for tx_hash in r.keys():
|
||||
tx_hashes.remove(tx_hash)
|
||||
tx_hashes.remove(add_0x(tx_hash))
|
||||
assert len(tx_hashes) == 0
|
||||
|
||||
@@ -75,7 +75,7 @@ def test_task_check_gas_ok(
|
||||
'cic_eth.eth.gas.check_gas',
|
||||
[
|
||||
[
|
||||
tx_hash_hex,
|
||||
strip_0x(tx_hash_hex),
|
||||
],
|
||||
default_chain_spec.asdict(),
|
||||
[],
|
||||
@@ -283,4 +283,3 @@ def test_task_resend_explicit(
|
||||
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']))
|
||||
assert tx_after['gasPrice'] > tx_before['gasPrice']
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
crypto-dev-signer>=0.4.15a1,<=0.4.15
|
||||
crypto-dev-signer>=0.4.15a7,<=0.4.15
|
||||
chainqueue>=0.0.5a1,<0.1.0
|
||||
cic-eth-registry>=0.6.1a2,<0.7.0
|
||||
cic-eth-registry>=0.6.1a3,<0.7.0
|
||||
redis==3.5.3
|
||||
hexathon~=0.0.1a8
|
||||
pycryptodome==3.10.1
|
||||
|
||||
@@ -9,8 +9,8 @@ build-test-cic-meta:
|
||||
- cd apps/cic-meta
|
||||
- docker build -t $MR_IMAGE_TAG -f docker/Dockerfile .
|
||||
- docker run --entrypoint=sh $MR_IMAGE_TAG docker/run_tests.sh
|
||||
#rules:
|
||||
#- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
# changes:
|
||||
# - apps/$APP_NAME/**/*
|
||||
# when: always
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
changes:
|
||||
- apps/$APP_NAME/**/*
|
||||
when: always
|
||||
|
||||
1
apps/cic-meta/README.md
Normal file
1
apps/cic-meta/README.md
Normal file
@@ -0,0 +1 @@
|
||||
# CIC-Meta
|
||||
@@ -11,12 +11,12 @@ celery_app = celery.current_app
|
||||
|
||||
|
||||
@celery_app.task
|
||||
def persist_notification(recipient, message):
|
||||
def persist_notification(message, recipient):
|
||||
"""
|
||||
:param recipient:
|
||||
:type recipient:
|
||||
:param message:
|
||||
:type message:
|
||||
:param recipient:
|
||||
:type recipient:
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
|
||||
@@ -11,12 +11,13 @@ local_logg = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@celery_app.task
|
||||
def log(recipient, message):
|
||||
def log(message, recipient):
|
||||
"""
|
||||
:param recipient:
|
||||
:type recipient:
|
||||
|
||||
:param message:
|
||||
:type message:
|
||||
:param recipient:
|
||||
:type recipient:
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
# standard imports
|
||||
|
||||
import json
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
# third-party imports
|
||||
from cic_eth.api import Api
|
||||
from cic_eth_aux.erc20_demurrage_token.api import Api as DemurrageApi
|
||||
|
||||
# local imports
|
||||
from cic_ussd.account.transaction import from_wei
|
||||
@@ -73,6 +75,24 @@ def calculate_available_balance(balances: dict) -> float:
|
||||
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:
|
||||
"""This function attempts to retrieve balance data from the redis cache.
|
||||
:param blockchain_address: Ethereum address of an account.
|
||||
@@ -81,10 +101,21 @@ def get_cached_available_balance(blockchain_address: str) -> float:
|
||||
:return: Operational balance of an account.
|
||||
:rtype: float
|
||||
"""
|
||||
identifier = bytes.fromhex(blockchain_address[2:])
|
||||
identifier = bytes.fromhex(blockchain_address)
|
||||
key = cache_data_key(identifier, salt=':cic.balances')
|
||||
cached_balances = get_cached_data(key=key)
|
||||
if cached_balances:
|
||||
return calculate_available_balance(json.loads(cached_balances))
|
||||
else:
|
||||
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)
|
||||
|
||||
@@ -4,7 +4,6 @@ import logging
|
||||
from typing import Optional
|
||||
|
||||
# external imports
|
||||
from chainlib.hash import strip_0x
|
||||
from cic_types.models.person import Person
|
||||
|
||||
# local imports
|
||||
@@ -20,7 +19,7 @@ def get_cached_preferred_language(blockchain_address: str) -> Optional[str]:
|
||||
:return: Account's set preferred language | Fallback preferred language.
|
||||
:rtype: str
|
||||
"""
|
||||
identifier = bytes.fromhex(strip_0x(blockchain_address))
|
||||
identifier = bytes.fromhex(blockchain_address)
|
||||
preferences_metadata_handler = PreferencesMetadata(identifier)
|
||||
cached_preferences_metadata = preferences_metadata_handler.get_cached_metadata()
|
||||
if cached_preferences_metadata:
|
||||
|
||||
@@ -86,7 +86,7 @@ def query_statement(blockchain_address: str, limit: int = 9):
|
||||
:param limit: Number of transactions to be returned.
|
||||
:type limit: int
|
||||
"""
|
||||
logg.debug(f'retrieving balance for address: {blockchain_address}')
|
||||
logg.debug(f'retrieving statement for address: {blockchain_address}')
|
||||
chain_str = Chain.spec.__str__()
|
||||
cic_eth_api = Api(
|
||||
chain_str=chain_str,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# standard import
|
||||
import decimal
|
||||
import json
|
||||
import logging
|
||||
from typing import Dict, Tuple
|
||||
|
||||
@@ -8,6 +9,8 @@ from cic_eth.api import Api
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
# local import
|
||||
from cic_ussd.account.chain import Chain
|
||||
from cic_ussd.account.tokens import get_cached_default_token
|
||||
from cic_ussd.db.models.account import Account
|
||||
from cic_ussd.db.models.base import SessionBase
|
||||
from cic_ussd.error import UnknownUssdRecipient
|
||||
@@ -59,7 +62,9 @@ def from_wei(value: int) -> float:
|
||||
:return: SRF equivalent of value in Wei
|
||||
:rtype: float
|
||||
"""
|
||||
value = float(value) / 1e+6
|
||||
cached_token_data = json.loads(get_cached_default_token(Chain.spec.__str__()))
|
||||
token_decimals: int = cached_token_data.get('decimals')
|
||||
value = float(value) / (10**token_decimals)
|
||||
return truncate(value=value, decimals=2)
|
||||
|
||||
|
||||
@@ -70,7 +75,9 @@ def to_wei(value: int) -> int:
|
||||
:return: Wei equivalent of value in SRF
|
||||
:rtype: int
|
||||
"""
|
||||
return int(value * 1e+6)
|
||||
cached_token_data = json.loads(get_cached_default_token(Chain.spec.__str__()))
|
||||
token_decimals: int = cached_token_data.get('decimals')
|
||||
return int(value * (10**token_decimals))
|
||||
|
||||
|
||||
def truncate(value: float, decimals: int):
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import json
|
||||
|
||||
# external imports
|
||||
from chainlib.hash import strip_0x
|
||||
from cic_eth.api import Api
|
||||
|
||||
# local imports
|
||||
@@ -101,7 +100,7 @@ class Account(SessionBase):
|
||||
session.add(self)
|
||||
session.flush()
|
||||
SessionBase.release_session(session=session)
|
||||
return f'Pin reset successful.'
|
||||
return 'Pin reset successful.'
|
||||
|
||||
def standard_metadata_id(self) -> str:
|
||||
"""This function creates an account's standard metadata identification information that contains an account owner's
|
||||
@@ -109,7 +108,7 @@ class Account(SessionBase):
|
||||
:return: Standard metadata identification information | e164 formatted phone number.
|
||||
:rtype: str
|
||||
"""
|
||||
identifier = bytes.fromhex(strip_0x(self.blockchain_address))
|
||||
identifier = bytes.fromhex(self.blockchain_address)
|
||||
key = cache_data_key(identifier, ':cic.person')
|
||||
account_metadata = get_cached_data(key)
|
||||
if not account_metadata:
|
||||
|
||||
@@ -44,7 +44,7 @@ class MetadataRequestsHandler(Metadata):
|
||||
|
||||
def create(self, data: Union[Dict, str]):
|
||||
""""""
|
||||
data = json.dumps(data)
|
||||
data = json.dumps(data).encode('utf-8')
|
||||
result = make_request(method='POST', url=self.url, data=data, headers=self.headers)
|
||||
|
||||
error_handler(result=result)
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
# standard imports
|
||||
import json
|
||||
import logging
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
# external imports
|
||||
import i18n.config
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
# local imports
|
||||
from cic_ussd.account.balance import calculate_available_balance, get_balances, get_cached_available_balance
|
||||
from cic_ussd.account.balance import (calculate_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.metadata import get_cached_preferred_language
|
||||
from cic_ussd.account.statement import (
|
||||
@@ -16,14 +20,15 @@ from cic_ussd.account.statement import (
|
||||
query_statement,
|
||||
statement_transaction_set
|
||||
)
|
||||
from cic_ussd.account.transaction import from_wei, to_wei
|
||||
from cic_ussd.account.tokens import get_default_token_symbol
|
||||
from cic_ussd.account.transaction import from_wei, to_wei
|
||||
from cic_ussd.cache import cache_data_key, cache_data
|
||||
from cic_ussd.db.models.account import Account
|
||||
from cic_ussd.metadata import PersonMetadata
|
||||
from cic_ussd.phone_number import Support
|
||||
from cic_ussd.processor.util import latest_input, parse_person_metadata
|
||||
from cic_ussd.processor.util import parse_person_metadata
|
||||
from cic_ussd.translation import translation_for
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
logg = logging.getLogger(__name__)
|
||||
|
||||
@@ -32,7 +37,7 @@ class MenuProcessor:
|
||||
def __init__(self, account: Account, display_key: str, menu_name: str, session: Session, ussd_session: dict):
|
||||
self.account = account
|
||||
self.display_key = display_key
|
||||
self.identifier = bytes.fromhex(self.account.blockchain_address[2:])
|
||||
self.identifier = bytes.fromhex(self.account.blockchain_address)
|
||||
self.menu_name = menu_name
|
||||
self.session = session
|
||||
self.ussd_session = ussd_session
|
||||
@@ -43,21 +48,26 @@ class MenuProcessor:
|
||||
:rtype:
|
||||
"""
|
||||
available_balance = get_cached_available_balance(self.account.blockchain_address)
|
||||
logg.debug('Requires call to retrieve tax and bonus amounts')
|
||||
tax = ''
|
||||
bonus = ''
|
||||
adjusted_balance = get_cached_adjusted_balance(self.identifier)
|
||||
token_symbol = get_default_token_symbol()
|
||||
preferred_language = get_cached_preferred_language(self.account.blockchain_address)
|
||||
if not preferred_language:
|
||||
preferred_language = i18n.config.get('fallback')
|
||||
return translation_for(
|
||||
key=self.display_key,
|
||||
preferred_language=preferred_language,
|
||||
available_balance=available_balance,
|
||||
tax=tax,
|
||||
bonus=bonus,
|
||||
token_symbol=token_symbol
|
||||
)
|
||||
with_available_balance = f'{self.display_key}.available_balance'
|
||||
with_fees = f'{self.display_key}.with_fees'
|
||||
if not adjusted_balance:
|
||||
return translation_for(key=with_available_balance,
|
||||
preferred_language=preferred_language,
|
||||
available_balance=available_balance,
|
||||
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:
|
||||
"""
|
||||
@@ -67,7 +77,7 @@ class MenuProcessor:
|
||||
cached_statement = get_cached_statement(self.account.blockchain_address)
|
||||
statement = json.loads(cached_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)
|
||||
if not preferred_language:
|
||||
preferred_language = i18n.config.get('fallback')
|
||||
@@ -149,12 +159,22 @@ class MenuProcessor:
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
chain_str = Chain.spec.__str__()
|
||||
token_symbol = get_default_token_symbol()
|
||||
blockchain_address = self.account.blockchain_address
|
||||
balances = get_balances(blockchain_address, Chain.spec.__str__(), token_symbol, False)[0]
|
||||
balances = get_balances(blockchain_address, chain_str, token_symbol, False)[0]
|
||||
key = cache_data_key(self.identifier, ':cic.balances')
|
||||
cache_data(key, json.dumps(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)
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ elif ssl == 0:
|
||||
else:
|
||||
ssl = True
|
||||
|
||||
|
||||
valid_service_codes = config.get('USSD_SERVICE_CODE').split(",")
|
||||
def main():
|
||||
|
||||
# TODO: improve url building
|
||||
@@ -79,9 +79,9 @@ def main():
|
||||
session = uuid.uuid4().hex
|
||||
data = {
|
||||
'sessionId': session,
|
||||
'serviceCode': config.get('APP_SERVICE_CODE'),
|
||||
'serviceCode': valid_service_codes[0],
|
||||
'phoneNumber': args.phone,
|
||||
'text': config.get('APP_SERVICE_CODE'),
|
||||
'text': "",
|
||||
}
|
||||
|
||||
state = "_BEGIN"
|
||||
|
||||
@@ -146,7 +146,7 @@ def create_ussd_session(
|
||||
)
|
||||
|
||||
|
||||
def update_ussd_session(ussd_session: UssdSession,
|
||||
def update_ussd_session(ussd_session: DbUssdSession,
|
||||
user_input: str,
|
||||
state: str,
|
||||
data: Optional[dict] = None) -> UssdSession:
|
||||
|
||||
@@ -154,15 +154,14 @@ def parse_person_metadata(account: Account, metadata: dict):
|
||||
phone_number = account.phone_number
|
||||
date_registered = int(account.created.replace().timestamp())
|
||||
blockchain_address = account.blockchain_address
|
||||
chain_spec = f'{Chain.spec.common_name()}:{Chain.spec.engine()}: {Chain.spec.chain_id()}'
|
||||
chain_str = Chain.spec.__str__()
|
||||
|
||||
if isinstance(metadata.get('identities'), dict):
|
||||
identities = metadata.get('identities')
|
||||
else:
|
||||
identities = manage_identity_data(
|
||||
blockchain_address=blockchain_address,
|
||||
blockchain_type=Chain.spec.engine(),
|
||||
chain_spec=chain_spec
|
||||
chain_str=chain_str
|
||||
)
|
||||
|
||||
return {
|
||||
|
||||
@@ -5,7 +5,6 @@ from datetime import timedelta
|
||||
|
||||
# third-party imports
|
||||
import celery
|
||||
from chainlib.hash import strip_0x
|
||||
|
||||
# local imports
|
||||
from cic_ussd.account.balance import get_balances, calculate_available_balance
|
||||
@@ -55,6 +54,7 @@ def account_creation_callback(self, result: str, url: str, status_code: int):
|
||||
session.add(account)
|
||||
session.commit()
|
||||
session.close()
|
||||
logg.debug(f'recorded account with identifier: {result}')
|
||||
|
||||
queue = self.request.delivery_info.get('routing_key')
|
||||
s_phone_pointer = celery.signature(
|
||||
@@ -86,7 +86,7 @@ def balances_callback(result: list, param: str, status_code: int):
|
||||
raise ValueError(f'Unexpected status code: {status_code}.')
|
||||
|
||||
balances = result[0]
|
||||
identifier = bytes.fromhex(strip_0x(param))
|
||||
identifier = bytes.fromhex(param)
|
||||
key = cache_data_key(identifier, ':cic.balances')
|
||||
cache_data(key, json.dumps(balances))
|
||||
|
||||
@@ -113,8 +113,10 @@ def statement_callback(self, result, param: str, status_code: int):
|
||||
for transaction in statement_transactions:
|
||||
recipient_transaction, sender_transaction = transaction_actors(transaction)
|
||||
if recipient_transaction.get('blockchain_address') == param:
|
||||
recipient_transaction['alt_blockchain_address'] = sender_transaction.get('blockchain_address')
|
||||
generate(param, queue, recipient_transaction)
|
||||
if sender_transaction.get('blockchain_address') == param:
|
||||
sender_transaction['alt_blockchain_address'] = recipient_transaction.get('blockchain_address')
|
||||
generate(param, queue, sender_transaction)
|
||||
|
||||
|
||||
@@ -138,26 +140,14 @@ def transaction_balances_callback(self, result: list, param: dict, status_code:
|
||||
balances_data = result[0]
|
||||
available_balance = calculate_available_balance(balances_data)
|
||||
transaction = param
|
||||
blockchain_address = transaction.get('blockchain_address')
|
||||
transaction['available_balance'] = available_balance
|
||||
queue = self.request.delivery_info.get('routing_key')
|
||||
|
||||
s_preferences_metadata = celery.signature(
|
||||
'cic_ussd.tasks.metadata.query_preferences_metadata', [blockchain_address], queue=queue
|
||||
)
|
||||
s_process_account_metadata = celery.signature(
|
||||
'cic_ussd.tasks.processor.parse_transaction', [transaction], queue=queue
|
||||
)
|
||||
s_notify_account = celery.signature('cic_ussd.tasks.notifications.transaction', queue=queue)
|
||||
|
||||
if transaction.get('transaction_type') == 'transfer':
|
||||
celery.chain(s_preferences_metadata, s_process_account_metadata, s_notify_account).apply_async()
|
||||
|
||||
if transaction.get('transaction_type') == 'tokengift':
|
||||
s_process_account_metadata = celery.signature(
|
||||
'cic_ussd.tasks.processor.parse_transaction', [{}, transaction], queue=queue
|
||||
)
|
||||
celery.chain(s_process_account_metadata, s_notify_account).apply_async()
|
||||
celery.chain(s_process_account_metadata, s_notify_account).apply_async()
|
||||
|
||||
|
||||
@celery_app.task
|
||||
|
||||
@@ -3,7 +3,6 @@ import logging
|
||||
|
||||
# third-party imports
|
||||
import celery
|
||||
from hexathon import strip_0x
|
||||
|
||||
# local imports
|
||||
from cic_ussd.metadata import CustomMetadata, PersonMetadata, PhonePointerMetadata, PreferencesMetadata
|
||||
@@ -21,7 +20,7 @@ def query_person_metadata(blockchain_address: str):
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
identifier = bytes.fromhex(strip_0x(blockchain_address))
|
||||
identifier = bytes.fromhex(blockchain_address)
|
||||
person_metadata_client = PersonMetadata(identifier=identifier)
|
||||
person_metadata_client.query()
|
||||
|
||||
@@ -36,14 +35,14 @@ def create_person_metadata(blockchain_address: str, data: dict):
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
identifier = bytes.fromhex(strip_0x(blockchain_address))
|
||||
identifier = bytes.fromhex(blockchain_address)
|
||||
person_metadata_client = PersonMetadata(identifier=identifier)
|
||||
person_metadata_client.create(data=data)
|
||||
|
||||
|
||||
@celery_app.task
|
||||
def edit_person_metadata(blockchain_address: str, data: dict):
|
||||
identifier = bytes.fromhex(strip_0x(blockchain_address))
|
||||
identifier = bytes.fromhex(blockchain_address)
|
||||
person_metadata_client = PersonMetadata(identifier=identifier)
|
||||
person_metadata_client.edit(data=data)
|
||||
|
||||
@@ -51,21 +50,21 @@ def edit_person_metadata(blockchain_address: str, data: dict):
|
||||
@celery_app.task(bind=True, base=CriticalMetadataTask)
|
||||
def add_phone_pointer(self, blockchain_address: str, phone_number: str):
|
||||
identifier = phone_number.encode('utf-8')
|
||||
stripped_address = strip_0x(blockchain_address)
|
||||
stripped_address = blockchain_address
|
||||
phone_metadata_client = PhonePointerMetadata(identifier=identifier)
|
||||
phone_metadata_client.create(data=stripped_address)
|
||||
|
||||
|
||||
@celery_app.task()
|
||||
def add_custom_metadata(blockchain_address: str, data: dict):
|
||||
identifier = bytes.fromhex(strip_0x(blockchain_address))
|
||||
identifier = bytes.fromhex(blockchain_address)
|
||||
custom_metadata_client = CustomMetadata(identifier=identifier)
|
||||
custom_metadata_client.create(data=data)
|
||||
|
||||
|
||||
@celery_app.task()
|
||||
def add_preferences_metadata(blockchain_address: str, data: dict):
|
||||
identifier = bytes.fromhex(strip_0x(blockchain_address))
|
||||
identifier = bytes.fromhex(blockchain_address)
|
||||
preferences_metadata_client = PreferencesMetadata(identifier=identifier)
|
||||
preferences_metadata_client.create(data=data)
|
||||
|
||||
@@ -76,7 +75,7 @@ def query_preferences_metadata(blockchain_address: str):
|
||||
:param blockchain_address: Blockchain address of an account.
|
||||
:type blockchain_address: str | Ox-hex
|
||||
"""
|
||||
identifier = bytes.fromhex(strip_0x(blockchain_address))
|
||||
identifier = bytes.fromhex(blockchain_address)
|
||||
logg.debug(f'Retrieving preferences metadata for address: {blockchain_address}.')
|
||||
person_metadata_client = PreferencesMetadata(identifier=identifier)
|
||||
return person_metadata_client.query()
|
||||
|
||||
@@ -24,7 +24,8 @@ def transaction(notification_data: dict):
|
||||
:rtype:
|
||||
"""
|
||||
role = notification_data.get('role')
|
||||
amount = from_wei(notification_data.get('token_value'))
|
||||
token_value = notification_data.get('token_value')
|
||||
amount = token_value if token_value == 0 else from_wei(token_value)
|
||||
balance = notification_data.get('available_balance')
|
||||
phone_number = notification_data.get('phone_number')
|
||||
preferred_language = notification_data.get('preferred_language')
|
||||
|
||||
@@ -5,9 +5,9 @@ import logging
|
||||
# third-party imports
|
||||
import celery
|
||||
import i18n
|
||||
from chainlib.hash import strip_0x
|
||||
|
||||
# local imports
|
||||
from cic_ussd.account.metadata import get_cached_preferred_language
|
||||
from cic_ussd.account.statement import get_cached_statement
|
||||
from cic_ussd.account.transaction import aux_transaction_data, validate_transaction_account
|
||||
from cic_ussd.cache import cache_data, cache_data_key
|
||||
@@ -23,17 +23,13 @@ logg = logging.getLogger(__file__)
|
||||
def generate_statement(self, querying_party: str, transaction: dict):
|
||||
""""""
|
||||
queue = self.request.delivery_info.get('routing_key')
|
||||
|
||||
s_preferences = celery.signature(
|
||||
'cic_ussd.tasks.metadata.query_preferences_metadata', [querying_party], queue=queue
|
||||
)
|
||||
s_parse_transaction = celery.signature(
|
||||
'cic_ussd.tasks.processor.parse_transaction', [transaction], queue=queue
|
||||
)
|
||||
s_cache_statement = celery.signature(
|
||||
'cic_ussd.tasks.processor.cache_statement', [querying_party], queue=queue
|
||||
)
|
||||
celery.chain(s_preferences, s_parse_transaction, s_cache_statement).apply_async()
|
||||
celery.chain(s_parse_transaction, s_cache_statement).apply_async()
|
||||
|
||||
|
||||
@celery_app.task
|
||||
@@ -52,25 +48,23 @@ def cache_statement(parsed_transaction: dict, querying_party: str):
|
||||
statement_transactions = json.loads(cached_statement)
|
||||
statement_transactions.append(parsed_transaction)
|
||||
data = json.dumps(statement_transactions)
|
||||
identifier = bytes.fromhex(strip_0x(querying_party))
|
||||
identifier = bytes.fromhex(querying_party)
|
||||
key = cache_data_key(identifier, ':cic.statement')
|
||||
cache_data(key, data)
|
||||
|
||||
|
||||
@celery_app.task
|
||||
def parse_transaction(preferences: dict, transaction: dict) -> dict:
|
||||
def parse_transaction(transaction: dict) -> dict:
|
||||
"""This function parses transaction objects and collates all relevant data for system use i.e:
|
||||
- An account's set preferred language.
|
||||
- Account identifier that facilitates notification.
|
||||
- Contextual tags i.e action and direction tags.
|
||||
:param preferences: An account's set preferences.
|
||||
:type preferences: dict
|
||||
:param transaction: Transaction object.
|
||||
:type transaction: dict
|
||||
:return: Transaction object with contextual data for use in the system.
|
||||
:rtype: dict
|
||||
"""
|
||||
preferred_language = preferences.get('preferred_language')
|
||||
preferred_language = get_cached_preferred_language(transaction.get('blockchain_address'))
|
||||
if not preferred_language:
|
||||
preferred_language = i18n.config.get('fallback')
|
||||
transaction['preferred_language'] = preferred_language
|
||||
@@ -83,6 +77,8 @@ def parse_transaction(preferences: dict, transaction: dict) -> dict:
|
||||
alt_account = session.query(Account).filter_by(blockchain_address=alt_blockchain_address).first()
|
||||
if alt_account:
|
||||
transaction['alt_metadata_id'] = alt_account.standard_metadata_id()
|
||||
else:
|
||||
transaction['alt_metadata_id'] = 'GRASSROOTS ECONOMICS'
|
||||
transaction['metadata_id'] = account.standard_metadata_id()
|
||||
transaction['phone_number'] = account.phone_number
|
||||
session.close()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# standard imports
|
||||
import semver
|
||||
|
||||
version = (0, 3, 1, 'alpha.4')
|
||||
version = (0, 3, 1, 'alpha.5')
|
||||
|
||||
version_object = semver.VersionInfo(
|
||||
major=version[0],
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
[app]
|
||||
service_code = *483*46#
|
||||
@@ -1,3 +1,4 @@
|
||||
[ussd]
|
||||
service_code = *483*46#
|
||||
user =
|
||||
pass =
|
||||
|
||||
@@ -10,6 +10,13 @@ RUN mkdir -vp data
|
||||
|
||||
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 \
|
||||
--extra-index-url $GITLAB_PYTHON_REGISTRY \
|
||||
--extra-index-url $EXTRA_INDEX_URL \
|
||||
cic-eth-aux-erc20-demurrage-token~=0.0.2a6
|
||||
|
||||
COPY requirements.txt .
|
||||
|
||||
RUN --mount=type=cache,mode=0755,target=/root/.cache/pip \
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
alembic==1.4.2
|
||||
attrs==21.2.0
|
||||
billiard==3.6.4.0
|
||||
bcrypt==3.2.0
|
||||
celery==4.4.7
|
||||
cffi==1.14.6
|
||||
cic-eth[services]~=0.12.4a7
|
||||
cic-notify~=0.4.0a10
|
||||
cic-types~=0.1.0a14
|
||||
cic-types~=0.1.0a15
|
||||
confini>=0.4.1a1,<0.5.0
|
||||
phonenumbers==8.12.12
|
||||
psycopg2==2.8.6
|
||||
|
||||
@@ -43,10 +43,13 @@ def test_sync_get_balances(activated_account,
|
||||
(5000000, 89000000, 67000000, 27.00)
|
||||
])
|
||||
def test_calculate_available_balance(activated_account,
|
||||
available_balance,
|
||||
balance_incoming,
|
||||
balance_network,
|
||||
balance_outgoing,
|
||||
available_balance):
|
||||
cache_balances,
|
||||
cache_default_token_data,
|
||||
load_chain_spec):
|
||||
balances = {
|
||||
'address': activated_account.blockchain_address,
|
||||
'converters': [],
|
||||
@@ -57,7 +60,11 @@ def test_calculate_available_balance(activated_account,
|
||||
assert calculate_available_balance(balances) == available_balance
|
||||
|
||||
|
||||
def test_get_cached_available_balance(activated_account, cache_balances, balances):
|
||||
def test_get_cached_available_balance(activated_account,
|
||||
balances,
|
||||
cache_balances,
|
||||
cache_default_token_data,
|
||||
load_chain_spec):
|
||||
cached_available_balance = get_cached_available_balance(activated_account.blockchain_address)
|
||||
available_balance = calculate_available_balance(balances[0])
|
||||
assert cached_available_balance == available_balance
|
||||
|
||||
@@ -27,6 +27,7 @@ def test_filter_statement_transactions(transactions_list):
|
||||
|
||||
|
||||
def test_generate(activated_account,
|
||||
cache_default_token_data,
|
||||
cache_preferences,
|
||||
celery_session_worker,
|
||||
init_cache,
|
||||
@@ -35,22 +36,22 @@ def test_generate(activated_account,
|
||||
preferences,
|
||||
preferences_metadata_url,
|
||||
transactions_list):
|
||||
with requests_mock.Mocker(real_http=False) as request_mocker:
|
||||
request_mocker.register_uri('GET', preferences_metadata_url, status_code=200, reason='OK', json=preferences)
|
||||
statement_transactions = filter_statement_transactions(transactions_list)
|
||||
for transaction in statement_transactions:
|
||||
querying_party = activated_account.blockchain_address
|
||||
recipient_transaction, sender_transaction = transaction_actors(transaction)
|
||||
if recipient_transaction.get('blockchain_address') == querying_party:
|
||||
generate(querying_party, None, recipient_transaction)
|
||||
if sender_transaction.get('blockchain_address') == querying_party:
|
||||
generate(querying_party, None, sender_transaction)
|
||||
time.sleep(2)
|
||||
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
|
||||
key = cache_data_key(identifier, ':cic.statement')
|
||||
statement = get_cached_data(key)
|
||||
statement = json.loads(statement)
|
||||
assert len(statement) == 1
|
||||
statement_transactions = filter_statement_transactions(transactions_list)
|
||||
for transaction in statement_transactions:
|
||||
querying_party = activated_account.blockchain_address
|
||||
recipient_transaction, sender_transaction = transaction_actors(transaction)
|
||||
if recipient_transaction.get('blockchain_address') == querying_party:
|
||||
recipient_transaction['alt_blockchain_address'] = sender_transaction.get('blockchain_address')
|
||||
generate(querying_party, None, recipient_transaction)
|
||||
if sender_transaction.get('blockchain_address') == querying_party:
|
||||
sender_transaction['alt_blockchain_address'] = recipient_transaction.get('blockchain_address')
|
||||
generate(querying_party, None, sender_transaction)
|
||||
time.sleep(2)
|
||||
identifier = bytes.fromhex(activated_account.blockchain_address)
|
||||
key = cache_data_key(identifier, ':cic.statement')
|
||||
statement = get_cached_data(key)
|
||||
statement = json.loads(statement)
|
||||
assert len(statement) == 1
|
||||
|
||||
|
||||
def test_get_cached_statement(activated_account, cache_statement, statement):
|
||||
@@ -60,7 +61,7 @@ def test_get_cached_statement(activated_account, cache_statement, statement):
|
||||
assert cached_statement[0].get('blockchain_address') == statement[0].get('blockchain_address')
|
||||
|
||||
|
||||
def test_parse_statement_transactions(statement):
|
||||
def test_parse_statement_transactions(cache_default_token_data, statement):
|
||||
parsed_transactions = parse_statement_transactions(statement)
|
||||
parsed_transaction = parsed_transactions[0]
|
||||
parsed_transaction.startswith('Sent')
|
||||
@@ -76,7 +77,7 @@ def test_query_statement(blockchain_address, limit, load_chain_spec, activated_a
|
||||
assert mock_transaction_list_query.get('limit') == limit
|
||||
|
||||
|
||||
def test_statement_transaction_set(preferences, set_locale_files, statement):
|
||||
def test_statement_transaction_set(cache_default_token_data, load_chain_spec, preferences, set_locale_files, statement):
|
||||
parsed_transactions = parse_statement_transactions(statement)
|
||||
preferred_language = preferences.get('preferred_language')
|
||||
transaction_set = statement_transaction_set(preferred_language, parsed_transactions)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("wei, expected_result", [
|
||||
@pytest.mark.parametrize("value, expected_result", [
|
||||
(50000000, Decimal('50.00')),
|
||||
(100000, Decimal('0.10'))
|
||||
])
|
||||
def test_from_wei(wei, expected_result):
|
||||
assert from_wei(wei) == expected_result
|
||||
def test_from_wei(cache_default_token_data, expected_result, value):
|
||||
assert from_wei(value) == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize("value, expected_result", [
|
||||
(50, 50000000),
|
||||
(0.10, 100000)
|
||||
])
|
||||
def test_to_wei(value, expected_result):
|
||||
def test_to_wei(cache_default_token_data, expected_result, value):
|
||||
assert to_wei(value) == expected_result
|
||||
|
||||
|
||||
@@ -96,6 +96,7 @@ def test_validate_transaction_account(activated_account, init_database, transact
|
||||
@pytest.mark.parametrize("amount", [50, 0.10])
|
||||
def test_outgoing_transaction_processor(activated_account,
|
||||
amount,
|
||||
cache_default_token_data,
|
||||
celery_session_worker,
|
||||
load_config,
|
||||
load_chain_spec,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# standard imports
|
||||
import json
|
||||
import datetime
|
||||
|
||||
# external imports
|
||||
from chainlib.hash import strip_0x
|
||||
@@ -14,6 +15,7 @@ from cic_ussd.account.statement import (
|
||||
)
|
||||
from cic_ussd.account.tokens import get_default_token_symbol
|
||||
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.metadata import PersonMetadata
|
||||
from cic_ussd.phone_number import Support
|
||||
@@ -38,24 +40,34 @@ def test_menu_processor(activated_account,
|
||||
load_chain_spec,
|
||||
load_support_phone,
|
||||
load_ussd_menu,
|
||||
mock_get_adjusted_balance,
|
||||
mock_sync_balance_api_query,
|
||||
mock_transaction_list_query,
|
||||
valid_recipient):
|
||||
preferred_language = get_cached_preferred_language(activated_account.blockchain_address)
|
||||
available_balance = get_cached_available_balance(activated_account.blockchain_address)
|
||||
token_symbol = get_default_token_symbol()
|
||||
|
||||
tax = ''
|
||||
bonus = ''
|
||||
display_key = 'ussd.kenya.account_balances'
|
||||
with_available_balance = 'ussd.kenya.account_balances.available_balance'
|
||||
with_fees = 'ussd.kenya.account_balances.with_fees'
|
||||
ussd_menu = UssdMenu.find_by_name('account_balances')
|
||||
name = ussd_menu.get('name')
|
||||
resp = response(activated_account, display_key, name, init_database, generic_ussd_session)
|
||||
assert resp == translation_for(display_key,
|
||||
resp = response(activated_account, 'ussd.kenya.account_balances', name, init_database, generic_ussd_session)
|
||||
assert resp == translation_for(with_available_balance,
|
||||
preferred_language,
|
||||
available_balance=available_balance,
|
||||
token_symbol=token_symbol)
|
||||
|
||||
identifier = bytes.fromhex(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,
|
||||
bonus=bonus,
|
||||
token_symbol=token_symbol)
|
||||
|
||||
cached_statement = get_cached_statement(activated_account.blockchain_address)
|
||||
@@ -96,7 +108,7 @@ def test_menu_processor(activated_account,
|
||||
display_key = 'ussd.kenya.display_user_metadata'
|
||||
ussd_menu = UssdMenu.find_by_name('display_user_metadata')
|
||||
name = ussd_menu.get('name')
|
||||
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
|
||||
identifier = bytes.fromhex(activated_account.blockchain_address)
|
||||
person_metadata = PersonMetadata(identifier)
|
||||
cached_person_metadata = person_metadata.get_cached_metadata()
|
||||
resp = response(activated_account, display_key, name, init_database, generic_ussd_session)
|
||||
@@ -123,6 +135,15 @@ def test_menu_processor(activated_account,
|
||||
account_balance=available_balance,
|
||||
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'
|
||||
ussd_menu = UssdMenu.find_by_name('transaction_pin_authorization')
|
||||
name = ussd_menu.get('name')
|
||||
|
||||
@@ -49,6 +49,7 @@ def test_is_valid_transaction_amount(activated_account, amount, expected_result,
|
||||
])
|
||||
def test_has_sufficient_balance(activated_account,
|
||||
cache_balances,
|
||||
cache_default_token_data,
|
||||
expected_result,
|
||||
generic_ussd_session,
|
||||
init_database,
|
||||
|
||||
@@ -114,6 +114,7 @@ def test_statement_callback(activated_account, mocker, transactions_list):
|
||||
s_statement_callback.apply_async().get()
|
||||
statement_transactions = filter_statement_transactions(transactions_list)
|
||||
recipient_transaction, sender_transaction = transaction_actors(statement_transactions[0])
|
||||
sender_transaction['alt_blockchain_address'] = recipient_transaction.get('blockchain_address')
|
||||
mock_statement_generate.assert_called_with(
|
||||
(activated_account.blockchain_address, sender_transaction), {}, queue='cic-ussd')
|
||||
|
||||
@@ -121,6 +122,7 @@ def test_statement_callback(activated_account, mocker, transactions_list):
|
||||
def test_transaction_balances_callback(activated_account,
|
||||
balances,
|
||||
cache_balances,
|
||||
cache_default_token_data,
|
||||
cache_person_metadata,
|
||||
cache_preferences,
|
||||
load_chain_spec,
|
||||
|
||||
@@ -13,7 +13,8 @@ from cic_ussd.translation import translation_for
|
||||
# tests imports
|
||||
|
||||
|
||||
def test_transaction(celery_session_worker,
|
||||
def test_transaction(cache_default_token_data,
|
||||
celery_session_worker,
|
||||
load_support_phone,
|
||||
mock_notifier_api,
|
||||
notification_data,
|
||||
|
||||
@@ -30,10 +30,11 @@ def test_generate_statement(activated_account,
|
||||
|
||||
|
||||
def test_cache_statement(activated_account,
|
||||
cache_default_token_data,
|
||||
cache_person_metadata,
|
||||
cache_preferences,
|
||||
celery_session_worker,
|
||||
init_database,
|
||||
preferences,
|
||||
transaction_result):
|
||||
recipient_transaction, sender_transaction = transaction_actors(transaction_result)
|
||||
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
|
||||
@@ -41,7 +42,7 @@ def test_cache_statement(activated_account,
|
||||
cached_statement = get_cached_data(key)
|
||||
assert cached_statement is None
|
||||
s_parse_transaction = celery.signature(
|
||||
'cic_ussd.tasks.processor.parse_transaction', [preferences, sender_transaction])
|
||||
'cic_ussd.tasks.processor.parse_transaction', [sender_transaction])
|
||||
result = s_parse_transaction.apply_async().get()
|
||||
s_cache_statement = celery.signature(
|
||||
'cic_ussd.tasks.processor.cache_statement', [result, activated_account.blockchain_address]
|
||||
@@ -61,15 +62,15 @@ def test_cache_statement(activated_account,
|
||||
|
||||
def test_parse_transaction(activated_account,
|
||||
cache_person_metadata,
|
||||
cache_preferences,
|
||||
celery_session_worker,
|
||||
init_database,
|
||||
preferences,
|
||||
transaction_result):
|
||||
recipient_transaction, sender_transaction = transaction_actors(transaction_result)
|
||||
assert sender_transaction.get('metadata_id') is None
|
||||
assert sender_transaction.get('phone_number') is None
|
||||
s_parse_transaction = celery.signature(
|
||||
'cic_ussd.tasks.processor.parse_transaction', [preferences, sender_transaction])
|
||||
'cic_ussd.tasks.processor.parse_transaction', [sender_transaction])
|
||||
result = s_parse_transaction.apply_async().get()
|
||||
assert result.get('metadata_id') == activated_account.standard_metadata_id()
|
||||
assert result.get('phone_number') == activated_account.phone_number
|
||||
|
||||
12
apps/cic-ussd/tests/fixtures/account.py
vendored
12
apps/cic-ussd/tests/fixtures/account.py
vendored
@@ -54,7 +54,7 @@ def cache_account_creation_data(init_cache, account_creation_data):
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def cache_balances(activated_account, balances, init_cache):
|
||||
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
|
||||
identifier = bytes.fromhex(activated_account.blockchain_address)
|
||||
balances = json.dumps(balances[0])
|
||||
key = cache_data_key(identifier, ':cic.balances')
|
||||
cache_data(key, balances)
|
||||
@@ -70,7 +70,7 @@ def cache_default_token_data(default_token_data, init_cache, load_chain_spec):
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def cache_person_metadata(activated_account, init_cache, person_metadata):
|
||||
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
|
||||
identifier = bytes.fromhex(activated_account.blockchain_address)
|
||||
person = json.dumps(person_metadata)
|
||||
key = cache_data_key(identifier, ':cic.person')
|
||||
cache_data(key, person)
|
||||
@@ -78,7 +78,7 @@ def cache_person_metadata(activated_account, init_cache, person_metadata):
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def cache_preferences(activated_account, init_cache, preferences):
|
||||
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
|
||||
identifier = bytes.fromhex(activated_account.blockchain_address)
|
||||
preferences = json.dumps(preferences)
|
||||
key = cache_data_key(identifier, ':cic.preferences')
|
||||
cache_data(key, preferences)
|
||||
@@ -86,10 +86,10 @@ def cache_preferences(activated_account, init_cache, preferences):
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def cache_statement(activated_account, init_cache, statement):
|
||||
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
|
||||
preferences = json.dumps(statement)
|
||||
identifier = bytes.fromhex(activated_account.blockchain_address)
|
||||
statement = json.dumps(statement)
|
||||
key = cache_data_key(identifier, ':cic.statement')
|
||||
cache_data(key, preferences)
|
||||
cache_data(key, statement)
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
|
||||
16
apps/cic-ussd/tests/fixtures/patches/account.py
vendored
16
apps/cic-ussd/tests/fixtures/patches/account.py
vendored
@@ -41,6 +41,22 @@ def mock_async_balance_api_query(mocker):
|
||||
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')
|
||||
def mock_notifier_api(mocker):
|
||||
sms = {}
|
||||
|
||||
14
apps/cic-ussd/tests/fixtures/transaction.py
vendored
14
apps/cic-ussd/tests/fixtures/transaction.py
vendored
@@ -7,6 +7,7 @@ import pytest
|
||||
# local import
|
||||
from cic_ussd.account.balance import get_cached_available_balance
|
||||
|
||||
|
||||
# tests imports
|
||||
|
||||
|
||||
@@ -103,8 +104,8 @@ def transactions_list(activated_account, valid_recipient):
|
||||
'destination_token': '0x0000000000000000000000000000000000000000',
|
||||
'block_number': 80,
|
||||
'tx_index': 0,
|
||||
'sender': '0x367cB0F65137b0A845c1DB4B7Ca47D3DEF32dDe8',
|
||||
'recipient': '0x103d1ed6e370dBa6267045c70d4999384c18a04A',
|
||||
'sender': '367cB0F65137b0A845c1DB4B7Ca47D3DEF32dDe8',
|
||||
'recipient': '103d1ed6e370dBa6267045c70d4999384c18a04A',
|
||||
'from_value': 0,
|
||||
'to_value': 0,
|
||||
'date_created': '2021-07-14T14:13:46.036198',
|
||||
@@ -122,8 +123,8 @@ def transactions_list(activated_account, valid_recipient):
|
||||
'destination_token': '0x0000000000000000000000000000000000000000',
|
||||
'block_number': 78,
|
||||
'tx_index': 0,
|
||||
'sender': '0xb41BfEE260693A473254D62b81aE1ADCC9E51AFb',
|
||||
'recipient': '0x367cB0F65137b0A845c1DB4B7Ca47D3DEF32dDe8',
|
||||
'sender': 'b41BfEE260693A473254D62b81aE1ADCC9E51AFb',
|
||||
'recipient': '367cB0F65137b0A845c1DB4B7Ca47D3DEF32dDe8',
|
||||
'from_value': 1800000000000000,
|
||||
'to_value': 1800000000000000,
|
||||
'date_created': '2021-07-14T14:13:35.839638',
|
||||
@@ -142,8 +143,8 @@ def transactions_list(activated_account, valid_recipient):
|
||||
'destination_token': '0x0000000000000000000000000000000000000000',
|
||||
'block_number': 79,
|
||||
'tx_index': 0,
|
||||
'sender': '0x367cB0F65137b0A845c1DB4B7Ca47D3DEF32dDe8',
|
||||
'recipient': '0x103d1ed6e370dBa6267045c70d4999384c18a04A',
|
||||
'sender': '367cB0F65137b0A845c1DB4B7Ca47D3DEF32dDe8',
|
||||
'recipient': '103d1ed6e370dBa6267045c70d4999384c18a04A',
|
||||
'from_value': 0,
|
||||
'to_value': 0,
|
||||
'date_created': '2021-07-14T14:13:35.638355',
|
||||
@@ -152,4 +153,3 @@ def transactions_list(activated_account, valid_recipient):
|
||||
'timestamp': 1626272015,
|
||||
'hash': '0x32ca3dd3bef06463b452f4d32f5f563d083cb4759219eed90f3d2a9c1791c5fc'}
|
||||
]
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import random
|
||||
import uuid
|
||||
|
||||
# external imports
|
||||
from chainlib.eth.address import to_checksum_address
|
||||
from faker import Faker
|
||||
from faker_e164.providers import E164Provider
|
||||
|
||||
@@ -21,7 +20,7 @@ def phone_number() -> str:
|
||||
|
||||
|
||||
def blockchain_address() -> str:
|
||||
return to_checksum_address('0x' + os.urandom(20).hex())
|
||||
return os.urandom(20).hex().lower()
|
||||
|
||||
|
||||
def session_id() -> str:
|
||||
|
||||
@@ -141,12 +141,22 @@ en:
|
||||
0. Back
|
||||
retry: |-
|
||||
%{retry_pin_entry}
|
||||
account_balances: |-
|
||||
CON Your balances are as follows:
|
||||
balance: %{available_balance} %{token_symbol}
|
||||
fees: %{tax} %{token_symbol}
|
||||
rewards: %{bonus} %{token_symbol}
|
||||
0. Back
|
||||
account_balances:
|
||||
available_balance: |-
|
||||
CON Your balances are as follows:
|
||||
balance: %{available_balance} %{token_symbol}
|
||||
0. Back
|
||||
with_fees: |-
|
||||
CON Your balances are as follows:
|
||||
balances: %{available_balance} %{token_symbol}
|
||||
fees: %{tax} %{token_symbol}
|
||||
0. Back
|
||||
with_rewards: |-
|
||||
CON Your balances are as follows:
|
||||
balance: %{available_balance} %{token_symbol}
|
||||
fees: %{tax} %{token_symbol}
|
||||
rewards: %{bonus} %{token_symbol}
|
||||
0. Back
|
||||
first_transaction_set: |-
|
||||
CON %{first_transaction_set}
|
||||
1. Next
|
||||
|
||||
@@ -140,12 +140,22 @@ sw:
|
||||
0. Nyuma
|
||||
retry: |-
|
||||
%{retry_pin_entry}
|
||||
account_balances: |-
|
||||
CON Salio zako ni zifuatazo:
|
||||
salio: %{available_balance} %{token_symbol}
|
||||
ushuru: %{tax} %{token_symbol}
|
||||
tuzo: %{bonus} %{token_symbol}
|
||||
0. Nyuma
|
||||
account_balances:
|
||||
available_balance: |-
|
||||
CON Salio zako ni zifuatazo:
|
||||
salio: %{available_balance} %{token_symbol}
|
||||
0. Nyuma
|
||||
with_fees: |-
|
||||
CON Salio zako ni zifuatazo:
|
||||
salio: %{available_balance} %{token_symbol}
|
||||
ushuru: %{tax} %{token_symbol}
|
||||
0. Nyuma
|
||||
with_rewards: |-
|
||||
CON Salio zako ni zifuatazo:
|
||||
salio: %{available_balance} %{token_symbol}
|
||||
ushuru: %{tax} %{token_symbol}
|
||||
tuzo: %{bonus} %{token_symbol}
|
||||
0. Nyuma
|
||||
first_transaction_set: |-
|
||||
CON %{first_transaction_set}
|
||||
1. Mbele
|
||||
|
||||
46
apps/contract-migration/config.sh
Normal file
46
apps/contract-migration/config.sh
Normal file
@@ -0,0 +1,46 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -a
|
||||
|
||||
if [ -z $DEV_DATA_DIR ]; then
|
||||
export DEV_DATA_DIR=`mktemp -d`
|
||||
else
|
||||
mkdir -p $DEV_DATA_DIR
|
||||
fi
|
||||
|
||||
if [ -z $DEV_CONFIG_RESET ]; then
|
||||
if [ -f ${DEV_DATA_DIR}/env_reset ]; then
|
||||
>&2 echo "importing existing configuration values from ${DEV_DATA_DIR}/env_reset"
|
||||
. ${DEV_DATA_DIR}/env_reset
|
||||
fi
|
||||
fi
|
||||
|
||||
# Handle wallet
|
||||
export WALLET_KEY_FILE=${WALLET_KEY_FILE:-`realpath ./keystore/UTC--2021-01-08T17-18-44.521011372Z--eb3907ecad74a0013c259d5874ae7f22dcbcc95c`}
|
||||
if [ ! -f $WALLET_KEY_FILE ]; then
|
||||
>&2 echo "wallet path '$WALLET_KEY_FILE' does not point to a file"
|
||||
exit 1
|
||||
fi
|
||||
export DEV_ETH_ACCOUNT_CONTRACT_DEPLOYER=`eth-checksum $(cat $WALLET_KEY_FILE | jq -r .address)`
|
||||
|
||||
# Wallet dependent variable defaults
|
||||
export DEV_ETH_ACCOUNT_RESERVE_MINTER=${DEV_ETH_ACCOUNT_RESERVE_MINTER:-$DEV_ETH_ACCOUNT_CONTRACT_DEPLOYER}
|
||||
export DEV_ETH_ACCOUNT_ACCOUNTS_INDEX_WRITER=${DEV_ETH_ACCOUNT_RESERVE_MINTER:-$DEV_ETH_ACCOUNT_CONTRACT_DEPLOYER}
|
||||
export CIC_TRUST_ADDRESS=${CIC_TRUST_ADDRESS:-$DEV_ETH_ACCOUNT_CONTRACT_DEPLOYER}
|
||||
export CIC_DEFAULT_TOKEN_SYMBOL=$TOKEN_SYMBOL
|
||||
export TOKEN_SINK_ADDRESS=${TOKEN_SINK_ADDRESS:-$DEV_ETH_ACCOUNT_CONTRACT_DEPLOYER}
|
||||
|
||||
|
||||
# Legacy variable defaults
|
||||
|
||||
|
||||
# Migration variable processing
|
||||
confini-dump --schema-dir ./config --prefix export > ${DEV_DATA_DIR}/env_reset
|
||||
echo "export CIC_TRUST_ADDRESS=$CIC_TRUST_ADDRESS
|
||||
export CIC_DEFAULT_TOKEN_SYMBOL=$CIC_DEFAULT_TOKEN_SYMBOL
|
||||
export WALLET_KEY_FILE=$WALLET_KEY_FILE
|
||||
" >> ${DEV_DATA_DIR}/env_reset
|
||||
|
||||
cat ${DEV_DATA_DIR}/env_reset
|
||||
|
||||
set +a
|
||||
13
apps/contract-migration/config/config.ini
Normal file
13
apps/contract-migration/config/config.ini
Normal file
@@ -0,0 +1,13 @@
|
||||
[dev]
|
||||
eth_account_contract_deployer =
|
||||
eth_account_reserve_minter =
|
||||
eth_account_accounts_index_writer =
|
||||
reserve_amount = 10000000000000000000000000000000000
|
||||
faucet_amount = 0
|
||||
gas_amount = 100000000000000000000000
|
||||
token_amount = 100000000000000000000000
|
||||
eth_gas_price =
|
||||
data_dir =
|
||||
pip_extra_index_url =
|
||||
eth_provider_host =
|
||||
eth_provider_port =
|
||||
@@ -1,44 +0,0 @@
|
||||
# syntax = docker/dockerfile:1.2
|
||||
FROM registry.gitlab.com/grassrootseconomics/cic-base-images:python-3.8.6-dev-55da5f4e
|
||||
|
||||
WORKDIR /root
|
||||
|
||||
RUN touch /etc/apt/sources.list.d/ethereum.list
|
||||
RUN echo 'deb http://ppa.launchpad.net/ethereum/ethereum/ubuntu bionic main' > /etc/apt/sources.list.d/ethereum.list
|
||||
RUN echo 'deb-src http://ppa.launchpad.net/ethereum/ethereum/ubuntu bionic main' >> /etc/apt/sources.list.d/ethereum.list
|
||||
RUN cat /etc/apt/sources.list.d/ethereum.list
|
||||
RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 2A518C819BE37D2C2031944D1C52189C923F6CA9
|
||||
|
||||
#RUN apt-get install solc
|
||||
|
||||
RUN mkdir -vp /usr/local/etc/cic
|
||||
|
||||
ENV CONFINI_DIR /usr/local/etc/cic/
|
||||
|
||||
|
||||
COPY config_template/ /usr/local/etc/cic/
|
||||
COPY requirements.txt .
|
||||
COPY override_requirements.txt .
|
||||
|
||||
ARG pip_index_url=https://pypi.org/simple
|
||||
ARG EXTRA_INDEX_URL="https://pip.grassrootseconomics.net:8433"
|
||||
ARG EXTRA_PIP_ARGS=""
|
||||
ARG GITLAB_PYTHON_REGISTRY="https://gitlab.com/api/v4/projects/27624814/packages/pypi/simple"
|
||||
ARG pip_trusted_host=pypi.org
|
||||
RUN pip install --index-url https://pypi.org/simple \
|
||||
pip install --index-url https://pypi.org/simple \
|
||||
--pre \
|
||||
--force-reinstall \
|
||||
--trusted-host $pip_trusted_host \
|
||||
--extra-index-url $GITLAB_PYTHON_REGISTRY --extra-index-url $EXTRA_INDEX_URL $EXTRA_PIP_ARGS \
|
||||
-r requirements.txt
|
||||
|
||||
RUN pip install --index-url https://pypi.org/simple \
|
||||
--force-reinstall \
|
||||
--pre \
|
||||
--trusted-host $pip_trusted_host \
|
||||
--extra-index-url $GITLAB_PYTHON_REGISTRY --extra-index-url $EXTRA_INDEX_URL $EXTRA_PIP_ARGS \
|
||||
-r override_requirements.txt
|
||||
|
||||
COPY . .
|
||||
RUN chmod +x *.sh
|
||||
@@ -56,6 +56,7 @@ ETH_PROVIDER
|
||||
ETH_ABI_DIR
|
||||
SIGNER_SOCKET_PATH
|
||||
SIGNER_SECRET
|
||||
SIGNER_PROVIDER
|
||||
CELERY_BROKER_URL
|
||||
CELERY_RESULT_URL
|
||||
META_PROVIDER
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
cic-eth[tools]==0.12.4a4
|
||||
chainlib-eth>=0.0.9a7,<0.1.0
|
||||
eth-erc20>=0.1.2a2,<0.2.0
|
||||
cic-eth[tools]==0.12.4a8
|
||||
chainlib-eth>=0.0.9rc4,<0.0.10
|
||||
chainlib==0.0.9rc1,<0.1.0
|
||||
eth-erc20>=0.1.2a3,<0.2.0
|
||||
erc20-demurrage-token>=0.0.5a2,<0.1.0
|
||||
eth-accounts-index>=0.1.2a2,<0.2.0
|
||||
eth-address-index>=0.2.3a4,<0.3.0
|
||||
#eth-accounts-index>=0.1.2a2,<0.2.0
|
||||
eth-address-index>=0.2.4a1,<0.3.0
|
||||
cic-eth-registry>=0.6.1a2,<0.7.0
|
||||
erc20-transfer-authorization>=0.3.5a2,<0.4.0
|
||||
erc20-faucet>=0.3.2a2,<0.4.0
|
||||
sarafu-faucet>=0.0.7a2,<0.1.0
|
||||
confini>=0.4.2rc3,<1.0.0
|
||||
crypto-dev-signer>=0.4.15rc2,<=0.4.15
|
||||
eth-token-index>=0.2.4a1,<=0.3.0
|
||||
okota>=0.2.4a5,<0.3.0
|
||||
|
||||
@@ -2,179 +2,129 @@
|
||||
|
||||
set -a
|
||||
|
||||
default_token=giftable_erc20_token
|
||||
TOKEN_SYMBOL=${CIC_DEFAULT_TOKEN_SYMBOL}
|
||||
TOKEN_NAME=${TOKEN_NAME}
|
||||
TOKEN_TYPE=${TOKEN_TYPE:-$default_token}
|
||||
cat <<EOF
|
||||
external token settings:
|
||||
token_type: $TOKEN_TYPE
|
||||
token_symbol: $TOKEN_SYMBOL
|
||||
token_name: $TOKEN_NAME
|
||||
token_decimals: $TOKEN_DECIMALS
|
||||
token_demurrage: $TOKEN_DEMURRAGE_LEVEL
|
||||
token_redistribution_period: $TOKEN_REDISTRIBUTION_PERIOD
|
||||
token_supply_limit: $TOKEN_SUPPLY_LIMIT
|
||||
EOF
|
||||
. ${DEV_DATA_DIR}/env_reset
|
||||
|
||||
CHAIN_SPEC=${CHAIN_SPEC:-$CIC_CHAIN_SPEC}
|
||||
RPC_HTTP_PROVIDER=${RPC_HTTP_PROVIDER:-$ETH_PROVIDER}
|
||||
|
||||
DEV_ETH_ACCOUNT_RESERVE_MINTER=${DEV_ETH_ACCOUNT_RESERVE_MINTER:-$DEV_ETH_ACCOUNT_CONTRACT_DEPLOYER}
|
||||
DEV_ETH_ACCOUNT_ACCOUNTS_INDEX_WRITER=${DEV_ETH_ACCOUNT_RESERVE_MINTER:-$DEV_ETH_ACCOUNT_CONTRACT_DEPLOYER}
|
||||
DEV_RESERVE_AMOUNT=${DEV_ETH_RESERVE_AMOUNT:-""10000000000000000000000000000000000}
|
||||
DEV_FAUCET_AMOUNT=${DEV_FAUCET_AMOUNT:-0}
|
||||
DEV_ETH_KEYSTORE_FILE=${DEV_ETH_KEYSTORE_FILE:-`realpath ./keystore/UTC--2021-01-08T17-18-44.521011372Z--eb3907ecad74a0013c259d5874ae7f22dcbcc95c`}
|
||||
WAIT_FOR_TIMEOUT=${WAIT_FOR_TIMEOUT:-60}
|
||||
|
||||
set -e
|
||||
|
||||
DEV_ETH_ACCOUNT_CONTRACT_DEPLOYER=`eth-checksum $(cat $DEV_ETH_KEYSTORE_FILE | jq -r .address)`
|
||||
|
||||
if [ ! -z $DEV_ETH_GAS_PRICE ]; then
|
||||
gas_price_arg="--gas-price $DEV_ETH_GAS_PRICE"
|
||||
fee_price_arg="--fee-price $DEV_ETH_GAS_PRICE"
|
||||
>&2 echo using static gas price $DEV_ETH_GAS_PRICE
|
||||
fi
|
||||
|
||||
echo "environment:"
|
||||
printenv
|
||||
echo \n
|
||||
|
||||
echo "using wallet address '$DEV_ETH_ACCOUNT_CONTRACT_DEPLOYER' from keystore file $DEV_ETH_KEYSTORE_FILE"
|
||||
|
||||
# This is a grassroots team convention for building the Bancor contracts using the bancor protocol repository truffle setup
|
||||
# Running this in docker-internal dev container (built from Docker folder in this repo) will write a
|
||||
# source-able env file to CIC_DATA_DIR. Services dependent on these contracts can mount this file OR
|
||||
# define these parameters at runtime
|
||||
# pushd /usr/src
|
||||
|
||||
if [ -z $CIC_DATA_DIR ]; then
|
||||
CIC_DATA_DIR=`mktemp -d`
|
||||
fi
|
||||
>&2 echo using data dir $CIC_DATA_DIR
|
||||
|
||||
init_level_file=${CIC_DATA_DIR}/.init
|
||||
if [ ! -f ${CIC_DATA_DIR}/.init ]; then
|
||||
echo "Creating .init file..."
|
||||
mkdir -p $CIC_DATA_DIR
|
||||
touch $CIC_DATA_DIR/.init
|
||||
# touch $init_level_file
|
||||
fi
|
||||
echo -n 1 > $init_level_file
|
||||
|
||||
# Abort on any error (including if wait-for-it fails).
|
||||
|
||||
# Wait for the backend to be up, if we know where it is.
|
||||
if [[ -n "${ETH_PROVIDER}" ]]; then
|
||||
|
||||
export CONFINI_DIR=$_CONFINI_DIR
|
||||
unset CONFINI_DIR
|
||||
|
||||
if [ ! -z "$DEV_USE_DOCKER_WAIT_SCRIPT" ]; then
|
||||
echo "waiting for ${ETH_PROVIDER}..."
|
||||
./wait-for-it.sh "${ETH_PROVIDER_HOST}:${ETH_PROVIDER_PORT}"
|
||||
fi
|
||||
|
||||
if [ "$TOKEN_TYPE" == "$default_token" ]; then
|
||||
if [ -z "$TOKEN_SYMBOL" ]; then
|
||||
>&2 echo token symbol not set, setting defaults for type $TOKEN_TYPE
|
||||
TOKEN_SYMBOL="GFT"
|
||||
TOKEN_NAME="Giftable Token"
|
||||
elif [ -z "$TOKEN_NAME" ]; then
|
||||
>&2 echo token name not set, setting same as symbol for type $TOKEN_TYPE
|
||||
TOKEN_NAME=$TOKEN_SYMBOL
|
||||
fi
|
||||
>&2 echo deploying default token $TOKEN_TYPE
|
||||
echo giftable-token-deploy $fee_price_arg -p $ETH_PROVIDER -y $DEV_ETH_KEYSTORE_FILE -i $CIC_CHAIN_SPEC -vv -s -ww --name "$TOKEN_NAME" --symbol $TOKEN_SYMBOL --decimals 6 -vv
|
||||
DEV_RESERVE_ADDRESS=`giftable-token-deploy $fee_price_arg -p $ETH_PROVIDER -y $DEV_ETH_KEYSTORE_FILE -i $CIC_CHAIN_SPEC -vv -s -ww --name "$TOKEN_NAME" --symbol $TOKEN_SYMBOL --decimals 6 -vv`
|
||||
elif [ "$TOKEN_TYPE" == "erc20_demurrage_token" ]; then
|
||||
if [ -z "$TOKEN_SYMBOL" ]; then
|
||||
>&2 echo token symbol not set, setting defaults for type $TOKEN_TYPE
|
||||
TOKEN_SYMBOL="SARAFU"
|
||||
TOKEN_NAME="Sarafu Token"
|
||||
elif [ -z "$TOKEN_NAME" ]; then
|
||||
>&2 echo token name not set, setting same as symbol for type $TOKEN_TYPE
|
||||
TOKEN_NAME=$TOKEN_SYMBOL
|
||||
fi
|
||||
>&2 echo deploying token $TOKEN_TYPE
|
||||
if [ -z $TOKEN_SINK_ADDRESS ]; then
|
||||
if [ ! -z $TOKEN_REDISTRIBUTION_PERIOD ]; then
|
||||
>&2 echo -e "\033[;93mtoken sink address not set, so redistribution will be BURNED\033[;39m"
|
||||
fi
|
||||
fi
|
||||
DEV_RESERVE_ADDRESS=`erc20-demurrage-token-deploy $fee_price_arg -p $ETH_PROVIDER -y $DEV_ETH_KEYSTORE_FILE -i $CIC_CHAIN_SPEC --name "$TOKEN_NAME" --symbol $TOKEN_SYMBOL -vv -ww -s`
|
||||
else
|
||||
>&2 echo unknown token type $TOKEN_TYPE
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "giftable-token-gift $fee_price_arg -p $ETH_PROVIDER -y $DEV_ETH_KEYSTORE_FILE -i $CIC_CHAIN_SPEC -vv -w -e $DEV_RESERVE_ADDRESS $DEV_RESERVE_AMOUNT"
|
||||
giftable-token-gift $fee_price_arg -p $ETH_PROVIDER -y $DEV_ETH_KEYSTORE_FILE -i $CIC_CHAIN_SPEC -u -vv -s -w -e $DEV_RESERVE_ADDRESS $DEV_RESERVE_AMOUNT
|
||||
|
||||
>&2 echo "deploy account index contract"
|
||||
DEV_ACCOUNT_INDEX_ADDRESS=`eth-accounts-index-deploy $fee_price_arg -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -y $DEV_ETH_KEYSTORE_FILE -vv -s -u -w`
|
||||
>&2 echo "add deployer address as account index writer"
|
||||
eth-accounts-index-writer $fee_price_arg -s -u -y $DEV_ETH_KEYSTORE_FILE -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -e $DEV_ACCOUNT_INDEX_ADDRESS -ww -vv $debug $DEV_ETH_ACCOUNT_CONTRACT_DEPLOYER
|
||||
|
||||
>&2 echo "deploy contract registry contract"
|
||||
CIC_REGISTRY_ADDRESS=`eth-contract-registry-deploy $fee_price_arg -i $CIC_CHAIN_SPEC -y $DEV_ETH_KEYSTORE_FILE --identifier AccountRegistry --identifier TokenRegistry --identifier AddressDeclarator --identifier Faucet --identifier TransferAuthorization --identifier ContractRegistry -p $ETH_PROVIDER -vv -s -u -w`
|
||||
eth-contract-registry-set $fee_price_arg -s -u -w -y $DEV_ETH_KEYSTORE_FILE -e $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -vv --identifier ContractRegistry $CIC_REGISTRY_ADDRESS
|
||||
eth-contract-registry-set $fee_price_arg -s -u -w -y $DEV_ETH_KEYSTORE_FILE -e $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -vv --identifier AccountRegistry $DEV_ACCOUNT_INDEX_ADDRESS
|
||||
|
||||
# Deploy address declarator registry
|
||||
>&2 echo "deploy address declarator contract"
|
||||
declarator_description=0x546869732069732074686520434943206e6574776f726b000000000000000000
|
||||
DEV_DECLARATOR_ADDRESS=`eth-address-declarator-deploy -s -u -y $DEV_ETH_KEYSTORE_FILE -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -w -vv $declarator_description`
|
||||
eth-contract-registry-set $fee_price_arg -s -u -w -y $DEV_ETH_KEYSTORE_FILE -e $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -vv --identifier AddressDeclarator $DEV_DECLARATOR_ADDRESS
|
||||
|
||||
# Deploy transfer authorization contact
|
||||
>&2 echo "deploy transfer auth contract"
|
||||
DEV_TRANSFER_AUTHORIZATION_ADDRESS=`erc20-transfer-auth-deploy $gas_price_arg -y $DEV_ETH_KEYSTORE_FILE -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -w -vv`
|
||||
eth-contract-registry-set $fee_price_arg -s -u -w -y $DEV_ETH_KEYSTORE_FILE -e $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -vv --identifier TransferAuthorization $DEV_TRANSFER_AUTHORIZATION_ADDRESS
|
||||
|
||||
# Deploy token index contract
|
||||
>&2 echo "deploy token index contract"
|
||||
DEV_TOKEN_INDEX_ADDRESS=`eth-token-index-deploy -s -u $fee_price_arg -y $DEV_ETH_KEYSTORE_FILE -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -w -vv`
|
||||
eth-contract-registry-set $fee_price_arg -s -u -w -y $DEV_ETH_KEYSTORE_FILE -e $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -vv --identifier TokenRegistry $DEV_TOKEN_INDEX_ADDRESS
|
||||
>&2 echo "add reserve token to token index"
|
||||
eth-token-index-add $fee_price_arg -s -u -w -y $DEV_ETH_KEYSTORE_FILE -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -vv -e $DEV_TOKEN_INDEX_ADDRESS $DEV_RESERVE_ADDRESS
|
||||
|
||||
# Sarafu faucet contract
|
||||
>&2 echo "deploy token faucet contract"
|
||||
DEV_FAUCET_ADDRESS=`sarafu-faucet-deploy $fee_price_arg -y $DEV_ETH_KEYSTORE_FILE -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -w -vv --account-index-address $DEV_ACCOUNT_INDEX_ADDRESS $DEV_RESERVE_ADDRESS -s`
|
||||
|
||||
>&2 echo "set token faucet amount"
|
||||
sarafu-faucet-set $fee_price_arg -y $DEV_ETH_KEYSTORE_FILE -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -e $DEV_FAUCET_ADDRESS -vv -s --fee-limit 100000 $DEV_FAUCET_AMOUNT
|
||||
|
||||
>&2 echo "register faucet in registry"
|
||||
eth-contract-registry-set -s -u $fee_price_arg -w -y $DEV_ETH_KEYSTORE_FILE -e $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -vv --identifier Faucet $DEV_FAUCET_ADDRESS
|
||||
|
||||
>&2 echo "set faucet as token minter"
|
||||
giftable-token-minter -s -u $fee_price_arg -w -y $DEV_ETH_KEYSTORE_FILE -e $DEV_RESERVE_ADDRESS -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -vv $DEV_FAUCET_ADDRESS
|
||||
|
||||
export CONFINI_DIR=$_CONFINI_DIR
|
||||
else
|
||||
echo "\$ETH_PROVIDER not set!"
|
||||
if [ -z "${RPC_PROVIDER}" ]; then
|
||||
echo "\$RPC_PROVIDER not set!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p $CIC_DATA_DIR
|
||||
>&2 echo using data dir $CIC_DATA_DIR for environment variable dump
|
||||
unset CONFINI_DIR
|
||||
|
||||
# this is consumed in downstream services to set environment variables
|
||||
cat << EOF > $CIC_DATA_DIR/.env
|
||||
export CIC_REGISTRY_ADDRESS=$CIC_REGISTRY_ADDRESS
|
||||
export CIC_TRUST_ADDRESS=$DEV_ETH_ACCOUNT_CONTRACT_DEPLOYER
|
||||
export CIC_DECLARATOR_ADDRESS=$CIC_DECLARATOR_ADDRESS
|
||||
EOF
|
||||
if [ ! -z "$DEV_USE_DOCKER_WAIT_SCRIPT" ]; then
|
||||
IFS=: read -a p <<< "$RPC_PROVIDER"
|
||||
read -i "/" rpc_provider_port <<< "${p[2]}"
|
||||
rpc_provider_host=${p[1]:2}
|
||||
echo "waiting for provider host $rpc_provider_host port $rpc_provider_port..."
|
||||
./wait-for-it.sh "$rpc_provider_host:$rpc_provider_port" -t $WAIT_FOR_TIMEOUT
|
||||
fi
|
||||
|
||||
cat ./envlist | bash from_env.sh > $CIC_DATA_DIR/.env_all
|
||||
cat ./envlist
|
||||
# popd
|
||||
if [ "$TOKEN_TYPE" == "giftable_erc20_token" ]; then
|
||||
if [ -z "$TOKEN_SYMBOL" ]; then
|
||||
>&2 echo token symbol not set, setting defaults for type $TOKEN_TYPE
|
||||
TOKEN_SYMBOL="GFT"
|
||||
TOKEN_NAME="Giftable Token"
|
||||
elif [ -z "$TOKEN_NAME" ]; then
|
||||
>&2 echo token name not set, setting same as symbol for type $TOKEN_TYPE
|
||||
TOKEN_NAME=$TOKEN_SYMBOL
|
||||
fi
|
||||
>&2 echo deploying default token $TOKEN_TYPE
|
||||
echo giftable-token-deploy $fee_price_arg -p $RPC_PROVIDER -y $WALLET_KEY_FILE -i $CIC_CHAIN_SPEC -vv -s -ww --name "$TOKEN_NAME" --symbol $TOKEN_SYMBOL --decimals 6 -vv
|
||||
DEV_RESERVE_ADDRESS=`giftable-token-deploy $fee_price_arg -p $RPC_PROVIDER -y $WALLET_KEY_FILE -i $CIC_CHAIN_SPEC -vv -s -ww --name "$TOKEN_NAME" --symbol $TOKEN_SYMBOL --decimals 6 -vv`
|
||||
elif [ "$TOKEN_TYPE" == "erc20_demurrage_token" ]; then
|
||||
if [ -z "$TOKEN_SYMBOL" ]; then
|
||||
>&2 echo token symbol not set, setting defaults for type $TOKEN_TYPE
|
||||
TOKEN_SYMBOL="DET"
|
||||
TOKEN_NAME="Demurrage Token"
|
||||
elif [ -z "$TOKEN_NAME" ]; then
|
||||
>&2 echo token name not set, setting same as symbol for type $TOKEN_TYPE
|
||||
TOKEN_NAME=$TOKEN_SYMBOL
|
||||
fi
|
||||
>&2 echo deploying token $TOKEN_TYPE
|
||||
if [ -z $TOKEN_SINK_ADDRESS ]; then
|
||||
if [ ! -z $TOKEN_REDISTRIBUTION_PERIOD ]; then
|
||||
>&2 echo -e "\033[;93mtoken sink address not set, so redistribution will be BURNED\033[;39m"
|
||||
fi
|
||||
fi
|
||||
DEV_RESERVE_ADDRESS=`erc20-demurrage-token-deploy $fee_price_arg -p $RPC_PROVIDER -y $WALLET_KEY_FILE -i $CIC_CHAIN_SPEC --name "$TOKEN_NAME" --symbol $TOKEN_SYMBOL -vv -ww -s`
|
||||
else
|
||||
>&2 echo unknown token type $TOKEN_TYPE
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "giftable-token-gift $fee_price_arg -p $RPC_PROVIDER -y $WALLET_KEY_FILE -i $CIC_CHAIN_SPEC -vv -w -e $DEV_RESERVE_ADDRESS $DEV_RESERVE_AMOUNT"
|
||||
giftable-token-gift $fee_price_arg -p $RPC_PROVIDER -y $WALLET_KEY_FILE -i $CIC_CHAIN_SPEC -u -vv -s -w -e $DEV_RESERVE_ADDRESS $DEV_RESERVE_AMOUNT
|
||||
|
||||
# Deploy address declarator registry
|
||||
>&2 echo "deploy address declarator contract"
|
||||
declarator_description=0x546869732069732074686520434943206e6574776f726b000000000000000000
|
||||
DEV_DECLARATOR_ADDRESS=`eth-address-declarator-deploy -s -u -y $WALLET_KEY_FILE -i $CIC_CHAIN_SPEC -p $RPC_PROVIDER -w -vv $declarator_description`
|
||||
|
||||
>&2 echo "deploy contract registry contract"
|
||||
#CIC_REGISTRY_ADDRESS=`eth-contract-registry-deploy $fee_price_arg -i $CIC_CHAIN_SPEC -y $WALLET_KEY_FILE --identifier AccountRegistry --identifier TokenRegistry --identifier AddressDeclarator --identifier Faucet --identifier TransferAuthorization --identifier ContractRegistry -p $RPC_PROVIDER -vv -s -u -w`
|
||||
CIC_REGISTRY_ADDRESS=`okota-contract-registry-deploy $fee_price_arg -i $CIC_CHAIN_SPEC -y $WALLET_KEY_FILE --identifier AccountRegistry --identifier TokenRegistry --identifier AddressDeclarator --identifier Faucet --identifier TransferAuthorization --identifier ContractRegistry --address-declarator $DEV_DECLARATOR_ADDRESS -p $RPC_PROVIDER -vv -s -u -w`
|
||||
eth-contract-registry-set $fee_price_arg -s -u -w -y $WALLET_KEY_FILE -e $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -p $RPC_PROVIDER -vv --identifier ContractRegistry $CIC_REGISTRY_ADDRESS
|
||||
|
||||
eth-contract-registry-set $fee_price_arg -s -u -w -y $WALLET_KEY_FILE -e $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -p $RPC_PROVIDER -vv --identifier AddressDeclarator $DEV_DECLARATOR_ADDRESS
|
||||
|
||||
>&2 echo "deploy account index contract"
|
||||
#DEV_ACCOUNT_INDEX_ADDRESS=`eth-accounts-index-deploy $fee_price_arg -i $CIC_CHAIN_SPEC -p $RPC_PROVIDER -y $WALLET_KEY_FILE -vv -s -u -w`
|
||||
DEV_ACCOUNT_INDEX_ADDRESS=`okota-accounts-index-deploy $fee_price_arg -i $CIC_CHAIN_SPEC -p $RPC_PROVIDER -y $WALLET_KEY_FILE -vv -s -u -w --address-declarator $DEV_DECLARATOR_ADDRESS --token-address $DEV_RESERVE_ADDRESS`
|
||||
#>&2 echo "add deployer address as account index writer"
|
||||
#eth-accounts-index-writer $fee_price_arg -s -u -y $WALLET_KEY_FILE -i $CIC_CHAIN_SPEC -p $RPC_PROVIDER -e $DEV_ACCOUNT_INDEX_ADDRESS -ww -vv $debug $DEV_ETH_ACCOUNT_CONTRACT_DEPLOYER
|
||||
eth-contract-registry-set $fee_price_arg -s -u -w -y $WALLET_KEY_FILE -e $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -p $RPC_PROVIDER -vv --identifier AccountRegistry $DEV_ACCOUNT_INDEX_ADDRESS
|
||||
|
||||
# Deploy transfer authorization contact
|
||||
>&2 echo "deploy transfer auth contract"
|
||||
DEV_TRANSFER_AUTHORIZATION_ADDRESS=`erc20-transfer-auth-deploy $gas_price_arg -y $WALLET_KEY_FILE -i $CIC_CHAIN_SPEC -p $RPC_PROVIDER -w -vv`
|
||||
eth-contract-registry-set $fee_price_arg -s -u -w -y $WALLET_KEY_FILE -e $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -p $RPC_PROVIDER -vv --identifier TransferAuthorization $DEV_TRANSFER_AUTHORIZATION_ADDRESS
|
||||
|
||||
# Deploy token index contract
|
||||
>&2 echo "deploy token index contract"
|
||||
#DEV_TOKEN_INDEX_ADDRESS=`eth-token-index-deploy -s -u $fee_price_arg -y $WALLET_KEY_FILE -i $CIC_CHAIN_SPEC -p $RPC_PROVIDER -w -vv`
|
||||
DEV_TOKEN_INDEX_ADDRESS=`okota-token-index-deploy -s -u $fee_price_arg -y $WALLET_KEY_FILE -i $CIC_CHAIN_SPEC -p $RPC_PROVIDER -w -vv --address-declarator $DEV_DECLARATOR_ADDRESS`
|
||||
eth-contract-registry-set $fee_price_arg -s -u -w -y $WALLET_KEY_FILE -e $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -p $RPC_PROVIDER -vv --identifier TokenRegistry $DEV_TOKEN_INDEX_ADDRESS
|
||||
>&2 echo "add reserve token to token index"
|
||||
eth-token-index-add $fee_price_arg -s -u -w -y $WALLET_KEY_FILE -i $CIC_CHAIN_SPEC -p $RPC_PROVIDER -vv -e $DEV_TOKEN_INDEX_ADDRESS $DEV_RESERVE_ADDRESS
|
||||
|
||||
# Sarafu faucet contract
|
||||
>&2 echo "deploy token faucet contract"
|
||||
DEV_FAUCET_ADDRESS=`sarafu-faucet-deploy $fee_price_arg -y $WALLET_KEY_FILE -i $CIC_CHAIN_SPEC -p $RPC_PROVIDER -w -vv --account-index-address $DEV_ACCOUNT_INDEX_ADDRESS $DEV_RESERVE_ADDRESS -s`
|
||||
|
||||
>&2 echo "set token faucet amount"
|
||||
sarafu-faucet-set $fee_price_arg -w -y $WALLET_KEY_FILE -i $CIC_CHAIN_SPEC -p $RPC_PROVIDER -e $DEV_FAUCET_ADDRESS -vv -s --fee-limit 100000 $DEV_FAUCET_AMOUNT
|
||||
|
||||
>&2 echo "register faucet in registry"
|
||||
eth-contract-registry-set -s -u $fee_price_arg -w -y $WALLET_KEY_FILE -e $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -p $RPC_PROVIDER -vv --identifier Faucet $DEV_FAUCET_ADDRESS
|
||||
|
||||
>&2 echo "set faucet as token minter"
|
||||
giftable-token-minter -s -u $fee_price_arg -w -y $WALLET_KEY_FILE -e $DEV_RESERVE_ADDRESS -i $CIC_CHAIN_SPEC -p $RPC_PROVIDER -vv $DEV_FAUCET_ADDRESS
|
||||
|
||||
|
||||
#echo "export CIC_DEFAULT_TOKEN_SYMBOL=$TOKEN_SYMBOL" >> ${DEV_DATA_DIR}/env_reset
|
||||
export CIC_DEFAULT_TOKEN_SYMBOL=$TOKEN_SYMBOL
|
||||
|
||||
echo "Writing env_reset file ..."
|
||||
|
||||
echo "export CIC_REGISTRY_ADDRESS=$CIC_REGISTRY_ADDRESS
|
||||
export CIC_DEFAULT_TOKEN_SYMBOL=$CIC_DEFAULT_TOKEN_SYMBOL
|
||||
export TOKEN_NAME=$TOKEN_NAME
|
||||
" >> "${DEV_DATA_DIR}"/env_reset
|
||||
|
||||
confini-dump --schema-dir ./config
|
||||
eth-contract-registry-list -i $CIC_CHAIN_SPEC -p $RPC_PROVIDER -e $CIC_REGISTRY_ADDRESS -u
|
||||
|
||||
set +a
|
||||
set +e
|
||||
|
||||
echo -n 2 > $init_level_file
|
||||
|
||||
exec "$@"
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
#! /bin/bash
|
||||
|
||||
>&2 echo -e "\033[;96mRUNNING\033[;39m configurations"
|
||||
./config.sh
|
||||
if [ $? -ne "0" ]; then
|
||||
>&2 echo -e "\033[;31mFAILED\033[;39m configurations"
|
||||
exit 1;
|
||||
fi
|
||||
>&2 echo -e "\033[;32mSUCCEEDED\033[;39m configurations"
|
||||
|
||||
if [[ $((RUN_MASK & 1)) -eq 1 ]]
|
||||
then
|
||||
>&2 echo -e "\033[;96mRUNNING\033[;39m RUN_MASK 1 - contract deployment"
|
||||
|
||||
@@ -1,68 +1,40 @@
|
||||
#!/bin/bash
|
||||
|
||||
# defaults
|
||||
#initlevel=`cat ${CIC_DATA_DIR}/.init`
|
||||
#echo $inilevel
|
||||
#if [ $initlevel -lt 2 ]; then
|
||||
# >&2 echo "initlevel too low $initlevel"
|
||||
# exit 1
|
||||
#fi
|
||||
source ${CIC_DATA_DIR}/.env
|
||||
source ${CIC_DATA_DIR}/.env_all
|
||||
DEV_PIP_EXTRA_INDEX_URL=${DEV_PIP_EXTRA_INDEX_URL:-https://pip.grassrootseconomics.net:8433}
|
||||
DEV_DATABASE_NAME_CIC_ETH=${DEV_DATABASE_NAME_CIC_ETH:-"cic-eth"}
|
||||
CIC_DATA_DIR=${CIC_DATA_DIR:-/tmp/cic}
|
||||
ETH_PASSPHRASE=''
|
||||
CIC_DEFAULT_TOKEN_SYMBOL=${CIC_DEFAULT_TOKEN_SYMBOL:-GFT}
|
||||
TOKEN_SYMBOL=$CIC_DEFAULT_TOKEN_SYMBOL
|
||||
|
||||
CHAIN_SPEC=${CHAIN_SPEC:-$CIC_CHAIN_SPEC}
|
||||
RPC_HTTP_PROVIDER=${RPC_HTTP_PROVIDER:-$ETH_PROVIDER}
|
||||
source ${DEV_DATA_DIR}/env_reset
|
||||
cat ${DEV_DATA_DIR}/env_reset
|
||||
|
||||
# Debug flag
|
||||
DEV_ETH_ACCOUNT_CONTRACT_DEPLOYER=0xEb3907eCad74a0013c259D5874AE7f22DcBcC95C
|
||||
keystore_file=./keystore/UTC--2021-01-08T17-18-44.521011372Z--eb3907ecad74a0013c259d5874ae7f22dcbcc95c
|
||||
debug='-vv'
|
||||
gas_amount=100000000000000000000000
|
||||
token_amount=${gas_amount}
|
||||
env_out_file=${CIC_DATA_DIR}/.env_seed
|
||||
init_level_file=${CIC_DATA_DIR}/.init
|
||||
empty_config_dir=$CONFINI_DIR/empty
|
||||
truncate $env_out_file -s 0
|
||||
|
||||
set -e
|
||||
set -a
|
||||
|
||||
#pip install --extra-index-url $DEV_PIP_EXTRA_INDEX_URL eth-address-index==0.1.1a7
|
||||
|
||||
export CONFINI_DIR=$_CONFINI_DIR
|
||||
unset CONFINI_DIR
|
||||
|
||||
# get required addresses from registries
|
||||
DEV_TOKEN_INDEX_ADDRESS=`eth-contract-registry-list -u -i $CHAIN_SPEC -p $ETH_PROVIDER -e $CIC_REGISTRY_ADDRESS -vv --raw TokenRegistry`
|
||||
DEV_ACCOUNT_INDEX_ADDRESS=`eth-contract-registry-list -u -i $CHAIN_SPEC -p $ETH_PROVIDER -e $CIC_REGISTRY_ADDRESS -vv --raw AccountRegistry`
|
||||
DEV_RESERVE_ADDRESS=`eth-token-index-list -i $CHAIN_SPEC -u -p $ETH_PROVIDER -e $DEV_TOKEN_INDEX_ADDRESS -vv --raw $CIC_DEFAULT_TOKEN_SYMBOL`
|
||||
cat <<EOF
|
||||
Token registry: $DEV_TOKEN_INDEX_ADDRESS
|
||||
Account reigstry: $DEV_ACCOUNT_INDEX_ADDRESS
|
||||
Reserve address: $DEV_RESERVE_ADDRESS ($CIC_DEFAULT_TOKEN_SYMBOL)
|
||||
EOF
|
||||
token_index_address=`eth-contract-registry-list -u -i $CHAIN_SPEC -p $RPC_PROVIDER -e $CIC_REGISTRY_ADDRESS -vv --raw TokenRegistry`
|
||||
account_index_address=`eth-contract-registry-list -u -i $CHAIN_SPEC -p $RPC_PROVIDER -e $CIC_REGISTRY_ADDRESS -vv --raw AccountRegistry`
|
||||
reserve_address=`eth-token-index-list -i $CHAIN_SPEC -u -p $RPC_PROVIDER -e $token_index_address -vv --raw $CIC_DEFAULT_TOKEN_SYMBOL`
|
||||
|
||||
>&2 echo "Token registry: $token_index_address"
|
||||
>&2 echo "Account registry: $account_index_address"
|
||||
>&2 echo "Reserve address: $reserve_address ($TOKEN_SYMBOL)"
|
||||
|
||||
>&2 echo "create account for gas gifter"
|
||||
old_gas_provider=$DEV_ETH_ACCOUNT_GAS_PROVIDER
|
||||
DEV_ETH_ACCOUNT_GAS_GIFTER=`CONFINI_DIR=$empty_config_dir cic-eth-create --redis-timeout 120 $debug --redis-host $REDIS_HOST --redis-host-callback=$REDIS_HOST --redis-port-callback=$REDIS_PORT --no-register`
|
||||
echo DEV_ETH_ACCOUNT_GAS_GIFTER=$DEV_ETH_ACCOUNT_GAS_GIFTER >> $env_out_file
|
||||
#DEV_ETH_ACCOUNT_GAS_GIFTER=`CONFINI_DIR=$empty_config_dir cic-eth-create --redis-timeout 120 $debug --redis-host $REDIS_HOST --redis-host-callback=$REDIS_HOST --redis-port-callback=$REDIS_PORT --no-register`
|
||||
DEV_ETH_ACCOUNT_GAS_GIFTER=`cic-eth-create --redis-timeout 120 $debug --redis-host $REDIS_HOST --redis-host-callback=$REDIS_HOST --redis-port-callback=$REDIS_PORT --no-register`
|
||||
cic-eth-tag -i $CHAIN_SPEC GAS_GIFTER $DEV_ETH_ACCOUNT_GAS_GIFTER
|
||||
|
||||
|
||||
>&2 echo "create account for sarafu gifter"
|
||||
DEV_ETH_ACCOUNT_SARAFU_GIFTER=`CONFINI_DIR=$empty_config_dir cic-eth-create $debug --redis-host $REDIS_HOST --redis-host-callback=$REDIS_HOST --redis-port-callback=$REDIS_PORT --no-register`
|
||||
echo DEV_ETH_ACCOUNT_SARAFU_GIFTER=$DEV_ETH_ACCOUNT_SARAFU_GIFTER >> $env_out_file
|
||||
cic-eth-tag -i $CHAIN_SPEC SARAFU_GIFTER $DEV_ETH_ACCOUNT_SARAFU_GIFTER
|
||||
|
||||
>&2 echo "create account for approval escrow owner"
|
||||
DEV_ETH_ACCOUNT_TRANSFER_AUTHORIZATION_OWNER=`CONFINI_DIR=$empty_config_dir cic-eth-create $debug --redis-host $REDIS_HOST --redis-host-callback=$REDIS_HOST --redis-port-callback=$REDIS_PORT --no-register`
|
||||
echo DEV_ETH_ACCOUNT_TRANSFER_AUTHORIZATION_OWNER=$DEV_ETH_ACCOUNT_TRANSFER_AUTHORIZATION_OWNER >> $env_out_file
|
||||
cic-eth-tag -i $CHAIN_SPEC TRANSFER_AUTHORIZATION_OWNER $DEV_ETH_ACCOUNT_TRANSFER_AUTHORIZATION_OWNER
|
||||
|
||||
#>&2 echo "create account for faucet owner"
|
||||
@@ -72,50 +44,45 @@ cic-eth-tag -i $CHAIN_SPEC TRANSFER_AUTHORIZATION_OWNER $DEV_ETH_ACCOUNT_TRANSFE
|
||||
|
||||
>&2 echo "create account for accounts index writer"
|
||||
DEV_ETH_ACCOUNT_ACCOUNT_REGISTRY_WRITER=`CONFINI_DIR=$empty_config_dir cic-eth-create $debug --redis-host $REDIS_HOST --redis-host-callback=$REDIS_HOST --redis-port-callback=$REDIS_PORT --no-register`
|
||||
echo DEV_ETH_ACCOUNT_ACCOUNT_REGISTRY_WRITER=$DEV_ETH_ACCOUNT_ACCOUNT_REGISTRY_WRITER >> $env_out_file
|
||||
cic-eth-tag -i $CHAIN_SPEC ACCOUNT_REGISTRY_WRITER $DEV_ETH_ACCOUNT_ACCOUNT_REGISTRY_WRITER
|
||||
>&2 echo "add acccounts index writer account as writer on contract"
|
||||
eth-accounts-index-writer -s -u -y $keystore_file -i $CHAIN_SPEC -p $ETH_PROVIDER -e $DEV_ACCOUNT_INDEX_ADDRESS -ww $debug $DEV_ETH_ACCOUNT_ACCOUNT_REGISTRY_WRITER
|
||||
#eth-accounts-index-writer -s -u -y $WALLET_KEY_FILE -i $CHAIN_SPEC -p $RPC_PROVIDER -e $account_index_address -ww $debug $DEV_ETH_ACCOUNT_ACCOUNT_REGISTRY_WRITER
|
||||
|
||||
# Transfer gas to custodial gas provider adddress
|
||||
_CONFINI_DIR=$CONFINI_DIR
|
||||
unset CONFINI_DIR
|
||||
>&2 echo gift gas to gas gifter
|
||||
>&2 eth-gas -s -u -y $keystore_file -i $CHAIN_SPEC -p $ETH_PROVIDER -w $debug -a $DEV_ETH_ACCOUNT_GAS_GIFTER $gas_amount
|
||||
>&2 eth-gas -s -u -y $WALLET_KEY_FILE -i $CHAIN_SPEC -p $RPC_PROVIDER -w $debug -a $DEV_ETH_ACCOUNT_GAS_GIFTER $DEV_GAS_AMOUNT
|
||||
|
||||
>&2 echo gift gas to sarafu token owner
|
||||
>&2 eth-gas -s -u -y $keystore_file -i $CHAIN_SPEC -p $ETH_PROVIDER -w $debug -a $DEV_ETH_ACCOUNT_SARAFU_GIFTER $gas_amount
|
||||
>&2 eth-gas -s -u -y $WALLET_KEY_FILE -i $CHAIN_SPEC -p $RPC_PROVIDER -w $debug -a $DEV_ETH_ACCOUNT_SARAFU_GIFTER $DEV_GAS_AMOUNT
|
||||
|
||||
>&2 echo gift gas to account index owner
|
||||
>&2 eth-gas -s -u -y $keystore_file -i $CHAIN_SPEC -p $ETH_PROVIDER -w $debug -a $DEV_ETH_ACCOUNT_ACCOUNT_REGISTRY_WRITER $gas_amount
|
||||
>&2 eth-gas -s -u -y $WALLET_KEY_FILE -i $CHAIN_SPEC -p $RPC_PROVIDER -w $debug -a $DEV_ETH_ACCOUNT_ACCOUNT_REGISTRY_WRITER $DEV_GAS_AMOUNT
|
||||
|
||||
|
||||
# Send token to token creator
|
||||
>&2 echo "gift tokens to sarafu owner"
|
||||
echo "giftable-token-gift -s -u -y $keystore_file -i $CHAIN_SPEC -p $ETH_PROVIDER -e $DEV_RESERVE_ADDRESS -a $DEV_ETH_ACCOUNT_SARAFU_GIFTER -w $debug $token_amount"
|
||||
>&2 giftable-token-gift -s -u -y $keystore_file -i $CHAIN_SPEC -p $ETH_PROVIDER -e $DEV_RESERVE_ADDRESS -a $DEV_ETH_ACCOUNT_SARAFU_GIFTER -w $debug $token_amount
|
||||
echo "giftable-token-gift -s -u -y $WALLET_KEY_FILE -i $CHAIN_SPEC -p $RPC_PROVIDER -e $reserve_address -a $DEV_ETH_ACCOUNT_SARAFU_GIFTER -w $debug $DEV_TOKEN_AMOUNT"
|
||||
>&2 giftable-token-gift -s -u -y $WALLET_KEY_FILE -i $CHAIN_SPEC -p $RPC_PROVIDER -e $reserve_address -a $DEV_ETH_ACCOUNT_SARAFU_GIFTER -w $debug $DEV_TOKEN_AMOUNT
|
||||
|
||||
# Send token to token gifter
|
||||
>&2 echo "gift tokens to keystore address"
|
||||
>&2 giftable-token-gift -s -u -y $keystore_file -i $CHAIN_SPEC -p $ETH_PROVIDER -e $DEV_RESERVE_ADDRESS -a $DEV_ETH_ACCOUNT_CONTRACT_DEPLOYER -w $debug $token_amount
|
||||
>&2 giftable-token-gift -s -u -y $WALLET_KEY_FILE -i $CHAIN_SPEC -p $RPC_PROVIDER -e $reserve_address -a $DEV_ETH_ACCOUNT_CONTRACT_DEPLOYER -w $debug $DEV_TOKEN_AMOUNT
|
||||
|
||||
>&2 echo "set sarafu token to reserve token (temporarily while bancor contracts are not connected)"
|
||||
echo DEV_ETH_SARAFU_TOKEN_ADDRESS=$DEV_ETH_RESERVE_ADDRESS >> $env_out_file
|
||||
export DEV_ETH_SARAFU_TOKEN_ADDRESS=$DEV_ETH_RESERVE_ADDRESS
|
||||
|
||||
# Transfer tokens to gifter address
|
||||
>&2 echo "transfer tokens to token gifter address"
|
||||
>&2 erc20-transfer -s -u -y $keystore_file -i $CHAIN_SPEC -p $ETH_PROVIDER --fee-limit 100000 -e $DEV_RESERVE_ADDRESS -w $debug -a $DEV_ETH_ACCOUNT_SARAFU_GIFTER ${token_amount:0:-1}
|
||||
>&2 erc20-transfer -s -u -y $WALLET_KEY_FILE -i $CHAIN_SPEC -p $RPC_PROVIDER --fee-limit 100000 -e $reserve_address -w $debug -a $DEV_ETH_ACCOUNT_SARAFU_GIFTER ${DEV_TOKEN_AMOUNT:0:-1}
|
||||
|
||||
#echo -n 0 > $init_level_file
|
||||
|
||||
#CONFINI_DIR=$_CONFINI_DIR
|
||||
# Remove the SEND (8), QUEUE (16) and INIT (2) locks (or'ed), set by default at migration
|
||||
cic-eth-ctl -i $CHAIN_SPEC unlock INIT
|
||||
cic-eth-ctl -i $CHAIN_SPEC unlock SEND
|
||||
cic-eth-ctl -i $CHAIN_SPEC unlock QUEUE
|
||||
cic-eth-ctl -vv -i $CHAIN_SPEC unlock INIT
|
||||
cic-eth-ctl -vv -i $CHAIN_SPEC unlock SEND
|
||||
cic-eth-ctl -vv -i $CHAIN_SPEC unlock QUEUE
|
||||
|
||||
export CONFINI_DIR=$_CONFINI_DIR
|
||||
#confini-dump --schema-module chainlib.eth.data.config --schema-module cic_eth.data.config --schema-dir ./config
|
||||
|
||||
set +a
|
||||
set +e
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
.cache
|
||||
.dot
|
||||
**/doc
|
||||
**/node_modules
|
||||
node_modules/
|
||||
**/venv
|
||||
**/.venv
|
||||
|
||||
|
||||
@@ -1,64 +1,61 @@
|
||||
# standard imports
|
||||
import argparse
|
||||
import logging
|
||||
import sys
|
||||
import os
|
||||
import sys
|
||||
|
||||
# external imports
|
||||
import celery
|
||||
import confini
|
||||
import redis
|
||||
from chainlib.chain import ChainSpec
|
||||
from chainlib.eth.address import to_checksum_address
|
||||
from chainlib.eth.connection import EthHTTPConnection
|
||||
from confini import Config
|
||||
from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer
|
||||
from crypto_dev_signer.keystore.dict import DictKeystore
|
||||
|
||||
# local imports
|
||||
from import_task import ImportTask, MetadataTask
|
||||
from import_util import BalanceProcessor, get_celery_worker_status
|
||||
from import_task import ImportTask, MetadataTask
|
||||
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
default_config_dir = '/usr/local/etc/data-seeding/'
|
||||
logg = logging.getLogger()
|
||||
|
||||
config_dir = './config'
|
||||
arg_parser = argparse.ArgumentParser(description='Daemon worker that handles data seeding tasks.')
|
||||
arg_parser.add_argument('-c', type=str, default=default_config_dir, help='config root to use.')
|
||||
arg_parser.add_argument('--env-prefix',
|
||||
default=os.environ.get('CONFINI_ENV_PREFIX'),
|
||||
dest='env_prefix',
|
||||
type=str,
|
||||
help='environment prefix for variables to overwrite configuration.')
|
||||
arg_parser.add_argument('--head', action='store_true', help='start at current block height (overrides --offset)')
|
||||
arg_parser.add_argument('-i', '--chain-spec', type=str, dest='i', help='chain spec')
|
||||
arg_parser.add_argument('--include-balances', dest='include_balances', help='include opening balance transactions',
|
||||
action='store_true')
|
||||
arg_parser.add_argument('--meta-host', dest='meta_host', type=str, help='metadata server host')
|
||||
arg_parser.add_argument('--meta-port', dest='meta_port', type=int, help='metadata server host')
|
||||
arg_parser.add_argument('-p', '--provider', dest='p', type=str, help='chain rpc provider address')
|
||||
arg_parser.add_argument('-q', type=str, default='cic-import-ussd', help='celery queue to submit data seeding tasks to.')
|
||||
arg_parser.add_argument('-r', '--registry-address', type=str, dest='r', help='CIC Registry address')
|
||||
arg_parser.add_argument('--redis-db', dest='redis_db', type=int, help='redis db to use for task submission and callback')
|
||||
arg_parser.add_argument('--redis-host', dest='redis_host', type=str, help='redis host to use for task submission')
|
||||
arg_parser.add_argument('--redis-port', dest='redis_port', type=int, help='redis host to use for task submission')
|
||||
arg_parser.add_argument('--token-symbol', default='GFT', type=str, dest='token_symbol',
|
||||
help='Token symbol to use for transactions')
|
||||
arg_parser.add_argument('-v', help='be verbose', action='store_true')
|
||||
arg_parser.add_argument('-vv', help='be more verbose', action='store_true')
|
||||
arg_parser.add_argument('-y', '--key-file', dest='y', type=str, help='Ethereum keystore file to use for signing')
|
||||
arg_parser.add_argument('--offset', type=int, default=0, help='block offset to start syncer from')
|
||||
arg_parser.add_argument('--old-chain-spec', type=str, dest='old_chain_spec', default='evm:oldchain:1',
|
||||
help='chain spec')
|
||||
arg_parser.add_argument('import_dir', default='out', type=str, help='user export directory')
|
||||
args = arg_parser.parse_args()
|
||||
|
||||
argparser = argparse.ArgumentParser(description='daemon that monitors transactions in new blocks')
|
||||
argparser.add_argument('-p', '--provider', dest='p', type=str, help='chain rpc provider address')
|
||||
argparser.add_argument('-y', '--key-file', dest='y', type=str, help='Ethereum keystore file to use for signing')
|
||||
argparser.add_argument('-c', type=str, default=config_dir, help='config root to use')
|
||||
argparser.add_argument('--old-chain-spec', type=str, dest='old_chain_spec', default='evm:oldchain:1', help='chain spec')
|
||||
argparser.add_argument('-i', '--chain-spec', type=str, dest='i', help='chain spec')
|
||||
argparser.add_argument('-r', '--registry-address', type=str, dest='r', help='CIC Registry address')
|
||||
argparser.add_argument('--meta-host', dest='meta_host', type=str, help='metadata server host')
|
||||
argparser.add_argument('--meta-port', dest='meta_port', type=int, help='metadata server host')
|
||||
argparser.add_argument('--redis-host', dest='redis_host', type=str, help='redis host to use for task submission')
|
||||
argparser.add_argument('--redis-port', dest='redis_port', type=int, help='redis host to use for task submission')
|
||||
argparser.add_argument('--redis-db', dest='redis_db', type=int, help='redis db to use for task submission and callback')
|
||||
argparser.add_argument('--token-symbol', default='GFT', type=str, dest='token_symbol',
|
||||
help='Token symbol to use for transactions')
|
||||
argparser.add_argument('--head', action='store_true', help='start at current block height (overrides --offset)')
|
||||
argparser.add_argument('--env-prefix', default=os.environ.get('CONFINI_ENV_PREFIX'), dest='env_prefix', type=str,
|
||||
help='environment prefix for variables to overwrite configuration')
|
||||
argparser.add_argument('-q', type=str, default='cic-import-ussd', help='celery queue to submit transaction tasks to')
|
||||
argparser.add_argument('--offset', type=int, default=0, help='block offset to start syncer from')
|
||||
argparser.add_argument('-v', help='be verbose', action='store_true')
|
||||
argparser.add_argument('-vv', help='be more verbose', action='store_true')
|
||||
argparser.add_argument('user_dir', default='out', type=str, help='user export directory')
|
||||
args = argparser.parse_args(sys.argv[1:])
|
||||
|
||||
if args.v:
|
||||
if args.vv:
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
elif args.v:
|
||||
logging.getLogger().setLevel(logging.INFO)
|
||||
|
||||
elif args.vv:
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
|
||||
config_dir = os.path.join(args.c)
|
||||
os.makedirs(config_dir, 0o777, True)
|
||||
config = confini.Config(config_dir, args.env_prefix)
|
||||
config = Config(args.c, args.env_prefix)
|
||||
config.process()
|
||||
|
||||
# override args
|
||||
args_override = {
|
||||
'CIC_CHAIN_SPEC': getattr(args, 'i'),
|
||||
'ETH_PROVIDER': getattr(args, 'p'),
|
||||
@@ -73,88 +70,76 @@ args_override = {
|
||||
config.dict_override(args_override, 'cli flag')
|
||||
config.censor('PASSWORD', 'DATABASE')
|
||||
config.censor('PASSWORD', 'SSL')
|
||||
logg.debug('config loaded from {}:\n{}'.format(config_dir, config))
|
||||
logg.debug(f'config loaded from {args.c}:\n{config}')
|
||||
|
||||
redis_host = config.get('REDIS_HOST')
|
||||
redis_port = config.get('REDIS_PORT')
|
||||
redis_db = config.get('REDIS_DB')
|
||||
r = redis.Redis(redis_host, redis_port, redis_db)
|
||||
db_config = {
|
||||
'database': config.get('DATABASE_NAME'),
|
||||
'host': config.get('DATABASE_HOST'),
|
||||
'port': config.get('DATABASE_PORT'),
|
||||
'user': config.get('DATABASE_USER'),
|
||||
'password': config.get('DATABASE_PASSWORD')
|
||||
}
|
||||
ImportTask.db_config = db_config
|
||||
|
||||
# create celery apps
|
||||
celery_app = celery.Celery(backend=config.get('CELERY_RESULT_URL'), broker=config.get('CELERY_BROKER_URL'))
|
||||
status = get_celery_worker_status(celery_app=celery_app)
|
||||
|
||||
signer_address = None
|
||||
keystore = DictKeystore()
|
||||
if args.y is not None:
|
||||
logg.debug('loading keystore file {}'.format(args.y))
|
||||
signer_address = keystore.import_keystore_file(args.y)
|
||||
logg.debug('now have key for signer address {}'.format(signer_address))
|
||||
|
||||
# define signer
|
||||
os.path.isfile(args.y)
|
||||
logg.debug(f'loading keystore file {args.y}')
|
||||
signer_address = keystore.import_keystore_file(args.y)
|
||||
logg.debug(f'now have key for signer address {signer_address}')
|
||||
signer = EIP155Signer(keystore)
|
||||
|
||||
queue = args.q
|
||||
chain_str = config.get('CIC_CHAIN_SPEC')
|
||||
block_offset = 0
|
||||
if args.head:
|
||||
block_offset = -1
|
||||
else:
|
||||
block_offset = args.offset
|
||||
block_offset = -1 if args.head else args.offset
|
||||
|
||||
chain_str = config.get('CIC_CHAIN_SPEC')
|
||||
chain_spec = ChainSpec.from_chain_str(chain_str)
|
||||
ImportTask.chain_spec = chain_spec
|
||||
old_chain_spec_str = args.old_chain_spec
|
||||
old_chain_spec = ChainSpec.from_chain_str(old_chain_spec_str)
|
||||
|
||||
user_dir = args.user_dir # user_out_dir from import_users.py
|
||||
|
||||
token_symbol = args.token_symbol
|
||||
|
||||
MetadataTask.meta_host = config.get('META_HOST')
|
||||
MetadataTask.meta_port = config.get('META_PORT')
|
||||
ImportTask.chain_spec = chain_spec
|
||||
|
||||
txs_dir = os.path.join(args.import_dir, 'txs')
|
||||
os.makedirs(txs_dir, exist_ok=True)
|
||||
sys.stdout.write(f'created txs dir: {txs_dir}')
|
||||
|
||||
celery_app = celery.Celery(broker=config.get('CELERY_BROKER_URL'), backend=config.get('CELERY_RESULT_URL'))
|
||||
get_celery_worker_status(celery_app)
|
||||
|
||||
|
||||
def main():
|
||||
conn = EthHTTPConnection(config.get('ETH_PROVIDER'))
|
||||
|
||||
ImportTask.balance_processor = BalanceProcessor(conn, chain_spec, config.get('CIC_REGISTRY_ADDRESS'),
|
||||
signer_address, signer)
|
||||
ImportTask.balance_processor.init(token_symbol)
|
||||
|
||||
# TODO get decimals from token
|
||||
ImportTask.balance_processor = BalanceProcessor(conn,
|
||||
chain_spec,
|
||||
config.get('CIC_REGISTRY_ADDRESS'),
|
||||
signer_address,
|
||||
signer)
|
||||
ImportTask.balance_processor.init(args.token_symbol)
|
||||
balances = {}
|
||||
f = open('{}/balances.csv'.format(user_dir, 'r'))
|
||||
remove_zeros = 10 ** 6
|
||||
i = 0
|
||||
while True:
|
||||
l = f.readline()
|
||||
if l is None:
|
||||
break
|
||||
r = l.split(',')
|
||||
try:
|
||||
address = to_checksum_address(r[0])
|
||||
sys.stdout.write('loading balance {} {} {}'.format(i, address, r[1]).ljust(200) + "\r")
|
||||
except ValueError:
|
||||
break
|
||||
balance = int(int(r[1].rstrip()) / remove_zeros)
|
||||
balances[address] = balance
|
||||
i += 1
|
||||
|
||||
f.close()
|
||||
|
||||
accuracy = 10 ** 6
|
||||
count = 0
|
||||
with open(f'{args.import_dir}/balances.csv', 'r') as balances_file:
|
||||
while True:
|
||||
line = balances_file.readline()
|
||||
if line is None:
|
||||
break
|
||||
balance_data = line.split(',')
|
||||
try:
|
||||
blockchain_address = to_checksum_address(balance_data[0])
|
||||
logg.info(
|
||||
'loading balance: {} {} {}'.format(count, blockchain_address, balance_data[1].ljust(200) + "\r"))
|
||||
except ValueError:
|
||||
break
|
||||
balance = int(int(balance_data[1].rstrip()) / accuracy)
|
||||
balances[blockchain_address] = balance
|
||||
count += 1
|
||||
ImportTask.balances = balances
|
||||
ImportTask.count = i
|
||||
ImportTask.import_dir = user_dir
|
||||
|
||||
s = celery.signature(
|
||||
'import_task.send_txs',
|
||||
[
|
||||
MetadataTask.balance_processor.nonce_offset,
|
||||
],
|
||||
queue=queue,
|
||||
)
|
||||
s.apply_async()
|
||||
ImportTask.count = count
|
||||
ImportTask.include_balances = args.include_balances is True
|
||||
ImportTask.import_dir = args.import_dir
|
||||
s_send_txs = celery.signature(
|
||||
'import_task.send_txs', [ImportTask.balance_processor.nonce_offset], queue=args.q)
|
||||
s_send_txs.apply_async()
|
||||
|
||||
argv = ['worker']
|
||||
if args.vv:
|
||||
@@ -165,6 +150,7 @@ def main():
|
||||
argv.append(args.q)
|
||||
argv.append('-n')
|
||||
argv.append(args.q)
|
||||
argv.append(f'--pidfile={args.q}.pid')
|
||||
celery_app.worker_main(argv)
|
||||
|
||||
|
||||
|
||||
@@ -1,71 +1,63 @@
|
||||
# standard import
|
||||
# standard imports
|
||||
import argparse
|
||||
import csv
|
||||
import logging
|
||||
import os
|
||||
import psycopg2
|
||||
|
||||
# third-party imports
|
||||
import celery
|
||||
import confini
|
||||
# external imports
|
||||
from confini import Config
|
||||
|
||||
# local imports
|
||||
from import_util import get_celery_worker_status
|
||||
|
||||
|
||||
default_config_dir = './config'
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
logg = logging.getLogger()
|
||||
|
||||
default_config_dir = './config'
|
||||
|
||||
arg_parser = argparse.ArgumentParser()
|
||||
arg_parser.add_argument('-c', type=str, default=default_config_dir, help='config root to use')
|
||||
arg_parser = argparse.ArgumentParser(description='Pins import script.')
|
||||
arg_parser.add_argument('-c', type=str, default=default_config_dir, help='config root to use.')
|
||||
arg_parser.add_argument('--env-prefix',
|
||||
default=os.environ.get('CONFINI_ENV_PREFIX'),
|
||||
dest='env_prefix',
|
||||
type=str,
|
||||
help='environment prefix for variables to overwrite configuration')
|
||||
arg_parser.add_argument('-q', type=str, default='cic-import-ussd', help='celery queue to submit transaction tasks to')
|
||||
help='environment prefix for variables to overwrite configuration.')
|
||||
arg_parser.add_argument('import_dir', default='out', type=str, help='user export directory')
|
||||
arg_parser.add_argument('-v', help='be verbose', action='store_true')
|
||||
arg_parser.add_argument('-vv', help='be more verbose', action='store_true')
|
||||
arg_parser.add_argument('pins_dir', default='out', type=str, help='user export directory')
|
||||
|
||||
args = arg_parser.parse_args()
|
||||
|
||||
# set log levels
|
||||
if args.v:
|
||||
logg.setLevel(logging.INFO)
|
||||
elif args.vv:
|
||||
logg.setLevel(logging.DEBUG)
|
||||
if args.vv:
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
elif args.v:
|
||||
logging.getLogger().setLevel(logging.INFO)
|
||||
|
||||
# process configs
|
||||
config_dir = args.c
|
||||
config = confini.Config(config_dir, os.environ.get('CONFINI_ENV_PREFIX'))
|
||||
config = Config(args.c, args.env_prefix)
|
||||
config.process()
|
||||
config.censor('PASSWORD', 'DATABASE')
|
||||
logg.debug('config loaded from {}:\n{}'.format(args.c, config))
|
||||
|
||||
celery_app = celery.Celery(broker=config.get('CELERY_BROKER_URL'), backend=config.get('CELERY_RESULT_URL'))
|
||||
status = get_celery_worker_status(celery_app=celery_app)
|
||||
|
||||
|
||||
db_configs = {
|
||||
'database': config.get('DATABASE_NAME'),
|
||||
'host': config.get('DATABASE_HOST'),
|
||||
'port': config.get('DATABASE_PORT'),
|
||||
'user': config.get('DATABASE_USER'),
|
||||
'password': config.get('DATABASE_PASSWORD')
|
||||
}
|
||||
logg.debug(f'config loaded from {args.c}:\n{config}')
|
||||
|
||||
|
||||
def main():
|
||||
with open(f'{args.pins_dir}/pins.csv') as pins_file:
|
||||
with open(f'{args.import_dir}/pins.csv') as pins_file:
|
||||
phone_to_pins = [tuple(row) for row in csv.reader(pins_file)]
|
||||
|
||||
s_import_pins = celery.signature(
|
||||
'import_task.set_pins',
|
||||
(db_configs, phone_to_pins),
|
||||
queue=args.q
|
||||
db_conn = psycopg2.connect(
|
||||
database=config.get('DATABASE_NAME'),
|
||||
host=config.get('DATABASE_HOST'),
|
||||
port=config.get('DATABASE_PORT'),
|
||||
user=config.get('DATABASE_USER'),
|
||||
password=config.get('DATABASE_PASSWORD')
|
||||
)
|
||||
result = s_import_pins.apply_async()
|
||||
logg.debug(f'TASK: {result.id}, STATUS: {result.status}')
|
||||
db_cursor = db_conn.cursor()
|
||||
sql = 'UPDATE account SET password_hash = %s WHERE phone_number = %s'
|
||||
for element in phone_to_pins:
|
||||
db_cursor.execute(sql, (element[1], element[0]))
|
||||
logg.debug(f'Updating account: {element[0]} with: {element[1]}')
|
||||
db_conn.commit()
|
||||
db_cursor.close()
|
||||
db_conn.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -1,38 +1,37 @@
|
||||
# standard imports
|
||||
import csv
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import random
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
import uuid
|
||||
from urllib import error, parse, request
|
||||
|
||||
# external imports
|
||||
import celery
|
||||
import psycopg2
|
||||
from celery import Task
|
||||
from chainlib.chain import ChainSpec
|
||||
from chainlib.eth.address import to_checksum_address
|
||||
from chainlib.eth.tx import (
|
||||
unpack,
|
||||
raw,
|
||||
)
|
||||
from cic_types.models.person import Person
|
||||
from cic_types.processor import generate_metadata_pointer
|
||||
from hexathon import (
|
||||
strip_0x,
|
||||
add_0x,
|
||||
)
|
||||
from chainlib.eth.tx import raw, unpack
|
||||
from cic_types.models.person import Person, generate_metadata_pointer
|
||||
from hexathon import add_0x, strip_0x
|
||||
|
||||
# local imports
|
||||
|
||||
logg = logging.getLogger()
|
||||
|
||||
celery_app = celery.current_app
|
||||
logg = logging.getLogger()
|
||||
|
||||
|
||||
class ImportTask(celery.Task):
|
||||
class ImportTask(Task):
|
||||
balances = None
|
||||
import_dir = 'out'
|
||||
count = 0
|
||||
chain_spec = None
|
||||
balance_processor = None
|
||||
chain_spec: ChainSpec = None
|
||||
count = 0
|
||||
db_config: dict = None
|
||||
import_dir = ''
|
||||
include_balances = False
|
||||
max_retries = None
|
||||
|
||||
|
||||
@@ -41,121 +40,70 @@ class MetadataTask(ImportTask):
|
||||
meta_port = None
|
||||
meta_path = ''
|
||||
meta_ssl = False
|
||||
autoretry_for = (
|
||||
urllib.error.HTTPError,
|
||||
OSError,
|
||||
)
|
||||
autoretry_for = (error.HTTPError, OSError,)
|
||||
retry_jitter = True
|
||||
retry_backoff = True
|
||||
retry_backoff_max = 60
|
||||
|
||||
@classmethod
|
||||
def meta_url(self):
|
||||
def meta_url(cls):
|
||||
scheme = 'http'
|
||||
if self.meta_ssl:
|
||||
if cls.meta_ssl:
|
||||
scheme += 's'
|
||||
url = urllib.parse.urlparse('{}://{}:{}/{}'.format(scheme, self.meta_host, self.meta_port, self.meta_path))
|
||||
return urllib.parse.urlunparse(url)
|
||||
url = parse.urlparse(f'{scheme}://{cls.meta_host}:{cls.meta_port}/{cls.meta_path}')
|
||||
return parse.urlunparse(url)
|
||||
|
||||
|
||||
def old_address_from_phone(base_path, phone):
|
||||
pidx = generate_metadata_pointer(phone.encode('utf-8'), ':cic.phone')
|
||||
phone_idx_path = os.path.join('{}/phone/{}/{}/{}'.format(
|
||||
base_path,
|
||||
pidx[:2],
|
||||
pidx[2:4],
|
||||
pidx,
|
||||
)
|
||||
)
|
||||
f = open(phone_idx_path, 'r')
|
||||
old_address = f.read()
|
||||
f.close()
|
||||
|
||||
def old_address_from_phone(base_path: str, phone_number: str):
|
||||
pid_x = generate_metadata_pointer(phone_number.encode('utf-8'), ':cic.phone')
|
||||
phone_idx_path = os.path.join(f'{base_path}/phone/{pid_x[:2]}/{pid_x[2:4]}/{pid_x}')
|
||||
with open(phone_idx_path, 'r') as f:
|
||||
old_address = f.read()
|
||||
return old_address
|
||||
|
||||
|
||||
@celery_app.task(bind=True, base=MetadataTask)
|
||||
def resolve_phone(self, phone):
|
||||
identifier = generate_metadata_pointer(phone.encode('utf-8'), ':cic.phone')
|
||||
url = urllib.parse.urljoin(self.meta_url(), identifier)
|
||||
logg.debug('attempt getting phone pointer at {} for phone {}'.format(url, phone))
|
||||
r = urllib.request.urlopen(url)
|
||||
address = json.load(r)
|
||||
address = address.replace('"', '')
|
||||
logg.debug('address {} for phone {}'.format(address, phone))
|
||||
|
||||
return address
|
||||
|
||||
|
||||
@celery_app.task(bind=True, base=MetadataTask)
|
||||
def generate_metadata(self, address, phone):
|
||||
old_address = old_address_from_phone(self.import_dir, phone)
|
||||
|
||||
logg.debug('address {}'.format(address))
|
||||
old_address_upper = strip_0x(old_address).upper()
|
||||
metadata_path = '{}/old/{}/{}/{}.json'.format(
|
||||
self.import_dir,
|
||||
old_address_upper[:2],
|
||||
old_address_upper[2:4],
|
||||
old_address_upper,
|
||||
)
|
||||
|
||||
f = open(metadata_path, 'r')
|
||||
o = json.load(f)
|
||||
f.close()
|
||||
|
||||
u = Person.deserialize(o)
|
||||
|
||||
if u.identities.get('evm') == None:
|
||||
u.identities['evm'] = {}
|
||||
sub_chain_str = '{}:{}'.format(self.chain_spec.common_name(), self.chain_spec.network_id())
|
||||
u.identities['evm'][sub_chain_str] = [add_0x(address)]
|
||||
|
||||
new_address_clean = strip_0x(address)
|
||||
filepath = os.path.join(
|
||||
def generate_person_metadata(self, blockchain_address: str, phone_number: str):
|
||||
logg.debug(f'blockchain address: {blockchain_address}')
|
||||
old_blockchain_address = old_address_from_phone(self.import_dir, phone_number)
|
||||
old_address_upper = strip_0x(old_blockchain_address).upper()
|
||||
metadata_path = f'{self.import_dir}/old/{old_address_upper[:2]}/{old_address_upper[2:4]}/{old_address_upper}.json'
|
||||
with open(metadata_path, 'r') as metadata_file:
|
||||
person_metadata = json.load(metadata_file)
|
||||
person = Person.deserialize(person_metadata)
|
||||
if not person.identities.get('evm'):
|
||||
person.identities['evm'] = {}
|
||||
sub_chain_str = f'{self.chain_spec.common_name()}:{self.chain_spec.network_id()}'
|
||||
person.identities['evm'][sub_chain_str] = [add_0x(blockchain_address)]
|
||||
blockchain_address = strip_0x(blockchain_address)
|
||||
file_path = os.path.join(
|
||||
self.import_dir,
|
||||
'new',
|
||||
new_address_clean[:2].upper(),
|
||||
new_address_clean[2:4].upper(),
|
||||
new_address_clean.upper() + '.json',
|
||||
blockchain_address[:2].upper(),
|
||||
blockchain_address[2:4].upper(),
|
||||
blockchain_address.upper() + '.json'
|
||||
)
|
||||
os.makedirs(os.path.dirname(filepath), exist_ok=True)
|
||||
|
||||
o = u.serialize()
|
||||
f = open(filepath, 'w')
|
||||
f.write(json.dumps(o))
|
||||
f.close()
|
||||
|
||||
meta_key = generate_metadata_pointer(bytes.fromhex(new_address_clean), ':cic.person')
|
||||
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
||||
serialized_person_metadata = person.serialize()
|
||||
with open(file_path, 'w') as metadata_file:
|
||||
metadata_file.write(json.dumps(serialized_person_metadata))
|
||||
logg.debug(f'written person metadata for address: {blockchain_address}')
|
||||
meta_filepath = os.path.join(
|
||||
self.import_dir,
|
||||
'meta',
|
||||
'{}.json'.format(new_address_clean.upper()),
|
||||
'{}.json'.format(blockchain_address.upper()),
|
||||
)
|
||||
os.symlink(os.path.realpath(filepath), meta_filepath)
|
||||
os.symlink(os.path.realpath(file_path), meta_filepath)
|
||||
return blockchain_address
|
||||
|
||||
# write ussd data
|
||||
ussd_data = {
|
||||
'phone': phone,
|
||||
'is_activated': 1,
|
||||
'preferred_language': random.sample(['en', 'sw'], 1)[0],
|
||||
'is_disabled': False
|
||||
}
|
||||
ussd_data_dir = os.path.join(self.import_dir, 'ussd')
|
||||
ussd_data_file_path = os.path.join(ussd_data_dir, f'{old_address}.json')
|
||||
f = open(ussd_data_file_path, 'w')
|
||||
f.write(json.dumps(ussd_data))
|
||||
f.close()
|
||||
|
||||
# write preferences data
|
||||
@celery_app.task(bind=True, base=MetadataTask)
|
||||
def generate_preferences_data(self, data: tuple):
|
||||
blockchain_address: str = data[0]
|
||||
preferences = data[1]
|
||||
preferences_dir = os.path.join(self.import_dir, 'preferences')
|
||||
preferences_data = {
|
||||
'preferred_language': ussd_data['preferred_language']
|
||||
}
|
||||
|
||||
preferences_key = generate_metadata_pointer(bytes.fromhex(new_address_clean[2:]), ':cic.preferences')
|
||||
preferences_key = generate_metadata_pointer(bytes.fromhex(strip_0x(blockchain_address)), ':cic.preferences')
|
||||
preferences_filepath = os.path.join(preferences_dir, 'meta', preferences_key)
|
||||
|
||||
filepath = os.path.join(
|
||||
preferences_dir,
|
||||
'new',
|
||||
@@ -164,95 +112,95 @@ def generate_metadata(self, address, phone):
|
||||
preferences_key.upper() + '.json'
|
||||
)
|
||||
os.makedirs(os.path.dirname(filepath), exist_ok=True)
|
||||
|
||||
f = open(filepath, 'w')
|
||||
f.write(json.dumps(preferences_data))
|
||||
f.close()
|
||||
with open(filepath, 'w') as preferences_file:
|
||||
preferences_file.write(json.dumps(preferences))
|
||||
logg.debug(f'written preferences metadata: {preferences} for address: {blockchain_address}')
|
||||
os.symlink(os.path.realpath(filepath), preferences_filepath)
|
||||
|
||||
logg.debug('found metadata {} for phone {}'.format(o, phone))
|
||||
|
||||
return address
|
||||
return blockchain_address
|
||||
|
||||
|
||||
@celery_app.task(bind=True, base=MetadataTask)
|
||||
def opening_balance_tx(self, address, phone, serial):
|
||||
old_address = old_address_from_phone(self.import_dir, phone)
|
||||
def generate_pins_data(self, blockchain_address: str, phone_number: str):
|
||||
pins_file = f'{self.import_dir}/pins.csv'
|
||||
file_op = 'a' if os.path.exists(pins_file) else 'w'
|
||||
with open(pins_file, file_op) as pins_file:
|
||||
password_hash = uuid.uuid4().hex
|
||||
pins_file.write(f'{phone_number},{password_hash}\n')
|
||||
logg.debug(f'written pin data for address: {blockchain_address}')
|
||||
return blockchain_address
|
||||
|
||||
k = to_checksum_address(strip_0x(old_address))
|
||||
balance = self.balances[k]
|
||||
logg.debug('found balance {} for address {} phone {}'.format(balance, old_address, phone))
|
||||
|
||||
@celery_app.task(bind=True, base=MetadataTask)
|
||||
def generate_ussd_data(self, blockchain_address: str, phone_number: str):
|
||||
ussd_data_file = f'{self.import_dir}/ussd_data.csv'
|
||||
file_op = 'a' if os.path.exists(ussd_data_file) else 'w'
|
||||
preferred_language = random.sample(["en", "sw"], 1)[0]
|
||||
preferences = {'preferred_language': preferred_language}
|
||||
with open(ussd_data_file, file_op) as ussd_data_file:
|
||||
ussd_data_file.write(f'{phone_number}, { 1}, {preferred_language}, {False}\n')
|
||||
logg.debug(f'written ussd data for address: {blockchain_address}')
|
||||
return blockchain_address, preferences
|
||||
|
||||
|
||||
@celery_app.task(bind=True, base=MetadataTask)
|
||||
def opening_balance_tx(self, blockchain_address: str, phone_number: str, serial: str):
|
||||
old_blockchain_address = old_address_from_phone(self.import_dir, phone_number)
|
||||
address = to_checksum_address(strip_0x(old_blockchain_address))
|
||||
balance = self.balances[address]
|
||||
logg.debug(f'found balance: {balance} for address: {address} phone: {phone_number}')
|
||||
decimal_balance = self.balance_processor.get_decimal_amount(int(balance))
|
||||
|
||||
(tx_hash_hex, o) = self.balance_processor.get_rpc_tx(address, decimal_balance, serial)
|
||||
|
||||
tx_hash_hex, o = self.balance_processor.get_rpc_tx(blockchain_address, decimal_balance, serial)
|
||||
tx = unpack(bytes.fromhex(strip_0x(o)), self.chain_spec)
|
||||
logg.debug('generated tx token value {} to {} tx hash {}'.format(decimal_balance, address, tx_hash_hex))
|
||||
|
||||
tx_path = os.path.join(
|
||||
self.import_dir,
|
||||
'txs',
|
||||
strip_0x(tx_hash_hex),
|
||||
)
|
||||
|
||||
f = open(tx_path, 'w')
|
||||
f.write(strip_0x(o))
|
||||
f.close()
|
||||
|
||||
tx_nonce_path = os.path.join(
|
||||
self.import_dir,
|
||||
'txs',
|
||||
'.' + str(tx['nonce']),
|
||||
)
|
||||
logg.debug(f'generated tx token value: {decimal_balance}: {blockchain_address} tx hash {tx_hash_hex}')
|
||||
tx_path = os.path.join(self.import_dir, 'txs', strip_0x(tx_hash_hex))
|
||||
with open(tx_path, 'w') as tx_file:
|
||||
tx_file.write(strip_0x(o))
|
||||
logg.debug(f'written tx with tx hash: {tx["hash"]} for address: {blockchain_address}')
|
||||
tx_nonce_path = os.path.join(self.import_dir, 'txs', '.' + str(tx['nonce']))
|
||||
os.symlink(os.path.realpath(tx_path), tx_nonce_path)
|
||||
|
||||
return tx['hash']
|
||||
|
||||
|
||||
@celery_app.task(bind=True, base=ImportTask, autoretry_for=(FileNotFoundError,), max_retries=None,
|
||||
@celery_app.task(bind=True, base=MetadataTask)
|
||||
def resolve_phone(self, phone_number: str):
|
||||
identifier = generate_metadata_pointer(phone_number.encode('utf-8'), ':cic.phone')
|
||||
url = parse.urljoin(self.meta_url(), identifier)
|
||||
logg.debug(f'attempt getting phone pointer at: {url} for phone: {phone_number}')
|
||||
r = request.urlopen(url)
|
||||
address = json.load(r)
|
||||
address = address.replace('"', '')
|
||||
logg.debug(f'address: {address} for phone: {phone_number}')
|
||||
return address
|
||||
|
||||
|
||||
@celery_app.task(autoretry_for=(FileNotFoundError,),
|
||||
bind=True,
|
||||
base=ImportTask,
|
||||
max_retries=None,
|
||||
default_retry_delay=0.1)
|
||||
def send_txs(self, nonce):
|
||||
if nonce == self.count + self.balance_processor.nonce_offset:
|
||||
logg.info('reached nonce {} (offset {} + count {}) exiting'.format(nonce, self.balance_processor.nonce_offset,
|
||||
self.count))
|
||||
return
|
||||
|
||||
logg.debug('attempt to open symlink for nonce {}'.format(nonce))
|
||||
tx_nonce_path = os.path.join(
|
||||
self.import_dir,
|
||||
'txs',
|
||||
'.' + str(nonce),
|
||||
)
|
||||
f = open(tx_nonce_path, 'r')
|
||||
tx_signed_raw_hex = f.read()
|
||||
f.close()
|
||||
|
||||
os.unlink(tx_nonce_path)
|
||||
|
||||
o = raw(add_0x(tx_signed_raw_hex))
|
||||
tx_hash_hex = self.balance_processor.conn.do(o)
|
||||
|
||||
logg.info('sent nonce {} tx hash {}'.format(nonce, tx_hash_hex)) # tx_signed_raw_hex))
|
||||
|
||||
nonce += 1
|
||||
|
||||
queue = self.request.delivery_info.get('routing_key')
|
||||
s = celery.signature(
|
||||
'import_task.send_txs',
|
||||
[
|
||||
nonce,
|
||||
],
|
||||
queue=queue,
|
||||
)
|
||||
s.apply_async()
|
||||
if nonce == self.count + self.balance_processor.nonce_offset:
|
||||
logg.info(f'reached nonce {nonce} (offset {self.balance_processor.nonce_offset} + count {self.count}).')
|
||||
celery_app.control.broadcast('shutdown', destination=[f'celery@{queue}'])
|
||||
|
||||
logg.debug(f'attempt to open symlink for nonce {nonce}')
|
||||
tx_nonce_path = os.path.join(self.import_dir, 'txs', '.' + str(nonce))
|
||||
with open(tx_nonce_path, 'r') as tx_nonce_file:
|
||||
tx_signed_raw_hex = tx_nonce_file.read()
|
||||
os.unlink(tx_nonce_path)
|
||||
o = raw(add_0x(tx_signed_raw_hex))
|
||||
if self.include_balances:
|
||||
tx_hash_hex = self.balance_processor.conn.do(o)
|
||||
logg.info(f'sent nonce {nonce} tx hash {tx_hash_hex}')
|
||||
nonce += 1
|
||||
s = celery.signature('import_task.send_txs', [nonce], queue=queue)
|
||||
s.apply_async()
|
||||
return nonce
|
||||
|
||||
|
||||
@celery_app.task
|
||||
def set_pins(config: dict, phone_to_pins: list):
|
||||
# define db connection
|
||||
@celery_app.task()
|
||||
def set_pin_data(config: dict, phone_to_pins: list):
|
||||
db_conn = psycopg2.connect(
|
||||
database=config.get('database'),
|
||||
host=config.get('host'),
|
||||
@@ -261,24 +209,17 @@ def set_pins(config: dict, phone_to_pins: list):
|
||||
password=config.get('password')
|
||||
)
|
||||
db_cursor = db_conn.cursor()
|
||||
|
||||
# update db
|
||||
sql = 'UPDATE account SET password_hash = %s WHERE phone_number = %s'
|
||||
for element in phone_to_pins:
|
||||
sql = 'UPDATE account SET password_hash = %s WHERE phone_number = %s'
|
||||
db_cursor.execute(sql, (element[1], element[0]))
|
||||
logg.debug(f'Updating: {element[0]} with: {element[1]}')
|
||||
|
||||
# commit changes
|
||||
db_conn.commit()
|
||||
|
||||
# close connections
|
||||
db_cursor.close()
|
||||
db_conn.close()
|
||||
|
||||
|
||||
@celery_app.task
|
||||
def set_ussd_data(config: dict, ussd_data: dict):
|
||||
# define db connection
|
||||
def set_ussd_data(config: dict, ussd_data: list):
|
||||
db_conn = psycopg2.connect(
|
||||
database=config.get('database'),
|
||||
host=config.get('host'),
|
||||
@@ -287,20 +228,12 @@ def set_ussd_data(config: dict, ussd_data: dict):
|
||||
password=config.get('password')
|
||||
)
|
||||
db_cursor = db_conn.cursor()
|
||||
|
||||
# process ussd_data
|
||||
account_status = 1
|
||||
if ussd_data['is_activated'] == 1:
|
||||
account_status = 2
|
||||
preferred_language = ussd_data['preferred_language']
|
||||
phone_number = ussd_data['phone']
|
||||
|
||||
sql = 'UPDATE account SET status = %s, preferred_language = %s WHERE phone_number = %s'
|
||||
db_cursor.execute(sql, (account_status, preferred_language, phone_number))
|
||||
|
||||
# commit changes
|
||||
for element in ussd_data:
|
||||
status = 2 if int(element[1]) == 1 else 1
|
||||
preferred_language = element[2]
|
||||
phone_number = element[0]
|
||||
db_cursor.execute(sql, (status, preferred_language, phone_number))
|
||||
db_conn.commit()
|
||||
|
||||
# close connections
|
||||
db_cursor.close()
|
||||
db_conn.close()
|
||||
|
||||
@@ -3,56 +3,61 @@ import argparse
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import redis
|
||||
import sys
|
||||
import time
|
||||
import urllib.request
|
||||
import uuid
|
||||
from urllib import request
|
||||
from urllib.parse import urlencode
|
||||
|
||||
# external imports
|
||||
import celery
|
||||
import confini
|
||||
import phonenumbers
|
||||
import redis
|
||||
from chainlib.chain import ChainSpec
|
||||
from cic_types.models.person import Person
|
||||
from confini import Config
|
||||
|
||||
# local imports
|
||||
from import_util import get_celery_worker_status
|
||||
|
||||
default_config_dir = './config'
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
logg = logging.getLogger()
|
||||
|
||||
default_config_dir = '/usr/local/etc/cic'
|
||||
arg_parser = argparse.ArgumentParser(description='Daemon worker that handles data seeding tasks.')
|
||||
# batch size should be slightly below cumulative gas limit worth, eg 80000 gas txs with 8000000 limit is a bit less than 100 batch size
|
||||
arg_parser.add_argument('--batch-size',
|
||||
dest='batch_size',
|
||||
default=100,
|
||||
type=int,
|
||||
help='burst size of sending transactions to node')
|
||||
arg_parser.add_argument('--batch-delay', dest='batch_delay', default=3, type=int, help='seconds delay between batches')
|
||||
arg_parser.add_argument('-c', type=str, default=default_config_dir, help='config root to use.')
|
||||
arg_parser.add_argument('--env-prefix',
|
||||
default=os.environ.get('CONFINI_ENV_PREFIX'),
|
||||
dest='env_prefix',
|
||||
type=str,
|
||||
help='environment prefix for variables to overwrite configuration.')
|
||||
arg_parser.add_argument('-i', '--chain-spec', type=str, dest='i', help='chain spec')
|
||||
arg_parser.add_argument('-q', type=str, default='cic-import-ussd', help='celery queue to submit data seeding tasks to.')
|
||||
arg_parser.add_argument('--redis-db', dest='redis_db', type=int, help='redis db to use for task submission and callback')
|
||||
arg_parser.add_argument('--redis-host', dest='redis_host', type=str, help='redis host to use for task submission')
|
||||
arg_parser.add_argument('--redis-port', dest='redis_port', type=int, help='redis host to use for task submission')
|
||||
arg_parser.add_argument('--ussd-host', dest='ussd_host', type=str,
|
||||
help="host to ussd app responsible for processing ussd requests.")
|
||||
arg_parser.add_argument('--ussd-no-ssl', dest='ussd_no_ssl', help='do not use ssl (careful)', action='store_true')
|
||||
arg_parser.add_argument('--ussd-port', dest='ussd_port', type=str,
|
||||
help="port to ussd app responsible for processing ussd requests.")
|
||||
arg_parser.add_argument('-v', help='be verbose', action='store_true')
|
||||
arg_parser.add_argument('-vv', help='be more verbose', action='store_true')
|
||||
arg_parser.add_argument('import_dir', default='out', type=str, help='user export directory')
|
||||
args = arg_parser.parse_args()
|
||||
|
||||
argparser = argparse.ArgumentParser()
|
||||
argparser.add_argument('-c', type=str, default=default_config_dir, help='config file')
|
||||
argparser.add_argument('-i', '--chain-spec', dest='i', type=str, help='Chain specification string')
|
||||
argparser.add_argument('--redis-host', dest='redis_host', type=str, help='redis host to use for task submission')
|
||||
argparser.add_argument('--redis-port', dest='redis_port', type=int, help='redis host to use for task submission')
|
||||
argparser.add_argument('--redis-db', dest='redis_db', type=int, help='redis db to use for task submission and callback')
|
||||
argparser.add_argument('--batch-size', dest='batch_size', default=100, type=int,
|
||||
help='burst size of sending transactions to node') # batch size should be slightly below cumulative gas limit worth, eg 80000 gas txs with 8000000 limit is a bit less than 100 batch size
|
||||
argparser.add_argument('--batch-delay', dest='batch_delay', default=3, type=int, help='seconds delay between batches')
|
||||
argparser.add_argument('--timeout', default=60.0, type=float, help='Callback timeout')
|
||||
argparser.add_argument('--ussd-host', dest='ussd_host', type=str,
|
||||
help="host to ussd app responsible for processing ussd requests.")
|
||||
argparser.add_argument('--ussd-port', dest='ussd_port', type=str,
|
||||
help="port to ussd app responsible for processing ussd requests.")
|
||||
argparser.add_argument('--ussd-no-ssl', dest='ussd_no_ssl', help='do not use ssl (careful)', action='store_true')
|
||||
argparser.add_argument('-q', type=str, default='cic-eth', help='Task queue')
|
||||
argparser.add_argument('-v', action='store_true', help='Be verbose')
|
||||
argparser.add_argument('-vv', action='store_true', help='Be more verbose')
|
||||
argparser.add_argument('user_dir', type=str, help='path to users export dir tree')
|
||||
args = argparser.parse_args()
|
||||
if args.vv:
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
elif args.v:
|
||||
logging.getLogger().setLevel(logging.INFO)
|
||||
|
||||
if args.v:
|
||||
logg.setLevel(logging.INFO)
|
||||
elif args.vv:
|
||||
logg.setLevel(logging.DEBUG)
|
||||
|
||||
config_dir = args.c
|
||||
config = confini.Config(config_dir, os.environ.get('CONFINI_ENV_PREFIX'))
|
||||
config = Config(args.c, args.env_prefix)
|
||||
config.process()
|
||||
args_override = {
|
||||
'CIC_CHAIN_SPEC': getattr(args, 'i'),
|
||||
@@ -60,44 +65,29 @@ args_override = {
|
||||
'REDIS_PORT': getattr(args, 'redis_port'),
|
||||
'REDIS_DB': getattr(args, 'redis_db'),
|
||||
}
|
||||
config.dict_override(args_override, 'cli')
|
||||
logg.debug('config loaded from {}:\n{}'.format(args.c, config))
|
||||
config.dict_override(args_override, 'cli flag')
|
||||
config.censor('PASSWORD', 'DATABASE')
|
||||
config.censor('PASSWORD', 'SSL')
|
||||
logg.debug(f'config loaded from {args.c}:\n{config}')
|
||||
|
||||
celery_app = celery.Celery(broker=config.get('CELERY_BROKER_URL'), backend=config.get('CELERY_RESULT_URL'))
|
||||
get_celery_worker_status(celery_app=celery_app)
|
||||
old_account_dir = os.path.join(args.import_dir, 'old')
|
||||
os.stat(old_account_dir)
|
||||
logg.debug(f'created old system data dir: {old_account_dir}')
|
||||
|
||||
redis_host = config.get('REDIS_HOST')
|
||||
redis_port = config.get('REDIS_PORT')
|
||||
redis_db = config.get('REDIS_DB')
|
||||
r = redis.Redis(redis_host, redis_port, redis_db)
|
||||
new_account_dir = os.path.join(args.import_dir, 'new')
|
||||
os.makedirs(new_account_dir, exist_ok=True)
|
||||
logg.debug(f'created new system data dir: {new_account_dir}')
|
||||
|
||||
ps = r.pubsub()
|
||||
person_metadata_dir = os.path.join(args.import_dir, 'meta')
|
||||
os.makedirs(person_metadata_dir, exist_ok=True)
|
||||
logg.debug(f'created person metadata dir: {person_metadata_dir}')
|
||||
|
||||
user_new_dir = os.path.join(args.user_dir, 'new')
|
||||
os.makedirs(user_new_dir, exist_ok=True)
|
||||
|
||||
ussd_data_dir = os.path.join(args.user_dir, 'ussd')
|
||||
os.makedirs(ussd_data_dir, exist_ok=True)
|
||||
|
||||
preferences_dir = os.path.join(args.user_dir, 'preferences')
|
||||
preferences_dir = os.path.join(args.import_dir, 'preferences')
|
||||
os.makedirs(os.path.join(preferences_dir, 'meta'), exist_ok=True)
|
||||
logg.debug(f'created preferences metadata dir: {preferences_dir}')
|
||||
|
||||
meta_dir = os.path.join(args.user_dir, 'meta')
|
||||
os.makedirs(meta_dir, exist_ok=True)
|
||||
valid_service_codes = config.get('USSD_SERVICE_CODE').split(",")
|
||||
|
||||
user_old_dir = os.path.join(args.user_dir, 'old')
|
||||
os.stat(user_old_dir)
|
||||
|
||||
txs_dir = os.path.join(args.user_dir, 'txs')
|
||||
os.makedirs(txs_dir, exist_ok=True)
|
||||
|
||||
chain_spec = ChainSpec.from_chain_str(config.get('CIC_CHAIN_SPEC'))
|
||||
chain_str = str(chain_spec)
|
||||
|
||||
batch_size = args.batch_size
|
||||
batch_delay = args.batch_delay
|
||||
ussd_port = args.ussd_port
|
||||
ussd_host = args.ussd_host
|
||||
ussd_no_ssl = args.ussd_no_ssl
|
||||
if ussd_no_ssl is True:
|
||||
ussd_ssl = False
|
||||
@@ -105,7 +95,17 @@ else:
|
||||
ussd_ssl = True
|
||||
|
||||
|
||||
def build_ussd_request(phone, host, port, service_code, username, password, ssl=False):
|
||||
celery_app = celery.Celery(broker=config.get('CELERY_BROKER_URL'), backend=config.get('CELERY_RESULT_URL'))
|
||||
get_celery_worker_status(celery_app)
|
||||
|
||||
|
||||
def build_ussd_request(host: str,
|
||||
password: str,
|
||||
phone_number: str,
|
||||
port: str,
|
||||
service_code: str,
|
||||
username: str,
|
||||
ssl: bool = False):
|
||||
url = 'http'
|
||||
if ssl:
|
||||
url += 's'
|
||||
@@ -115,16 +115,16 @@ def build_ussd_request(phone, host, port, service_code, username, password, ssl=
|
||||
url += '/?username={}&password={}'.format(username, password)
|
||||
|
||||
logg.info('ussd service url {}'.format(url))
|
||||
logg.info('ussd phone {}'.format(phone))
|
||||
logg.info('ussd phone {}'.format(phone_number))
|
||||
|
||||
session = uuid.uuid4().hex
|
||||
data = {
|
||||
'sessionId': session,
|
||||
'serviceCode': service_code,
|
||||
'phoneNumber': phone,
|
||||
'phoneNumber': phone_number,
|
||||
'text': service_code,
|
||||
}
|
||||
req = urllib.request.Request(url)
|
||||
req = request.Request(url)
|
||||
req.method = 'POST'
|
||||
data_str = urlencode(data)
|
||||
data_bytes = data_str.encode('utf-8')
|
||||
@@ -134,85 +134,77 @@ def build_ussd_request(phone, host, port, service_code, username, password, ssl=
|
||||
return req
|
||||
|
||||
|
||||
def register_ussd(i, u):
|
||||
phone_object = phonenumbers.parse(u.tel)
|
||||
phone = phonenumbers.format_number(phone_object, phonenumbers.PhoneNumberFormat.E164)
|
||||
logg.debug('tel {} {}'.format(u.tel, phone))
|
||||
req = build_ussd_request(
|
||||
phone,
|
||||
ussd_host,
|
||||
ussd_port,
|
||||
config.get('APP_SERVICE_CODE'),
|
||||
'',
|
||||
'',
|
||||
ussd_ssl
|
||||
)
|
||||
response = urllib.request.urlopen(req)
|
||||
def e164_phone_number(phone_number: str):
|
||||
phone_object = phonenumbers.parse(phone_number)
|
||||
return phonenumbers.format_number(phone_object, phonenumbers.PhoneNumberFormat.E164)
|
||||
|
||||
|
||||
def register_account(person: Person):
|
||||
phone_number = e164_phone_number(person.tel)
|
||||
logg.debug(f'tel: {phone_number}')
|
||||
req = build_ussd_request(args.ussd_host,
|
||||
'',
|
||||
phone_number,
|
||||
args.ussd_port,
|
||||
valid_service_codes[0],
|
||||
'',
|
||||
ussd_ssl)
|
||||
response = request.urlopen(req)
|
||||
response_data = response.read().decode('utf-8')
|
||||
state = response_data[:3]
|
||||
out = response_data[4:]
|
||||
logg.debug('ussd reponse: {}'.format(out))
|
||||
logg.debug(f'ussd response: {response_data[4:]}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
i = 0
|
||||
j = 0
|
||||
for x in os.walk(user_old_dir):
|
||||
for x in os.walk(old_account_dir):
|
||||
for y in x[2]:
|
||||
if y[len(y) - 5:] != '.json':
|
||||
continue
|
||||
# handle json containing person object
|
||||
filepath = os.path.join(x[0], y)
|
||||
f = open(filepath, 'r')
|
||||
try:
|
||||
o = json.load(f)
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
f.close()
|
||||
logg.error('load error for {}: {}'.format(y, e))
|
||||
continue
|
||||
f.close()
|
||||
u = Person.deserialize(o)
|
||||
|
||||
register_ussd(i, u)
|
||||
|
||||
phone_object = phonenumbers.parse(u.tel)
|
||||
phone = phonenumbers.format_number(phone_object, phonenumbers.PhoneNumberFormat.E164)
|
||||
|
||||
s_phone = celery.signature(
|
||||
'import_task.resolve_phone',
|
||||
[
|
||||
phone,
|
||||
],
|
||||
queue='cic-import-ussd',
|
||||
file_path = os.path.join(x[0], y)
|
||||
with open(file_path, 'r') as account_file:
|
||||
try:
|
||||
account_data = json.load(account_file)
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
logg.error('load error for {}: {}'.format(y, e))
|
||||
continue
|
||||
person = Person.deserialize(account_data)
|
||||
register_account(person)
|
||||
phone_number = e164_phone_number(person.tel)
|
||||
s_resolve_phone = celery.signature(
|
||||
'import_task.resolve_phone', [phone_number], queue=args.q
|
||||
)
|
||||
|
||||
s_meta = celery.signature(
|
||||
'import_task.generate_metadata',
|
||||
[
|
||||
phone,
|
||||
],
|
||||
queue='cic-import-ussd',
|
||||
s_person_metadata = celery.signature(
|
||||
'import_task.generate_person_metadata', [phone_number], queue=args.q
|
||||
)
|
||||
|
||||
s_balance = celery.signature(
|
||||
'import_task.opening_balance_tx',
|
||||
[
|
||||
phone,
|
||||
i,
|
||||
],
|
||||
queue='cic-import-ussd',
|
||||
s_ussd_data = celery.signature(
|
||||
'import_task.generate_ussd_data', [phone_number], queue=args.q
|
||||
)
|
||||
|
||||
s_meta.link(s_balance)
|
||||
s_phone.link(s_meta)
|
||||
# block time plus a bit of time for ussd processing
|
||||
s_phone.apply_async(countdown=7)
|
||||
s_preferences_metadata = celery.signature(
|
||||
'import_task.generate_preferences_data', [], queue=args.q
|
||||
)
|
||||
|
||||
s_pins_data = celery.signature(
|
||||
'import_task.generate_pins_data', [phone_number], queue=args.q
|
||||
)
|
||||
|
||||
s_opening_balance = celery.signature(
|
||||
'import_task.opening_balance_tx', [phone_number, i], queue=args.q
|
||||
)
|
||||
celery.chain(s_resolve_phone,
|
||||
s_person_metadata,
|
||||
s_ussd_data,
|
||||
s_preferences_metadata,
|
||||
s_pins_data,
|
||||
s_opening_balance).apply_async(countdown=7)
|
||||
|
||||
i += 1
|
||||
sys.stdout.write('imported {} {}'.format(i, u).ljust(200) + "\r")
|
||||
|
||||
sys.stdout.write('imported: {} {}'.format(i, person).ljust(200) + "\r\n")
|
||||
j += 1
|
||||
if j == batch_size:
|
||||
time.sleep(batch_delay)
|
||||
if j == args.batch_size:
|
||||
time.sleep(args.batch_delay)
|
||||
j = 0
|
||||
|
||||
@@ -1,67 +1,67 @@
|
||||
# standard imports
|
||||
import argparse
|
||||
import json
|
||||
import csv
|
||||
import logging
|
||||
import os
|
||||
import psycopg2
|
||||
|
||||
# external imports
|
||||
import celery
|
||||
from confini import Config
|
||||
|
||||
# local imports
|
||||
|
||||
|
||||
default_config_dir = './config'
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
logg = logging.getLogger()
|
||||
|
||||
default_config_dir = '/usr/local/etc/cic'
|
||||
arg_parser = argparse.ArgumentParser(description='Pins import script.')
|
||||
arg_parser.add_argument('-c', type=str, default=default_config_dir, help='config root to use.')
|
||||
arg_parser.add_argument('--env-prefix',
|
||||
default=os.environ.get('CONFINI_ENV_PREFIX'),
|
||||
dest='env_prefix',
|
||||
type=str,
|
||||
help='environment prefix for variables to overwrite configuration.')
|
||||
arg_parser.add_argument('import_dir', default='out', type=str, help='user export directory')
|
||||
arg_parser.add_argument('-v', help='be verbose', action='store_true')
|
||||
arg_parser.add_argument('-vv', help='be more verbose', action='store_true')
|
||||
|
||||
arg_parser = argparse.ArgumentParser()
|
||||
arg_parser.add_argument('-c', type=str, default=default_config_dir, help='config file')
|
||||
arg_parser.add_argument('-q', type=str, default='cic-import-ussd', help='Task queue')
|
||||
arg_parser.add_argument('-v', action='store_true', help='Be verbose')
|
||||
arg_parser.add_argument('-vv', action='store_true', help='Be more verbose')
|
||||
arg_parser.add_argument('user_dir', type=str, help='path to users export dir tree')
|
||||
args = arg_parser.parse_args()
|
||||
|
||||
if args.v:
|
||||
logg.setLevel(logging.INFO)
|
||||
elif args.vv:
|
||||
logg.setLevel(logging.DEBUG)
|
||||
if args.vv:
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
elif args.v:
|
||||
logging.getLogger().setLevel(logging.INFO)
|
||||
|
||||
config_dir = args.c
|
||||
config = Config(config_dir, os.environ.get('CONFINI_ENV_PREFIX'))
|
||||
config = Config(args.c, args.env_prefix)
|
||||
config.process()
|
||||
logg.debug('config loaded from {}:\n{}'.format(args.c, config))
|
||||
config.censor('PASSWORD', 'DATABASE')
|
||||
logg.debug(f'config loaded from {args.c}:\n{config}')
|
||||
|
||||
ussd_data_dir = os.path.join(args.user_dir, 'ussd')
|
||||
|
||||
db_configs = {
|
||||
'database': config.get('DATABASE_NAME'),
|
||||
'host': config.get('DATABASE_HOST'),
|
||||
'port': config.get('DATABASE_PORT'),
|
||||
'user': config.get('DATABASE_USER'),
|
||||
'password': config.get('DATABASE_PASSWORD')
|
||||
}
|
||||
celery_app = celery.Celery(broker=config.get('CELERY_BROKER_URL'), backend=config.get('CELERY_RESULT_URL'))
|
||||
def main():
|
||||
with open(f'{args.import_dir}/ussd_data.csv') as ussd_data_file:
|
||||
ussd_data = [tuple(row) for row in csv.reader(ussd_data_file)]
|
||||
|
||||
db_conn = psycopg2.connect(
|
||||
database=config.get('DATABASE_NAME'),
|
||||
host=config.get('DATABASE_HOST'),
|
||||
port=config.get('DATABASE_PORT'),
|
||||
user=config.get('DATABASE_USER'),
|
||||
password=config.get('DATABASE_PASSWORD')
|
||||
)
|
||||
db_cursor = db_conn.cursor()
|
||||
sql = 'UPDATE account SET status = %s, preferred_language = %s WHERE phone_number = %s'
|
||||
for element in ussd_data:
|
||||
status = 2 if int(element[1]) == 1 else 1
|
||||
preferred_language = element[2]
|
||||
phone_number = element[0]
|
||||
db_cursor.execute(sql, (status, preferred_language, phone_number))
|
||||
logg.debug(f'Updating account:{phone_number} with: preferred language: {preferred_language} status: {status}.')
|
||||
db_conn.commit()
|
||||
db_cursor.close()
|
||||
db_conn.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
for x in os.walk(ussd_data_dir):
|
||||
for y in x[2]:
|
||||
|
||||
if y[len(y) - 5:] == '.json':
|
||||
filepath = os.path.join(x[0], y)
|
||||
f = open(filepath, 'r')
|
||||
try:
|
||||
ussd_data = json.load(f)
|
||||
logg.debug(f'LOADING USSD DATA: {ussd_data}')
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
f.close()
|
||||
logg.error('load error for {}: {}'.format(y, e))
|
||||
continue
|
||||
f.close()
|
||||
|
||||
s_set_ussd_data = celery.signature(
|
||||
'import_task.set_ussd_data',
|
||||
[db_configs, ussd_data]
|
||||
)
|
||||
s_set_ussd_data.apply_async(queue='cic-import-ussd')
|
||||
main()
|
||||
|
||||
@@ -1,27 +1,4 @@
|
||||
[app]
|
||||
ALLOWED_IP=0.0.0.0/0
|
||||
LOCALE_FALLBACK=en
|
||||
LOCALE_PATH=/usr/src/cic-ussd/var/lib/locale/
|
||||
MAX_BODY_LENGTH=1024
|
||||
PASSWORD_PEPPER=QYbzKff6NhiQzY3ygl2BkiKOpER8RE/Upqs/5aZWW+I=
|
||||
SERVICE_CODE=*483*46#
|
||||
|
||||
[phone_number]
|
||||
REGION=KE
|
||||
|
||||
[ussd]
|
||||
MENU_FILE=/usr/src/data/ussd_menu.json
|
||||
user =
|
||||
pass =
|
||||
|
||||
[statemachine]
|
||||
STATES=/usr/src/cic-ussd/states/
|
||||
TRANSITIONS=/usr/src/cic-ussd/transitions/
|
||||
|
||||
[client]
|
||||
host =
|
||||
port =
|
||||
ssl =
|
||||
|
||||
[keystore]
|
||||
file_path = keystore/UTC--2021-01-08T17-18-44.521011372Z--eb3907ecad74a0013c259d5874ae7f22dcbcc95c
|
||||
allowed_ip=0.0.0.0/0
|
||||
max_body_length=1024
|
||||
password_pepper=
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
[cic]
|
||||
registry_address = 0x32E860c2A0645d1B7B005273696905F5D6DC5D05
|
||||
registry_address =
|
||||
token_index_address =
|
||||
accounts_index_address =
|
||||
declarator_address =
|
||||
approval_escrow_address =
|
||||
chain_spec = evm:bloxberg:8996
|
||||
chain_spec =
|
||||
tx_retry_delay =
|
||||
trust_address = 0xEb3907eCad74a0013c259D5874AE7f22DcBcC95C
|
||||
trust_address =
|
||||
user_ussd_svc_service_port =
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
[database]
|
||||
NAME=sempo
|
||||
USER=postgres
|
||||
PASSWORD=
|
||||
HOST=localhost
|
||||
PORT=5432
|
||||
ENGINE=postgresql
|
||||
DRIVER=psycopg2
|
||||
DEBUG=0
|
||||
POOL_SIZE=1
|
||||
name=cic_ussd
|
||||
user=postgres
|
||||
password=
|
||||
host=localhost
|
||||
port=5432
|
||||
engine=postgresql
|
||||
driver=psycopg2
|
||||
debug=0
|
||||
pool_size=1
|
||||
|
||||
5
apps/data-seeding/config/ussd.ini
Normal file
5
apps/data-seeding/config/ussd.ini
Normal file
@@ -0,0 +1,5 @@
|
||||
[ussd]
|
||||
menu_file=data/ussd_menu.json
|
||||
service_code=*483*46#,*483*061#,*384*96#
|
||||
user =
|
||||
pass =
|
||||
@@ -1,91 +0,0 @@
|
||||
# standard imports
|
||||
import argparse
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import uuid
|
||||
|
||||
# third-party imports
|
||||
import bcrypt
|
||||
import celery
|
||||
import confini
|
||||
import phonenumbers
|
||||
import random
|
||||
from cic_types.models.person import Person
|
||||
from cryptography.fernet import Fernet
|
||||
|
||||
# local imports
|
||||
|
||||
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
logg = logging.getLogger()
|
||||
|
||||
script_dir = os.path.realpath(os.path.dirname(__file__))
|
||||
default_config_dir = os.environ.get('CONFINI_DIR', os.path.join(script_dir, 'config'))
|
||||
|
||||
arg_parser = argparse.ArgumentParser()
|
||||
arg_parser.add_argument('-c', type=str, default=default_config_dir, help='Config dir')
|
||||
arg_parser.add_argument('-v', action='store_true', help='Be verbose')
|
||||
arg_parser.add_argument('-vv', action='store_true', help='Be more verbose')
|
||||
arg_parser.add_argument('--userdir', type=str, help='path to users export dir tree')
|
||||
arg_parser.add_argument('pins_dir', type=str, help='path to pin export dir tree')
|
||||
|
||||
|
||||
args = arg_parser.parse_args()
|
||||
|
||||
if args.v:
|
||||
logg.setLevel(logging.INFO)
|
||||
elif args.vv:
|
||||
logg.setLevel(logging.DEBUG)
|
||||
|
||||
config = confini.Config(args.c, os.environ.get('CONFINI_ENV_PREFIX'))
|
||||
config.process()
|
||||
logg.info('loaded config\n{}'.format(config))
|
||||
|
||||
celery_app = celery.Celery(broker=config.get('CELERY_BROKER_URL'), backend=config.get('CELERY_RESULT_URL'))
|
||||
|
||||
user_dir = args.userdir
|
||||
pins_dir = args.pins_dir
|
||||
|
||||
|
||||
def generate_password_hash():
|
||||
key = Fernet.generate_key()
|
||||
fnt = Fernet(key)
|
||||
pin = str(random.randint(1000, 9999))
|
||||
return fnt.encrypt(bcrypt.hashpw(pin.encode('utf-8'), bcrypt.gensalt())).decode()
|
||||
|
||||
|
||||
user_old_dir = os.path.join(user_dir, 'old')
|
||||
logg.debug(f'reading user data from: {user_old_dir}')
|
||||
|
||||
pins_file = open(f'{pins_dir}/pins.csv', 'w')
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
for x in os.walk(user_old_dir):
|
||||
for y in x[2]:
|
||||
# skip non-json files
|
||||
if y[len(y) - 5:] != '.json':
|
||||
continue
|
||||
|
||||
# define file path for
|
||||
filepath = None
|
||||
if y[:15] != '_ussd_data.json':
|
||||
filepath = os.path.join(x[0], y)
|
||||
f = open(filepath, 'r')
|
||||
try:
|
||||
o = json.load(f)
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
f.close()
|
||||
logg.error('load error for {}: {}'.format(y, e))
|
||||
continue
|
||||
f.close()
|
||||
u = Person.deserialize(o)
|
||||
|
||||
phone_object = phonenumbers.parse(u.tel)
|
||||
phone = phonenumbers.format_number(phone_object, phonenumbers.PhoneNumberFormat.E164)
|
||||
password_hash = uuid.uuid4().hex
|
||||
pins_file.write(f'{phone},{password_hash}\n')
|
||||
logg.info(f'Writing phone: {phone}, password_hash: {password_hash}')
|
||||
|
||||
pins_file.close()
|
||||
@@ -28,12 +28,11 @@ logg = logging.getLogger()
|
||||
|
||||
fake = Faker(['sl', 'en_US', 'no', 'de', 'ro'])
|
||||
|
||||
script_dir = os.path.realpath(os.path.dirname(__file__))
|
||||
# config_dir = os.environ.get('CONFINI_DIR', '/usr/local/etc/cic')
|
||||
config_dir = os.environ.get('CONFINI_DIR', os.path.join(script_dir, 'config'))
|
||||
default_config_dir = './config'
|
||||
|
||||
|
||||
argparser = argparse.ArgumentParser()
|
||||
argparser.add_argument('-c', type=str, default=config_dir, help='Config dir')
|
||||
argparser.add_argument('-c', type=str, default=default_config_dir, help='Config dir')
|
||||
argparser.add_argument('--tag', type=str, action='append',
|
||||
help='Tags to add to record')
|
||||
argparser.add_argument('--gift-threshold', type=int,
|
||||
@@ -53,7 +52,7 @@ elif args.vv:
|
||||
|
||||
config = confini.Config(args.c, os.environ.get('CONFINI_ENV_PREFIX'))
|
||||
config.process()
|
||||
logg.info('loaded config\n{}'.format(config))
|
||||
logg.debug('loaded config\n{}'.format(config))
|
||||
|
||||
|
||||
dt_now = datetime.datetime.utcnow()
|
||||
|
||||
@@ -9,9 +9,12 @@ COPY package.json \
|
||||
package-lock.json \
|
||||
.
|
||||
|
||||
RUN --mount=type=cache,mode=0755,target=/root/node_modules npm install
|
||||
|
||||
COPY requirements.txt .
|
||||
RUN npm ci --production
|
||||
#RUN --mount=type=cache,mode=0755,target=/root/node_modules npm install
|
||||
|
||||
COPY requirements.txt .
|
||||
COPY config/ /usr/local/etc/data-seeding
|
||||
|
||||
ARG EXTRA_INDEX_URL="https://pip.grassrootseconomics.net:8433"
|
||||
ARG GITLAB_PYTHON_REGISTRY="https://gitlab.com/api/v4/projects/27624814/packages/pypi/simple"
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
# syntax = docker/dockerfile:1.2
|
||||
FROM registry.gitlab.com/grassrootseconomics/cic-base-images:python-3.8.6-dev-5ab8bf45
|
||||
|
||||
WORKDIR /root
|
||||
|
||||
RUN mkdir -vp /usr/local/etc/cic
|
||||
|
||||
COPY package.json \
|
||||
package-lock.json \
|
||||
.
|
||||
|
||||
RUN npm install
|
||||
|
||||
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 \
|
||||
--extra-index-url $GITLAB_PYTHON_REGISTRY \
|
||||
--extra-index-url $EXTRA_INDEX_URL -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
ENTRYPOINT [ ]
|
||||
94
apps/data-seeding/import_ussd.sh
Normal file
94
apps/data-seeding/import_ussd.sh
Normal file
@@ -0,0 +1,94 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [[ -d "$OUT_DIR" ]]
|
||||
then
|
||||
echo "found existing IMPORT DIR cleaning up..."
|
||||
rm -rf "$OUT_DIR"
|
||||
mkdir -p "$OUT_DIR"
|
||||
else
|
||||
echo "IMPORT DIR does not exist creating it."
|
||||
mkdir -p "$OUT_DIR"
|
||||
fi
|
||||
|
||||
# using timeout because the timeout flag for celery inspect does not work
|
||||
timeout 5 celery inspect ping -b "$CELERY_BROKER_URL"
|
||||
if [[ $? -eq 124 ]]
|
||||
then
|
||||
>&2 echo "Celery workers not available. Is the CELERY_BROKER_URL ($CELERY_BROKER_URL) correct?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Creating seed data..."
|
||||
python create_import_users.py -vv -c "$CONFIG" --dir "$OUT_DIR" "$NUMBER_OF_USERS"
|
||||
wait $!
|
||||
|
||||
echo "Check for running celery workers ..."
|
||||
if [ -f ./cic-ussd-import.pid ];
|
||||
then
|
||||
echo "Found a running worker. Killing ..."
|
||||
kill -9 $(<cic-ussd-import.pid)
|
||||
fi
|
||||
|
||||
echo "Purge tasks from celery worker"
|
||||
celery -A cic_ussd.import_task purge -Q "$CELERY_QUEUE" --broker redis://"$REDIS_HOST":"$REDIS_PORT" -f
|
||||
|
||||
echo "Start celery work and import balance job"
|
||||
if [ "$INCLUDE_BALANCES" != "y" ]
|
||||
then
|
||||
echo "Running worker without opening balance transactions"
|
||||
TARGET_TX_COUNT=$NUMBER_OF_USERS
|
||||
nohup python cic_ussd/import_balance.py -vv -c "$CONFIG" -p "$ETH_PROVIDER" -r "$CIC_REGISTRY_ADDRESS" --token-symbol "$TOKEN_SYMBOL" -y "$KEYSTORE_PATH" "$OUT_DIR" > nohup.out 2> nohup.err < /dev/null &
|
||||
else
|
||||
echo "Running worker with opening balance transactions"
|
||||
TARGET_TX_COUNT=$((NUMBER_OF_USERS*2))
|
||||
nohup python cic_ussd/import_balance.py -vv -c "$CONFIG" -p "$ETH_PROVIDER" -r "$CIC_REGISTRY_ADDRESS" --include-balances --token-symbol "$TOKEN_SYMBOL" -y "$KEYSTORE_PATH" "$OUT_DIR" &
|
||||
fi
|
||||
|
||||
echo "Target count set to ${TARGET_TX_COUNT}"
|
||||
until [ -f ./cic-import-ussd.pid ]
|
||||
do
|
||||
echo "Polling for celery worker pid file..."
|
||||
sleep 1
|
||||
done
|
||||
IMPORT_BALANCE_JOB=$(<cic-import-ussd.pid)
|
||||
|
||||
echo "Start import users job"
|
||||
if [ "$USSD_SSL" == "y" ]
|
||||
then
|
||||
echo "Targeting secure ussd-user server"
|
||||
python cic_ussd/import_users.py -vv -c "$CONFIG" --ussd-host "$USSD_HOST" --ussd-port "$USSD_PORT" "$OUT_DIR"
|
||||
else
|
||||
python cic_ussd/import_users.py -vv -c "$CONFIG" --ussd-host "$USSD_HOST" --ussd-port "$USSD_PORT" --ussd-no-ssl "$OUT_DIR"
|
||||
fi
|
||||
|
||||
echo "Waiting for import balance job to complete ..."
|
||||
tail --pid="$IMPORT_BALANCE_JOB" -f /dev/null
|
||||
set -e
|
||||
|
||||
echo "Importing pins"
|
||||
python cic_ussd/import_pins.py -c "$CONFIG" -vv "$OUT_DIR"
|
||||
set +e
|
||||
wait $!
|
||||
set -e
|
||||
|
||||
echo "Importing ussd data"
|
||||
python cic_ussd/import_ussd_data.py -c "$CONFIG" -vv "$OUT_DIR"
|
||||
set +e
|
||||
wait $!
|
||||
|
||||
echo "Importing person metadata"
|
||||
node cic_meta/import_meta.js "$OUT_DIR" "$NUMBER_OF_USERS"
|
||||
|
||||
echo "Import preferences metadata"
|
||||
node cic_meta/import_meta_preferences.js "$OUT_DIR" "$NUMBER_OF_USERS"
|
||||
|
||||
CIC_NOTIFY_DATABASE=postgres://$DATABASE_USER:$DATABASE_PASSWORD@$DATABASE_HOST:$DATABASE_PORT/$NOTIFY_DATABASE_NAME
|
||||
NOTIFICATION_COUNT=$(psql -qtA "$CIC_NOTIFY_DATABASE" -c 'SELECT COUNT(message) FROM notification WHERE message IS NOT NULL')
|
||||
while [[ "$NOTIFICATION_COUNT" < "$TARGET_TX_COUNT" ]]
|
||||
do
|
||||
NOTIFICATION_COUNT=$(psql -qtA "$CIC_NOTIFY_DATABASE" -c 'SELECT COUNT(message) FROM notification WHERE message IS NOT NULL')
|
||||
sleep 5
|
||||
echo "Notification count is: ${NOTIFICATION_COUNT} of ${TARGET_TX_COUNT}. Checking after 5 ..."
|
||||
done
|
||||
echo "Running verify script."
|
||||
python verify.py -c "$CONFIG" -v -p "$ETH_PROVIDER" -r "$CIC_REGISTRY_ADDRESS" --exclude "$EXCLUSIONS" --meta-provider "$META_URL" --token-symbol "$TOKEN_SYMBOL" --ussd-provider "$USSD_PROVIDER" "$OUT_DIR"
|
||||
2216
apps/data-seeding/package-lock.json
generated
2216
apps/data-seeding/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user