Compare commits
15 Commits
lash/updat
...
lash/descr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3915d51ec | ||
|
|
61abd8c192 | ||
|
|
61e672dea4
|
||
|
|
f2df5b596f
|
||
|
|
39f4191c52
|
||
|
|
d93d52d20a
|
||
|
|
601079433b
|
||
|
|
696476d11d
|
||
|
|
a2de0fc8a5 | ||
|
|
e84c31f043 | ||
|
|
2e3f80a3f4
|
||
|
|
425bd184df
|
||
|
|
965aeeacb9
|
||
|
|
3b1cc00c32 | ||
|
|
da324ce7d6
|
@@ -1,28 +1,22 @@
|
||||
# standard imports
|
||||
import logging
|
||||
import datetime
|
||||
|
||||
# external imports
|
||||
# third-party imports
|
||||
import moolb
|
||||
|
||||
# local imports
|
||||
from cic_cache.db.list import (
|
||||
list_transactions_mined,
|
||||
list_transactions_account_mined,
|
||||
list_transactions_mined_with_data,
|
||||
)
|
||||
from cic_cache.db import list_transactions_mined
|
||||
from cic_cache.db import list_transactions_account_mined
|
||||
|
||||
logg = logging.getLogger()
|
||||
|
||||
|
||||
class Cache:
|
||||
class BloomCache:
|
||||
|
||||
def __init__(self, session):
|
||||
self.session = session
|
||||
|
||||
|
||||
class BloomCache(Cache):
|
||||
|
||||
@staticmethod
|
||||
def __get_filter_size(n):
|
||||
n = 8192 * 8
|
||||
@@ -93,43 +87,3 @@ class BloomCache(Cache):
|
||||
f_blocktx.add(block + tx)
|
||||
logg.debug('added block {} tx {} lo {} hi {}'.format(r[0], r[1], lowest_block, highest_block))
|
||||
return (lowest_block, highest_block, f_block.to_bytes(), f_blocktx.to_bytes(),)
|
||||
|
||||
|
||||
class DataCache(Cache):
|
||||
|
||||
def load_transactions_with_data(self, offset, end):
|
||||
rows = list_transactions_mined_with_data(self.session, offset, end)
|
||||
tx_cache = []
|
||||
highest_block = -1;
|
||||
lowest_block = -1;
|
||||
date_is_str = None # stick this in startup
|
||||
for r in rows:
|
||||
if highest_block == -1:
|
||||
highest_block = r['block_number']
|
||||
lowest_block = r['block_number']
|
||||
tx_type = 'unknown'
|
||||
|
||||
if r['value'] != None:
|
||||
tx_type = '{}.{}'.format(r['domain'], r['value'])
|
||||
|
||||
if date_is_str == None:
|
||||
date_is_str = type(r['date_block']).__name__ == 'str'
|
||||
|
||||
o = {
|
||||
'block_number': r['block_number'],
|
||||
'tx_hash': r['tx_hash'],
|
||||
'date_block': r['date_block'],
|
||||
'sender': r['sender'],
|
||||
'recipient': r['recipient'],
|
||||
'from_value': int(r['from_value']),
|
||||
'to_value': int(r['to_value']),
|
||||
'source_token': r['source_token'],
|
||||
'destination_token': r['destination_token'],
|
||||
'tx_type': tx_type,
|
||||
}
|
||||
|
||||
if date_is_str:
|
||||
o['date_block'] = datetime.datetime.fromisoformat(r['date_block'])
|
||||
|
||||
tx_cache.append(o)
|
||||
return (lowest_block, highest_block, tx_cache)
|
||||
|
||||
@@ -28,26 +28,6 @@ def list_transactions_mined(
|
||||
return r
|
||||
|
||||
|
||||
def list_transactions_mined_with_data(
|
||||
session,
|
||||
offset,
|
||||
end,
|
||||
):
|
||||
"""Executes db query to return all confirmed transactions according to the specified offset and limit.
|
||||
|
||||
:param offset: Offset in data set to return transactions from
|
||||
:type offset: int
|
||||
:param limit: Max number of transactions to retrieve
|
||||
:type limit: int
|
||||
:result: Result set
|
||||
:rtype: SQLAlchemy.ResultProxy
|
||||
"""
|
||||
s = "SELECT tx_hash, block_number, date_block, sender, recipient, from_value, to_value, source_token, destination_token, domain, value FROM tx LEFT JOIN tag_tx_link ON tx.id = tag_tx_link.tx_id LEFT JOIN tag ON tag_tx_link.tag_id = tag.id WHERE block_number >= {} AND block_number <= {} ORDER BY block_number ASC, tx_index ASC".format(offset, end)
|
||||
|
||||
r = session.execute(s)
|
||||
return r
|
||||
|
||||
|
||||
def list_transactions_account_mined(
|
||||
session,
|
||||
address,
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
# standard imports
|
||||
import logging
|
||||
import json
|
||||
import re
|
||||
import base64
|
||||
|
||||
# local imports
|
||||
from cic_cache.cache import (
|
||||
BloomCache,
|
||||
DataCache,
|
||||
)
|
||||
|
||||
logg = logging.getLogger(__name__)
|
||||
|
||||
re_transactions_all_bloom = r'/tx/(\d+)?/?(\d+)/?'
|
||||
re_transactions_account_bloom = r'/tx/user/((0x)?[a-fA-F0-9]+)/?(\d+)?/?(\d+)/?'
|
||||
re_transactions_all_data = r'/txa/(\d+)/(\d+)/?'
|
||||
|
||||
DEFAULT_LIMIT = 100
|
||||
|
||||
|
||||
def process_transactions_account_bloom(session, env):
|
||||
r = re.match(re_transactions_account_bloom, env.get('PATH_INFO'))
|
||||
if not r:
|
||||
return None
|
||||
|
||||
address = r[1]
|
||||
if r[2] == None:
|
||||
address = '0x' + address
|
||||
offset = DEFAULT_LIMIT
|
||||
if r.lastindex > 2:
|
||||
offset = r[3]
|
||||
limit = 0
|
||||
if r.lastindex > 3:
|
||||
limit = r[4]
|
||||
|
||||
c = BloomCache(session)
|
||||
(lowest_block, highest_block, bloom_filter_block, bloom_filter_tx) = c.load_transactions_account(address, offset, limit)
|
||||
|
||||
o = {
|
||||
'alg': 'sha256',
|
||||
'low': lowest_block,
|
||||
'high': highest_block,
|
||||
'block_filter': base64.b64encode(bloom_filter_block).decode('utf-8'),
|
||||
'blocktx_filter': base64.b64encode(bloom_filter_tx).decode('utf-8'),
|
||||
'filter_rounds': 3,
|
||||
}
|
||||
|
||||
j = json.dumps(o)
|
||||
|
||||
return ('application/json', j.encode('utf-8'),)
|
||||
|
||||
|
||||
def process_transactions_all_bloom(session, env):
|
||||
r = re.match(re_transactions_all_bloom, env.get('PATH_INFO'))
|
||||
if not r:
|
||||
return None
|
||||
|
||||
offset = DEFAULT_LIMIT
|
||||
if r.lastindex > 0:
|
||||
offset = r[1]
|
||||
limit = 0
|
||||
if r.lastindex > 1:
|
||||
limit = r[2]
|
||||
|
||||
c = BloomCache(session)
|
||||
(lowest_block, highest_block, bloom_filter_block, bloom_filter_tx) = c.load_transactions(offset, limit)
|
||||
|
||||
o = {
|
||||
'alg': 'sha256',
|
||||
'low': lowest_block,
|
||||
'high': highest_block,
|
||||
'block_filter': base64.b64encode(bloom_filter_block).decode('utf-8'),
|
||||
'blocktx_filter': base64.b64encode(bloom_filter_tx).decode('utf-8'),
|
||||
'filter_rounds': 3,
|
||||
}
|
||||
|
||||
j = json.dumps(o)
|
||||
|
||||
return ('application/json', j.encode('utf-8'),)
|
||||
|
||||
|
||||
def process_transactions_all_data(session, env):
|
||||
r = re.match(re_transactions_all_data, env.get('PATH_INFO'))
|
||||
if not r:
|
||||
return None
|
||||
if env.get('HTTP_X_CIC_CACHE_MODE') != 'all':
|
||||
return None
|
||||
|
||||
offset = r[1]
|
||||
end = r[2]
|
||||
if r[2] < r[1]:
|
||||
raise ValueError('cart before the horse, dude')
|
||||
|
||||
c = DataCache(session)
|
||||
(lowest_block, highest_block, tx_cache) = c.load_transactions_with_data(offset, end)
|
||||
|
||||
for r in tx_cache:
|
||||
r['date_block'] = r['date_block'].timestamp()
|
||||
|
||||
o = {
|
||||
'low': lowest_block,
|
||||
'high': highest_block,
|
||||
'data': tx_cache,
|
||||
}
|
||||
|
||||
|
||||
j = json.dumps(o)
|
||||
|
||||
return ('application/json', j.encode('utf-8'),)
|
||||
@@ -1,20 +1,18 @@
|
||||
# standard imports
|
||||
import os
|
||||
import re
|
||||
import logging
|
||||
import argparse
|
||||
import json
|
||||
import base64
|
||||
|
||||
# external imports
|
||||
# third-party imports
|
||||
import confini
|
||||
|
||||
# local imports
|
||||
from cic_cache import BloomCache
|
||||
from cic_cache.db import dsn_from_config
|
||||
from cic_cache.db.models.base import SessionBase
|
||||
from cic_cache.runnable.daemons.query import (
|
||||
process_transactions_account_bloom,
|
||||
process_transactions_all_bloom,
|
||||
process_transactions_all_data,
|
||||
)
|
||||
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
logg = logging.getLogger()
|
||||
@@ -46,6 +44,72 @@ logg.debug('config:\n{}'.format(config))
|
||||
dsn = dsn_from_config(config)
|
||||
SessionBase.connect(dsn, config.true('DATABASE_DEBUG'))
|
||||
|
||||
re_transactions_all_bloom = r'/tx/(\d+)?/?(\d+)/?'
|
||||
re_transactions_account_bloom = r'/tx/user/((0x)?[a-fA-F0-9]+)/?(\d+)?/?(\d+)/?'
|
||||
|
||||
DEFAULT_LIMIT = 100
|
||||
|
||||
|
||||
def process_transactions_account_bloom(session, env):
|
||||
r = re.match(re_transactions_account_bloom, env.get('PATH_INFO'))
|
||||
if not r:
|
||||
return None
|
||||
|
||||
address = r[1]
|
||||
if r[2] == None:
|
||||
address = '0x' + address
|
||||
offset = DEFAULT_LIMIT
|
||||
if r.lastindex > 2:
|
||||
offset = r[3]
|
||||
limit = 0
|
||||
if r.lastindex > 3:
|
||||
limit = r[4]
|
||||
|
||||
c = BloomCache(session)
|
||||
(lowest_block, highest_block, bloom_filter_block, bloom_filter_tx) = c.load_transactions_account(address, offset, limit)
|
||||
|
||||
o = {
|
||||
'alg': 'sha256',
|
||||
'low': lowest_block,
|
||||
'high': highest_block,
|
||||
'block_filter': base64.b64encode(bloom_filter_block).decode('utf-8'),
|
||||
'blocktx_filter': base64.b64encode(bloom_filter_tx).decode('utf-8'),
|
||||
'filter_rounds': 3,
|
||||
}
|
||||
|
||||
j = json.dumps(o)
|
||||
|
||||
return ('application/json', j.encode('utf-8'),)
|
||||
|
||||
|
||||
def process_transactions_all_bloom(session, env):
|
||||
r = re.match(re_transactions_all_bloom, env.get('PATH_INFO'))
|
||||
if not r:
|
||||
return None
|
||||
|
||||
offset = DEFAULT_LIMIT
|
||||
if r.lastindex > 0:
|
||||
offset = r[1]
|
||||
limit = 0
|
||||
if r.lastindex > 1:
|
||||
limit = r[2]
|
||||
|
||||
c = BloomCache(session)
|
||||
(lowest_block, highest_block, bloom_filter_block, bloom_filter_tx) = c.load_transactions(offset, limit)
|
||||
|
||||
o = {
|
||||
'alg': 'sha256',
|
||||
'low': lowest_block,
|
||||
'high': highest_block,
|
||||
'block_filter': base64.b64encode(bloom_filter_block).decode('utf-8'),
|
||||
'blocktx_filter': base64.b64encode(bloom_filter_tx).decode('utf-8'),
|
||||
'filter_rounds': 3,
|
||||
}
|
||||
|
||||
j = json.dumps(o)
|
||||
|
||||
return ('application/json', j.encode('utf-8'),)
|
||||
|
||||
|
||||
# uwsgi application
|
||||
def application(env, start_response):
|
||||
@@ -55,16 +119,10 @@ def application(env, start_response):
|
||||
|
||||
session = SessionBase.create_session()
|
||||
for handler in [
|
||||
process_transactions_all_data,
|
||||
process_transactions_all_bloom,
|
||||
process_transactions_account_bloom,
|
||||
]:
|
||||
r = None
|
||||
try:
|
||||
r = handler(session, env)
|
||||
except ValueError as e:
|
||||
start_response('400 {}'.format(str(e)))
|
||||
return []
|
||||
r = handler(session, env)
|
||||
if r != None:
|
||||
(mime_type, content) = r
|
||||
break
|
||||
|
||||
@@ -88,16 +88,3 @@ def txs(
|
||||
tx_hash_first,
|
||||
tx_hash_second,
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def tag_txs(
|
||||
init_database,
|
||||
txs,
|
||||
):
|
||||
|
||||
db.add_tag(init_database, 'taag', domain='test')
|
||||
init_database.commit()
|
||||
|
||||
db.tag_transaction(init_database, txs[1], 'taag', domain='test')
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
# standard imports
|
||||
import json
|
||||
|
||||
# external imports
|
||||
import pytest
|
||||
|
||||
# local imports
|
||||
from cic_cache.runnable.daemons.query import process_transactions_all_data
|
||||
|
||||
|
||||
def test_api_all_data(
|
||||
init_database,
|
||||
txs,
|
||||
):
|
||||
|
||||
env = {
|
||||
'PATH_INFO': '/txa/410000/420000',
|
||||
'HTTP_X_CIC_CACHE_MODE': 'all',
|
||||
}
|
||||
j = process_transactions_all_data(init_database, env)
|
||||
o = json.loads(j[1])
|
||||
|
||||
assert len(o['data']) == 2
|
||||
|
||||
env = {
|
||||
'PATH_INFO': '/txa/420000/410000',
|
||||
'HTTP_X_CIC_CACHE_MODE': 'all',
|
||||
}
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
j = process_transactions_all_data(init_database, env)
|
||||
@@ -9,7 +9,6 @@ import pytest
|
||||
|
||||
# local imports
|
||||
from cic_cache import BloomCache
|
||||
from cic_cache.cache import DataCache
|
||||
|
||||
logg = logging.getLogger()
|
||||
|
||||
@@ -34,23 +33,3 @@ def test_cache(
|
||||
|
||||
assert b[0] == list_defaults['block'] - 1
|
||||
|
||||
|
||||
def test_cache_data(
|
||||
init_database,
|
||||
list_defaults,
|
||||
list_actors,
|
||||
list_tokens,
|
||||
txs,
|
||||
tag_txs,
|
||||
):
|
||||
|
||||
session = init_database
|
||||
|
||||
c = DataCache(session)
|
||||
b = c.load_transactions_with_data(410000, 420000)
|
||||
|
||||
assert len(b[2]) == 2
|
||||
assert b[2][0]['tx_hash'] == txs[1]
|
||||
assert b[2][1]['tx_type'] == 'unknown'
|
||||
assert b[2][0]['tx_type'] == 'test.taag'
|
||||
|
||||
|
||||
@@ -16,6 +16,4 @@ def default_token(self):
|
||||
return {
|
||||
'symbol': self.default_token_symbol,
|
||||
'address': self.default_token_address,
|
||||
'name': self.default_token_name,
|
||||
'decimals': self.default_token_decimals,
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ from chainlib.eth.connection import (
|
||||
from chainlib.chain import ChainSpec
|
||||
from chainqueue.db.models.otx import Otx
|
||||
from cic_eth_registry.error import UnknownContractError
|
||||
from cic_eth_registry.erc20 import ERC20Token
|
||||
import liveness.linux
|
||||
|
||||
|
||||
@@ -208,11 +207,6 @@ 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, rpc, BaseTask.default_token_address)
|
||||
default_token.load(rpc)
|
||||
BaseTask.default_token_decimals = default_token.decimals
|
||||
BaseTask.default_token_name = default_token.name
|
||||
|
||||
BaseTask.run_dir = config.get('CIC_RUN_DIR')
|
||||
logg.info('default token set to {} {}'.format(BaseTask.default_token_symbol, BaseTask.default_token_address))
|
||||
|
||||
|
||||
@@ -67,8 +67,6 @@ def main():
|
||||
token_info = t.get()
|
||||
print('Default token symbol: {}'.format(token_info['symbol']))
|
||||
print('Default token address: {}'.format(token_info['address']))
|
||||
logg.debug('Default token name: {}'.format(token_info['name']))
|
||||
logg.debug('Default token decimals: {}'.format(token_info['decimals']))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -33,8 +33,6 @@ class BaseTask(celery.Task):
|
||||
create_gas_oracle = RPCGasOracle
|
||||
default_token_address = None
|
||||
default_token_symbol = None
|
||||
default_token_name = None
|
||||
default_token_decimals = None
|
||||
run_dir = '/run'
|
||||
|
||||
def create_session(self):
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[AFRICASTALKING]
|
||||
api_username =
|
||||
api_key =
|
||||
api_sender_id =
|
||||
api_username = foo
|
||||
api_key = bar
|
||||
api_sender_id = baz
|
||||
|
||||
@@ -120,7 +120,7 @@ class MetadataRequestsHandler(Metadata):
|
||||
data = json.loads(response_data.decode('utf-8'))
|
||||
if result.status_code == 200 and self.cic_type == ':cic.person':
|
||||
person = Person()
|
||||
deserialized_person = person.deserialize(person_data=data)
|
||||
deserialized_person = person.deserialize(person_data=json.loads(data))
|
||||
data = json.dumps(deserialized_person.serialize())
|
||||
cache_data(self.metadata_pointer, data=data)
|
||||
logg.debug(f'caching: {data} with key: {self.metadata_pointer}')
|
||||
|
||||
@@ -325,14 +325,6 @@ def process_menu_interaction_requests(chain_str: str,
|
||||
# get user
|
||||
user = Account.session.query(Account).filter_by(phone_number=phone_number).first()
|
||||
|
||||
# retrieve and cache user's metadata
|
||||
blockchain_address = user.blockchain_address
|
||||
s_query_person_metadata = celery.signature(
|
||||
'cic_ussd.tasks.metadata.query_person_metadata',
|
||||
[blockchain_address]
|
||||
)
|
||||
s_query_person_metadata.apply_async(queue='cic-ussd')
|
||||
|
||||
# find any existing ussd session
|
||||
existing_ussd_session = UssdSession.session.query(UssdSession).filter_by(
|
||||
external_session_id=external_session_id).first()
|
||||
|
||||
@@ -371,6 +371,13 @@ def process_start_menu(display_key: str, user: Account):
|
||||
# get operational balance
|
||||
operational_balance = compute_operational_balance(balances=balances_data)
|
||||
|
||||
# retrieve and cache account's metadata
|
||||
s_query_person_metadata = celery.signature(
|
||||
'cic_ussd.tasks.metadata.query_person_metadata',
|
||||
[blockchain_address]
|
||||
)
|
||||
s_query_person_metadata.apply_async(queue='cic-ussd')
|
||||
|
||||
# retrieve and cache account's statement
|
||||
retrieve_account_statement(blockchain_address=blockchain_address)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
en:
|
||||
account_successfully_created: |-
|
||||
You have been registered on Sarafu Network! To use dial *384*96# on Safaricom and *483*96# on other networks. For help %{support_phone}.
|
||||
Hello, you have been registered on Sarafu Network! Your balance is %{balance} %{token_symbol}. To use dial *483*46#. For help 0757628885.
|
||||
received_tokens: |-
|
||||
Successfully received %{amount} %{token_symbol} from %{tx_sender_information} %{timestamp}. New balance is %{balance} %{token_symbol}.
|
||||
terms: |-
|
||||
By using the service, you agree to the terms and conditions at http://grassecon.org/tos
|
||||
By using the service, you agree to the terms and conditions at https://www.grassrootseconomics.org/terms-and-conditions.
|
||||
@@ -1,7 +1,7 @@
|
||||
sw:
|
||||
account_successfully_created: |-
|
||||
Umesajiliwa kwa huduma ya Sarafu! Kutumia bonyeza *384*96# Safaricom ama *483*46# kwa utandao tofauti. Kwa Usaidizi %{support_phone}.
|
||||
Habari, umesajiliwa kwa huduma ya sarafu! Salio lako ni %{token_symbol} %{balance}. Kutumia bonyeza *483*46#. Kwa Usaidizi 0757628885.
|
||||
received_tokens: |-
|
||||
Umepokea %{amount} %{token_symbol} kutoka kwa %{tx_sender_information} %{timestamp}. Salio la %{token_symbol} ni %{balance}.
|
||||
terms: |-
|
||||
Kwa kutumia hii huduma, umekubali sheria na masharti yafuatayo http://grassecon.org/tos
|
||||
Kwa kutumia hii huduma, umekubali sheria na masharti yafuatayo https://www.grassrootseconomics.org/terms-and-conditions.
|
||||
@@ -1,30 +1,29 @@
|
||||
en:
|
||||
kenya:
|
||||
initial_language_selection: |-
|
||||
CON Welcome to Sarafu Network
|
||||
CON Welcome to Sarafu
|
||||
1. English
|
||||
2. Kiswahili
|
||||
3. Help
|
||||
initial_pin_entry: |-
|
||||
CON Please enter a new four number PIN for your account.
|
||||
CON Please enter a PIN to manage your account.
|
||||
0. Back
|
||||
initial_pin_confirmation: |-
|
||||
CON Enter your four number PIN again
|
||||
CON Enter your PIN again
|
||||
0. Back
|
||||
enter_given_name: |-
|
||||
CON Enter first name
|
||||
0. Back
|
||||
enter_family_name: |-
|
||||
CON Enter family name
|
||||
CON Enter last name
|
||||
0. Back
|
||||
enter_gender: |-
|
||||
CON Enter gender
|
||||
1. Male
|
||||
2. Female
|
||||
3. Other
|
||||
0. Back
|
||||
enter_location: |-
|
||||
CON Enter your location
|
||||
CON Enter location
|
||||
0. Back
|
||||
enter_products: |-
|
||||
CON Please enter a product or service you offer
|
||||
@@ -84,34 +83,34 @@ en:
|
||||
Please enter your PIN to confirm.
|
||||
0. Back
|
||||
retry: |-
|
||||
CON Please enter your PIN. You have %{remaining_attempts} attempts remaining
|
||||
CON Please enter your PIN. You have %{remaining_attempts} attempts remaining.
|
||||
0. Back
|
||||
display_metadata_pin_authorization:
|
||||
first: |-
|
||||
CON Please enter your PIN
|
||||
CON Please enter your PIN.
|
||||
0. Back
|
||||
retry: |-
|
||||
CON Please enter your PIN. You have %{remaining_attempts} attempts remaining
|
||||
CON Please enter your PIN. You have %{remaining_attempts} attempts remaining.
|
||||
0. Back
|
||||
account_balances_pin_authorization:
|
||||
first: |-
|
||||
CON Please enter your PIN to view balances
|
||||
CON Please enter your PIN to view balances.
|
||||
0. Back
|
||||
retry: |-
|
||||
CON Please enter your PIN. You have %{remaining_attempts} attempts remaining
|
||||
CON Please enter your PIN. You have %{remaining_attempts} attempts remaining.
|
||||
0. Back
|
||||
account_statement_pin_authorization:
|
||||
first: |-
|
||||
CON Please enter your PIN to view statement
|
||||
CON Please enter your PIN to view statement.
|
||||
0. Back
|
||||
retry: |-
|
||||
CON Please enter your PIN. You have %{remaining_attempts} attempts remaining
|
||||
CON Please enter your PIN. You have %{remaining_attempts} attempts remaining.
|
||||
0. Back
|
||||
account_balances: |-
|
||||
CON Your balances are as follows:
|
||||
balance: %{operational_balance} %{token_symbol}
|
||||
fees: %{tax} %{token_symbol}
|
||||
rewards: %{bonus} %{token_symbol}
|
||||
taxes: %{tax} %{token_symbol}
|
||||
bonsuses: %{bonus} %{token_symbol}
|
||||
0. Back
|
||||
first_transaction_set: |-
|
||||
CON %{first_transaction_set}
|
||||
@@ -141,9 +140,9 @@ en:
|
||||
exit_pin_blocked: |-
|
||||
END Your PIN has been blocked. For help, please call %{support_phone}.
|
||||
exit_invalid_pin: |-
|
||||
END The PIN you have entered is invalid. PIN must consist of 4 digits. For help, call %{support_phone}.
|
||||
END The PIN you have entered is Invalid. PIN must consist of 4 digits. For help, call %{support_phone}.
|
||||
exit_invalid_new_pin: |-
|
||||
END The PIN you have entered is invalid. PIN must be different from your current PIN. For help, call %{support_phone}.
|
||||
END The PIN you have entered is Invalid. PIN must be different from your current PIN. For help, call %{support_phone}.
|
||||
exit_pin_mismatch: |-
|
||||
END The new PIN does not match the one you entered. Please try again. For help, call %{support_phone}.
|
||||
exit_invalid_recipient: |-
|
||||
@@ -170,4 +169,4 @@ en:
|
||||
00. Back
|
||||
99. Exit
|
||||
account_creation_prompt: |-
|
||||
Your account is being created. You will receive an SMS when your account is ready.
|
||||
Your account is being created. You will receive an SMS when your account is ready.
|
||||
@@ -89,12 +89,7 @@ After this step is run, you can find top-level ethereum addresses (like the cic
|
||||
|
||||
|
||||
#### Custodial provisions
|
||||
response_data = send_ussd_request(address, self.data_dir)
|
||||
state = response_data[:3]
|
||||
out = response_data[4:]
|
||||
m = '{} {}'.format(state, out[:7])
|
||||
if m != 'CON Welcome':
|
||||
raise VerifierError(response_data, 'ussd')
|
||||
|
||||
This step is _only_ needed if you are importing using `cic_eth` or `cic_ussd`
|
||||
|
||||
`RUN_MASK=2 docker-compose up contract-migration`
|
||||
@@ -109,8 +104,8 @@ If importing using `cic_eth` or `cic_ussd` also run:
|
||||
* cic-eth-retrier
|
||||
|
||||
If importing using `cic_ussd` also run:
|
||||
* cic-user-tasker
|
||||
* cic-user-ussd-server
|
||||
* cic-ussd-tasker
|
||||
* cic-ussd-server
|
||||
* cic-notify-tasker
|
||||
|
||||
If metadata is to be imported, also run:
|
||||
@@ -174,26 +169,6 @@ In second terminal:
|
||||
|
||||
`python cic_ussd/import_users.py -v -c config out`
|
||||
|
||||
|
||||
##### Importing pins and ussd data (optional)
|
||||
Once the user imports are complete the next step should be importing the user's pins and auxiliary ussd data. This can be done in 3 steps:
|
||||
|
||||
In one terminal run:
|
||||
|
||||
`python create_import_pins.py -c config -v --userdir <path to the users export dir tree> pinsdir <path to pin export dir tree>`
|
||||
|
||||
This script will recursively walk through all the directories defining user data in the users export directory and generate a csv file containing phone numbers and password hashes generated using fernet in a manner reflecting the nature of said hashes in the old system.
|
||||
This csv file will be stored in the pins export dir defined as the positional argument.
|
||||
|
||||
Once the creation of the pins file is complete, proceed to import the pins and ussd data as follows:
|
||||
|
||||
- To import the pins:
|
||||
|
||||
`python cic_ussd/import_pins.py -c config -v pinsdir <path to pin export dir tree>`
|
||||
|
||||
- To import ussd data:
|
||||
`python cic_ussd/import_ussd_data.py -c config -v userdir <path to the users export dir tree>`
|
||||
|
||||
The balance script is a celery task worker, and will not exit by itself in its current version. However, after it's done doing its job, you will find "reached nonce ... exiting" among the last lines of the log.
|
||||
|
||||
The connection parameters for the `cic-ussd-server` is currently _hardcoded_ in the `import_users.py` script file.
|
||||
|
||||
@@ -171,7 +171,6 @@ if __name__ == '__main__':
|
||||
logg.error('load error for {}: {}'.format(y, e))
|
||||
continue
|
||||
f.close()
|
||||
logg.debug('deserializing {} {}'.format(filepath, o))
|
||||
u = Person.deserialize(o)
|
||||
|
||||
new_address = register_eth(i, u)
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
# standard import
|
||||
import argparse
|
||||
import csv
|
||||
import logging
|
||||
import os
|
||||
|
||||
# third-party imports
|
||||
import celery
|
||||
import confini
|
||||
|
||||
# local imports
|
||||
from import_task import *
|
||||
|
||||
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.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')
|
||||
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)
|
||||
|
||||
# process configs
|
||||
config_dir = args.c
|
||||
config = confini.Config(config_dir, os.environ.get('CONFINI_ENV_PREFIX'))
|
||||
config.process()
|
||||
|
||||
celery_app = celery.Celery(broker=config.get('CELERY_BROKER_URL'), backend=config.get('CELERY_RESULT_URL'))
|
||||
|
||||
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')
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
with open(f'{args.pins_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
|
||||
)
|
||||
s_import_pins.apply_async()
|
||||
|
||||
argv = ['worker', '-Q', 'cic-import-ussd', '--loglevel=DEBUG']
|
||||
celery_app.worker_main(argv)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -8,8 +8,6 @@ import json
|
||||
|
||||
# external imports
|
||||
import celery
|
||||
import psycopg2
|
||||
from psycopg2 import extras
|
||||
from hexathon import (
|
||||
strip_0x,
|
||||
add_0x,
|
||||
@@ -55,7 +53,7 @@ class MetadataTask(ImportTask):
|
||||
def meta_url(self):
|
||||
scheme = 'http'
|
||||
if self.meta_ssl:
|
||||
scheme += 's'
|
||||
scheme += s
|
||||
url = urllib.parse.urlparse('{}://{}:{}/{}'.format(scheme, self.meta_host, self.meta_port, self.meta_path))
|
||||
return urllib.parse.urlunparse(url)
|
||||
|
||||
@@ -93,6 +91,7 @@ def resolve_phone(self, phone):
|
||||
def generate_metadata(self, address, phone):
|
||||
old_address = old_address_from_phone(self.import_dir, phone)
|
||||
|
||||
logg.debug('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> foo')
|
||||
logg.debug('address {}'.format(address))
|
||||
old_address_upper = strip_0x(old_address).upper()
|
||||
metadata_path = '{}/old/{}/{}/{}.json'.format(
|
||||
@@ -217,60 +216,3 @@ def send_txs(self, nonce):
|
||||
|
||||
|
||||
return nonce
|
||||
|
||||
|
||||
@celery_app.task
|
||||
def set_pins(config: dict, phone_to_pins: list):
|
||||
# define db connection
|
||||
db_conn = psycopg2.connect(
|
||||
database=config.get('database'),
|
||||
host=config.get('host'),
|
||||
port=config.get('port'),
|
||||
user=config.get('user'),
|
||||
password=config.get('password')
|
||||
)
|
||||
db_cursor = db_conn.cursor()
|
||||
|
||||
# update db
|
||||
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
|
||||
db_conn = psycopg2.connect(
|
||||
database=config.get('database'),
|
||||
host=config.get('host'),
|
||||
port=config.get('port'),
|
||||
user=config.get('user'),
|
||||
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 account_status = %s, preferred_language = %s WHERE phone_number = %s'
|
||||
db_cursor.execute(sql, (account_status, preferred_language, phone_number))
|
||||
|
||||
# commit changes
|
||||
db_conn.commit()
|
||||
|
||||
# close connections
|
||||
db_cursor.close()
|
||||
db_conn.close()
|
||||
|
||||
|
||||
@@ -87,13 +87,6 @@ chain_str = str(chain_spec)
|
||||
batch_size = args.batch_size
|
||||
batch_delay = args.batch_delay
|
||||
|
||||
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')
|
||||
}
|
||||
|
||||
|
||||
def build_ussd_request(phone, host, port, service_code, username, password, ssl=False):
|
||||
@@ -142,60 +135,57 @@ if __name__ == '__main__':
|
||||
for y in x[2]:
|
||||
if y[len(y)-5:] != '.json':
|
||||
continue
|
||||
# handle json containing person object
|
||||
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
|
||||
filepath = os.path.join(x[0], y)
|
||||
f = open(filepath, 'r')
|
||||
try:
|
||||
o = json.load(f)
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
f.close()
|
||||
u = Person.deserialize(o)
|
||||
logg.error('load error for {}: {}'.format(y, e))
|
||||
continue
|
||||
f.close()
|
||||
u = Person.deserialize(o)
|
||||
|
||||
new_address = register_ussd(i, u)
|
||||
new_address = register_ussd(i, u)
|
||||
|
||||
phone_object = phonenumbers.parse(u.tel)
|
||||
phone = phonenumbers.format_number(phone_object, phonenumbers.PhoneNumberFormat.E164)
|
||||
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',
|
||||
)
|
||||
s_phone = celery.signature(
|
||||
'import_task.resolve_phone',
|
||||
[
|
||||
phone,
|
||||
],
|
||||
queue='cic-import-ussd',
|
||||
)
|
||||
|
||||
s_meta = celery.signature(
|
||||
'import_task.generate_metadata',
|
||||
[
|
||||
phone,
|
||||
],
|
||||
queue='cic-import-ussd',
|
||||
)
|
||||
s_meta = celery.signature(
|
||||
'import_task.generate_metadata',
|
||||
[
|
||||
phone,
|
||||
],
|
||||
queue='cic-import-ussd',
|
||||
)
|
||||
|
||||
s_balance = celery.signature(
|
||||
'import_task.opening_balance_tx',
|
||||
[
|
||||
phone,
|
||||
i,
|
||||
],
|
||||
queue='cic-import-ussd',
|
||||
)
|
||||
s_balance = celery.signature(
|
||||
'import_task.opening_balance_tx',
|
||||
[
|
||||
phone,
|
||||
i,
|
||||
],
|
||||
queue='cic-import-ussd',
|
||||
)
|
||||
|
||||
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_meta.link(s_balance)
|
||||
s_phone.link(s_meta)
|
||||
s_phone.apply_async(countdown=7) # block time plus a bit of time for ussd processing
|
||||
|
||||
i += 1
|
||||
sys.stdout.write('imported {} {}'.format(i, u).ljust(200) + "\r")
|
||||
|
||||
j += 1
|
||||
if j == batch_size:
|
||||
time.sleep(batch_delay)
|
||||
j = 0
|
||||
i += 1
|
||||
sys.stdout.write('imported {} {}'.format(i, u).ljust(200) + "\r")
|
||||
|
||||
j += 1
|
||||
if j == batch_size:
|
||||
time.sleep(batch_delay)
|
||||
j = 0
|
||||
|
||||
#fi.close()
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
# standard imports
|
||||
import argparse
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
|
||||
# external imports
|
||||
import celery
|
||||
from confini import Config
|
||||
|
||||
# local imports
|
||||
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
logg = logging.getLogger()
|
||||
|
||||
default_config_dir = '/usr/local/etc/cic'
|
||||
|
||||
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-eth', 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)
|
||||
|
||||
config_dir = args.c
|
||||
config = Config(config_dir, os.environ.get('CONFINI_ENV_PREFIX'))
|
||||
config.process()
|
||||
|
||||
user_old_dir = os.path.join(args.user_dir, 'old')
|
||||
os.stat(user_old_dir)
|
||||
|
||||
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'))
|
||||
|
||||
if __name__ == '__main__':
|
||||
for x in os.walk(user_old_dir):
|
||||
for y in x[2]:
|
||||
|
||||
if y[len(y) - 5:] != '.json':
|
||||
continue
|
||||
|
||||
# handle ussd_data json object
|
||||
if y[:15] == '_ussd_data.json':
|
||||
filepath = os.path.join(x[0], y)
|
||||
f = open(filepath, 'r')
|
||||
try:
|
||||
ussd_data = json.load(f)
|
||||
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')
|
||||
@@ -1,90 +0,0 @@
|
||||
# standard imports
|
||||
import argparse
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
|
||||
# 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 = generate_password_hash()
|
||||
pins_file.write(f'{phone},{password_hash}\n')
|
||||
logg.info(f'Writing phone: {phone}, password_hash: {password_hash}')
|
||||
|
||||
pins_file.close()
|
||||
@@ -105,7 +105,7 @@ def genId(addr, typ):
|
||||
def genDate():
|
||||
|
||||
ts = random.randint(ts_then, ts_now)
|
||||
return int(datetime.datetime.fromtimestamp(ts).timestamp())
|
||||
return datetime.datetime.fromtimestamp(ts).timestamp()
|
||||
|
||||
|
||||
def genPhone():
|
||||
@@ -130,7 +130,6 @@ def genCats():
|
||||
def genAmount():
|
||||
return random.randint(0, gift_max) * gift_factor
|
||||
|
||||
|
||||
def genDob():
|
||||
dob_src = fake.date_of_birth(minimum_age=15)
|
||||
dob = {}
|
||||
@@ -169,9 +168,8 @@ def gen():
|
||||
}
|
||||
p.location['area_name'] = city
|
||||
if random.randint(0, 1):
|
||||
p.location['latitude'] = (random.random() + 180) - 90 #fake.local_latitude()
|
||||
p.location['longitude'] = (random.random() + 360) - 180 #fake.local_latitude()
|
||||
|
||||
p.identities['latitude'] = (random.random() + 180) - 90 #fake.local_latitude()
|
||||
p.identities['longitude'] = (random.random() + 360) - 180 #fake.local_latitude()
|
||||
|
||||
return (old_blockchain_checksum_address, phone, p)
|
||||
|
||||
@@ -193,7 +191,6 @@ def prepareLocalFilePath(datadir, address):
|
||||
if __name__ == '__main__':
|
||||
|
||||
base_dir = os.path.join(user_dir, 'old')
|
||||
ussd_dir = os.path.join(user_dir, 'ussd')
|
||||
os.makedirs(base_dir, exist_ok=True)
|
||||
|
||||
fa = open(os.path.join(user_dir, 'balances.csv'), 'w')
|
||||
@@ -213,23 +210,11 @@ if __name__ == '__main__':
|
||||
|
||||
print(o)
|
||||
|
||||
ussd_data = {
|
||||
'phone': phone,
|
||||
'is_activated': 1,
|
||||
'preferred_language': random.sample(['en', 'sw'], 1)[0],
|
||||
'is_disabled': False
|
||||
}
|
||||
|
||||
d = prepareLocalFilePath(base_dir, uid)
|
||||
f = open('{}/{}'.format(d, uid + '.json'), 'w')
|
||||
json.dump(o.serialize(), f)
|
||||
f.close()
|
||||
|
||||
d = prepareLocalFilePath(ussd_dir, uid)
|
||||
x = open('{}/{}'.format(d, uid + '_ussd_data.json'), 'w')
|
||||
json.dump(ussd_data, x)
|
||||
x.close()
|
||||
|
||||
pidx = genPhoneIndex(phone)
|
||||
d = prepareLocalFilePath(os.path.join(user_dir, 'phone'), pidx)
|
||||
f = open('{}/{}'.format(d, pidx), 'w')
|
||||
|
||||
@@ -23,7 +23,7 @@ from chainlib.eth.connection import EthHTTPConnection
|
||||
from chainlib.eth.gas import RPCGasOracle
|
||||
from chainlib.eth.nonce import RPCNonceOracle
|
||||
from cic_types.processor import generate_metadata_pointer
|
||||
from eth_accounts_index.registry import AccountRegistry
|
||||
from eth_accounts_index import AccountRegistry
|
||||
from eth_contract_registry import Registry
|
||||
from crypto_dev_signer.keystore.dict import DictKeystore
|
||||
from crypto_dev_signer.eth.signer.defaultsigner import ReferenceSigner as EIP155Signer
|
||||
|
||||
@@ -1,37 +1,52 @@
|
||||
# standard imports
|
||||
import argparse
|
||||
import copy
|
||||
import hashlib
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
import time
|
||||
import argparse
|
||||
import sys
|
||||
import re
|
||||
import hashlib
|
||||
import csv
|
||||
import json
|
||||
import urllib
|
||||
import urllib.request
|
||||
import copy
|
||||
import uuid
|
||||
import urllib.request
|
||||
|
||||
# external imports
|
||||
import celery
|
||||
import confini
|
||||
import eth_abi
|
||||
import confini
|
||||
from hexathon import (
|
||||
strip_0x,
|
||||
add_0x,
|
||||
)
|
||||
from chainsyncer.backend.memory import MemBackend
|
||||
from chainsyncer.driver import HeadSyncer
|
||||
from chainlib.chain import ChainSpec
|
||||
from chainlib.eth.address import to_checksum_address
|
||||
from chainlib.eth.connection import EthHTTPConnection
|
||||
from chainlib.eth.constant import ZERO_ADDRESS
|
||||
from chainlib.eth.gas import (
|
||||
OverrideGasOracle,
|
||||
balance,
|
||||
)
|
||||
from chainlib.eth.tx import TxFactory
|
||||
from chainlib.eth.block import (
|
||||
block_latest,
|
||||
block_by_number,
|
||||
Block,
|
||||
)
|
||||
from chainlib.hash import keccak256_string_to_hex
|
||||
from chainlib.eth.address import to_checksum_address
|
||||
from chainlib.eth.gas import (
|
||||
OverrideGasOracle,
|
||||
balance,
|
||||
)
|
||||
from chainlib.eth.tx import TxFactory
|
||||
from chainlib.jsonrpc import jsonrpc_template
|
||||
from chainlib.eth.error import EthException
|
||||
from cic_types.models.person import (
|
||||
Person,
|
||||
generate_metadata_pointer,
|
||||
)
|
||||
Person,
|
||||
generate_metadata_pointer,
|
||||
)
|
||||
from erc20_faucet import Faucet
|
||||
from eth_erc20 import ERC20
|
||||
from hexathon.parse import strip_0x, add_0x
|
||||
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
logg = logging.getLogger()
|
||||
@@ -57,7 +72,6 @@ eth_tests = [
|
||||
|
||||
phone_tests = [
|
||||
'ussd',
|
||||
'ussd_pins'
|
||||
]
|
||||
|
||||
all_tests = eth_tests + custodial_tests + metadata_tests + phone_tests
|
||||
@@ -157,39 +171,6 @@ if logg.isEnabledFor(logging.DEBUG):
|
||||
outfunc = logg.debug
|
||||
|
||||
|
||||
def send_ussd_request(address, data_dir):
|
||||
upper_address = strip_0x(address).upper()
|
||||
f = open(os.path.join(
|
||||
data_dir,
|
||||
'new',
|
||||
upper_address[:2],
|
||||
upper_address[2:4],
|
||||
upper_address + '.json',
|
||||
), 'r'
|
||||
)
|
||||
o = json.load(f)
|
||||
f.close()
|
||||
|
||||
p = Person.deserialize(o)
|
||||
phone = p.tel
|
||||
|
||||
session = uuid.uuid4().hex
|
||||
data = {
|
||||
'sessionId': session,
|
||||
'serviceCode': config.get('APP_SERVICE_CODE'),
|
||||
'phoneNumber': phone,
|
||||
'text': '',
|
||||
}
|
||||
|
||||
req = urllib.request.Request(config.get('_USSD_PROVIDER'))
|
||||
data_str = json.dumps(data)
|
||||
data_bytes = data_str.encode('utf-8')
|
||||
req.add_header('Content-Type', 'application/json')
|
||||
req.data = data_bytes
|
||||
response = urllib.request.urlopen(req)
|
||||
return response.read().decode('utf-8')
|
||||
|
||||
|
||||
class VerifierState:
|
||||
|
||||
def __init__(self, item_keys, active_tests=None):
|
||||
@@ -373,18 +354,42 @@ class Verifier:
|
||||
|
||||
|
||||
def verify_ussd(self, address, balance=None):
|
||||
response_data = send_ussd_request(address, self.data_dir)
|
||||
upper_address = strip_0x(address).upper()
|
||||
f = open(os.path.join(
|
||||
self.data_dir,
|
||||
'new',
|
||||
upper_address[:2],
|
||||
upper_address[2:4],
|
||||
upper_address + '.json',
|
||||
), 'r'
|
||||
)
|
||||
o = json.load(f)
|
||||
f.close()
|
||||
|
||||
p = Person.deserialize(o)
|
||||
phone = p.tel
|
||||
|
||||
session = uuid.uuid4().hex
|
||||
data = {
|
||||
'sessionId': session,
|
||||
'serviceCode': config.get('APP_SERVICE_CODE'),
|
||||
'phoneNumber': phone,
|
||||
'text': config.get('APP_SERVICE_CODE'),
|
||||
}
|
||||
|
||||
req = urllib.request.Request(config.get('_USSD_PROVIDER'))
|
||||
data_str = json.dumps(data)
|
||||
data_bytes = data_str.encode('utf-8')
|
||||
req.add_header('Content-Type', 'application/json')
|
||||
req.data = data_bytes
|
||||
response = urllib.request.urlopen(req)
|
||||
response_data = response.read().decode('utf-8')
|
||||
state = response_data[:3]
|
||||
out = response_data[4:]
|
||||
m = '{} {}'.format(state, out[:7])
|
||||
if m != 'CON Welcome':
|
||||
raise VerifierError(response_data, 'ussd')
|
||||
|
||||
def verify_ussd_pins(self, address, balance):
|
||||
response_data = send_ussd_request(address, self.data_dir)
|
||||
if response_data[:11] != 'CON Balance':
|
||||
raise VerifierError(response_data, 'pins')
|
||||
|
||||
|
||||
def verify(self, address, balance, debug_stem=None):
|
||||
|
||||
|
||||
Reference in New Issue
Block a user