Compare commits

...

37 Commits

Author SHA1 Message Date
nolash
e0a22e0f9e Add block height command 2021-05-26 09:17:57 +02:00
nolash
c9c93aac69 Remove stray pasted code 2021-05-26 09:14:41 +02:00
nolash
71e22fb637 Add token symbol flag 2021-05-26 09:09:36 +02:00
4ae094fd30 Merge branch 'cic-eth-unittest' into 'master'
Unit tests for cic-eth

See merge request grassrootseconomics/cic-internal-integration!164
2021-05-25 16:22:26 +00:00
cb239f112a Unit tests for cic-eth 2021-05-25 16:22:26 +00:00
Geoff Turk
d971a6eded Merge branch 'fix-meta-data-seeding' into 'master'
Fix path to PGP exports directory

See merge request grassrootseconomics/cic-internal-integration!165
2021-05-21 16:42:21 +00:00
Spencer Ofwiti
b0a6df0177 Merge branch 'spencer/metadata-identifiers' into 'master'
Add meta update cli tool.

See merge request grassrootseconomics/cic-internal-integration!138
2021-05-21 09:42:08 +00:00
Spencer Ofwiti
92c9df4e19 Add meta update cli tool. 2021-05-21 09:42:08 +00:00
Geoff Turk
9c49d568e0 Fix path to PGP exports directory 2021-05-21 10:51:32 +02:00
Louis Holbrook
d7113f3923 Merge branch 'lash/rehabilitate-traffic-2' into 'master'
Rehabilitate traffic generator script

See merge request grassrootseconomics/cic-internal-integration!145
2021-05-20 21:25:15 +00:00
Louis Holbrook
c569fe4b17 Rehabilitate traffic generator script 2021-05-20 21:25:14 +00:00
1c650df27d Merge branch 'bvander/move-scripts-to-e2e-folder' into 'master'
move files out of scripts folder to their own dir

See merge request grassrootseconomics/cic-internal-integration!137
2021-05-20 14:31:08 +00:00
a31b7bc9cd move files out of scripts folder to their own dir 2021-05-20 14:31:08 +00:00
Geoff Turk
78ff58c1a2 Merge branch 'geoff/data-success' into 'master'
Add success field to transactions_all_data

See merge request grassrootseconomics/cic-internal-integration!163
2021-05-20 10:28:07 +00:00
1676addbeb Merge branch 'philip/meta-cluster-bug' into 'master'
Refactors to handle error in metadata handling

Closes cic-ussd#45

See merge request grassrootseconomics/cic-internal-integration!160
2021-05-19 16:25:10 +00:00
1efc25ac15 Refactors to handle error in metadata handling 2021-05-19 16:25:10 +00:00
Louis Holbrook
db2ec0dcfa Merge branch 'philip/notify-errors' into 'master'
Philip/notify errors

Closes cic-notify#4

See merge request grassrootseconomics/cic-internal-integration!161
2021-05-19 16:13:06 +00:00
5148e6428b Philip/notify errors 2021-05-19 16:13:06 +00:00
Louis Holbrook
0c186ed968 Merge branch 'lash/rehabilitate-tests-eth' into 'master'
Fix outdated module names in cic-eth tests

See merge request grassrootseconomics/cic-internal-integration!162
2021-05-19 15:11:08 +00:00
Louis Holbrook
c44439bd90 Fix outdated module names in cic-eth tests 2021-05-19 15:11:08 +00:00
Geoff Turk
0411603078 Add success field to transactions_all_data 2021-05-19 16:47:22 +02:00
eee895ea71 Merge branch 'willruddick-master-patch-91858' into 'master'
small updates. note other gender

See merge request grassrootseconomics/cic-internal-integration!154
2021-05-19 10:26:52 +00:00
Louis Holbrook
a5ca898532 Merge branch 'lash/update-contracts-in-migration-2' into 'master'
imports: Fix stale dep in sovereign import users script

See merge request grassrootseconomics/cic-internal-integration!149
2021-05-19 09:57:03 +00:00
Louis Holbrook
6d8508aebf imports: Fix stale dep in sovereign import users script 2021-05-19 09:57:02 +00:00
Louis Holbrook
f8f66984d2 Merge branch 'lash/no-ussd-contamination' into 'master'
Isolate ussd-related data files in imports

See merge request grassrootseconomics/cic-internal-integration!150
2021-05-19 09:55:24 +00:00
Louis Holbrook
0f02dd1b7c Isolate ussd-related data files in imports 2021-05-19 09:55:24 +00:00
63a4a82ab0 Merge branch 'philip/replicate-wills-changes' into 'master'
Replicates changes in broken MR by will.

See merge request grassrootseconomics/cic-internal-integration!158
2021-05-19 08:25:19 +00:00
949c1070a9 Replicates changes in broken MR by will. 2021-05-19 11:19:29 +03:00
5d9fbe9b64 Merge branch 'willruddick-master-patch-28332' into 'master'
shortened and changed service code

See merge request grassrootseconomics/cic-internal-integration!152
2021-05-19 07:47:09 +00:00
873a3f082a shortened and changed service code 2021-05-19 07:47:09 +00:00
7b408cf564 Merge branch 'willruddick-master-patch-28450' into 'master'
small changes, note the 'other' gender

See merge request grassrootseconomics/cic-internal-integration!151
2021-05-19 07:33:37 +00:00
Louis Holbrook
9dfbd7034c Merge branch 'lash/decimals-in-api' into 'master'
cic-eth-tasker: Add decimals and token name to default token api call return struct

Closes cic-eth#123

See merge request grassrootseconomics/cic-internal-integration!148
2021-05-19 06:59:43 +00:00
Louis Holbrook
235f5cede8 cic-eth-tasker: Add decimals and token name to default token api call return struct 2021-05-19 06:59:42 +00:00
Geoff Turk
0a59539f9a Merge branch 'lash/cache-data-api' into 'master'
cic-cache: Add data API

Closes cic-cache#11

See merge request grassrootseconomics/cic-internal-integration!157
2021-05-18 17:13:57 +00:00
Louis Holbrook
60b36945df cic-cache: Add data API 2021-05-18 17:13:57 +00:00
e8512ebbae small updates. note other gender 2021-05-17 11:27:45 +00:00
f2c955c60b small changes, note the 'other' gender 2021-05-17 11:06:10 +00:00
102 changed files with 5456 additions and 517 deletions

View File

@@ -6,6 +6,7 @@ include:
- local: 'apps/cic-notify/.gitlab-ci.yml'
- local: 'apps/cic-meta/.gitlab-ci.yml'
- local: 'apps/cic-cache/.gitlab-ci.yml'
- local: 'apps/data-seeding/.gitlab-ci.yml'
stages:
- build

View File

@@ -1,22 +1,28 @@
# standard imports
import logging
import datetime
# third-party imports
# external imports
import moolb
# local imports
from cic_cache.db import list_transactions_mined
from cic_cache.db import list_transactions_account_mined
from cic_cache.db.list import (
list_transactions_mined,
list_transactions_account_mined,
list_transactions_mined_with_data,
)
logg = logging.getLogger()
class BloomCache:
class Cache:
def __init__(self, session):
self.session = session
class BloomCache(Cache):
@staticmethod
def __get_filter_size(n):
n = 8192 * 8
@@ -87,3 +93,44 @@ class BloomCache:
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'],
'success': r['success'],
'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)

View File

@@ -28,6 +28,26 @@ 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, success, 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,

View File

@@ -0,0 +1,110 @@
# 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'),)

View File

@@ -1,18 +1,20 @@
# standard imports
import os
import re
import logging
import argparse
import json
import base64
# third-party imports
# external 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()
@@ -44,72 +46,6 @@ 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):
@@ -119,10 +55,16 @@ 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 = handler(session, env)
r = None
try:
r = handler(session, env)
except ValueError as e:
start_response('400 {}'.format(str(e)))
return []
if r != None:
(mime_type, content) = r
break

View File

@@ -0,0 +1,2 @@
[syncer]
loop_interval = 1

View File

@@ -88,3 +88,16 @@ 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')

View File

@@ -0,0 +1,31 @@
# 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)

View File

@@ -9,6 +9,7 @@ import pytest
# local imports
from cic_cache import BloomCache
from cic_cache.cache import DataCache
logg = logging.getLogger()
@@ -33,3 +34,23 @@ 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'

View File

@@ -5,18 +5,31 @@
.cic_eth_changes_target:
rules:
- changes:
- $CONTEXT/$APP_NAME/*
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
#changes:
#- $CONTEXT/$APP_NAME/**/*
when: always
build-mr-cic-eth:
extends:
- .cic_eth_changes_target
- .py_build_merge_request
- .cic_eth_variables
- .cic_eth_changes_target
- .py_build_target_test
test-mr-cic-eth:
extends:
- .cic_eth_variables
- .cic_eth_changes_target
stage: test
image: $CI_REGISTRY_IMAGE/$APP_NAME-test:latest
script:
- cd apps/$APP_NAME/
- pytest tests/unit/
- pytest tests/task/
- pytest tests/filters/
needs: ["build-mr-cic-eth"]
build-push-cic-eth:
extends:
- .py_build_push
- .cic_eth_variables

View File

@@ -16,4 +16,6 @@ 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,
}

View File

@@ -72,7 +72,9 @@ class CallbackFilter(SyncFilter):
#transfer_data['token_address'] = tx.inputs[0]
faucet_contract = tx.inputs[0]
o = Faucet.token(faucet_contract, sender_address=self.caller_address)
c = Faucet(self.chain_spec)
o = c.token(faucet_contract, sender_address=self.caller_address)
r = conn.do(o)
transfer_data['token_address'] = add_0x(c.parse_token(r))

View File

@@ -22,6 +22,7 @@ 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
@@ -207,6 +208,11 @@ 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))

View File

@@ -67,6 +67,8 @@ 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__':

View File

@@ -33,6 +33,8 @@ 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):

View File

@@ -10,7 +10,7 @@ version = (
0,
11,
0,
'beta.13',
'beta.14',
)
version_object = semver.VersionInfo(

View File

@@ -1,48 +1,62 @@
# FROM grassrootseconomics:cic
#FROM python:3.8.6-alpine
FROM python:3.8.6-slim-buster
#COPY --from=0 /usr/local/share/cic/solidity/ /usr/local/share/cic/solidity/
FROM python:3.8.6-slim-buster as compile
WORKDIR /usr/src/cic-eth
ARG pip_extra_index_url_flag='--index https://pypi.org/simple --extra-index-url https://pip.grassrootseconomics.net:8433'
ARG root_requirement_file='requirements.txt'
#RUN apk update && \
# apk add gcc musl-dev gnupg libpq
#RUN apk add postgresql-dev
#RUN apk add linux-headers
#RUN apk add libffi-dev
RUN apt-get update && \
apt install -y gcc gnupg libpq-dev wget make g++ gnupg bash procps git
# Copy shared requirements from top of mono-repo
RUN echo "copying root req file: ${root_requirement_file}"
#COPY $root_requirement_file .
#RUN pip install -r $root_requirement_file $pip_extra_index_url_flag
RUN /usr/local/bin/python -m pip install --upgrade pip
#RUN git clone https://gitlab.com/grassrootseconomics/cic-base.git && \
# cd cic-base && \
# git checkout 7ae1f02efc206b13a65873567b0f6d1c3b7f9bc0 && \
# python merge_requirements.py | tee merged_requirements.txt
#RUN cd cic-base && \
# pip install $pip_extra_index_url_flag -r ./merged_requirements.txt
RUN pip install $pip_extra_index_url_flag cic-base[full_graph]==0.1.2b9
#RUN python -m venv venv && . venv/bin/activate
COPY cic-eth/scripts/ scripts/
COPY cic-eth/setup.cfg cic-eth/setup.py ./
COPY cic-eth/cic_eth/ cic_eth/
# Copy app specific requirements
COPY cic-eth/requirements.txt .
COPY cic-eth/test_requirements.txt .
ARG pip_extra_index_url_flag='--index https://pypi.org/simple --extra-index-url https://pip.grassrootseconomics.net:8433'
RUN /usr/local/bin/python -m pip install --upgrade pip
RUN pip install semver
# TODO use a packaging style that lets us copy requirments only ie. pip-tools
COPY cic-eth/ .
RUN pip install $pip_extra_index_url_flag .
# --- TEST IMAGE ---
FROM python:3.8.6-slim-buster as test
RUN apt-get update && \
apt install -y gcc gnupg libpq-dev wget make g++ gnupg bash procps git
WORKDIR /usr/src/cic-eth
RUN /usr/local/bin/python -m pip install --upgrade pip
COPY --from=compile /usr/local/bin/ /usr/local/bin/
COPY --from=compile /usr/local/lib/python3.8/site-packages/ \
/usr/local/lib/python3.8/site-packages/
# TODO we could use venv inside container to isolate the system and app deps further
# COPY --from=compile /usr/src/cic-eth/ .
# RUN . venv/bin/activate
COPY cic-eth/test_requirements.txt .
RUN pip install $pip_extra_index_url_flag -r test_requirements.txt
COPY cic-eth .
ENV PYTHONPATH .
ENTRYPOINT ["pytest"]
# --- RUNTIME ---
FROM python:3.8.6-slim-buster as runtime
RUN apt-get update && \
apt install -y gnupg libpq-dev procps
WORKDIR /usr/src/cic-eth
COPY --from=compile /usr/local/bin/ /usr/local/bin/
COPY --from=compile /usr/local/lib/python3.8/site-packages/ \
/usr/local/lib/python3.8/site-packages/
COPY cic-eth/docker/* ./
RUN chmod 755 *.sh
COPY cic-eth/tests/ tests/
COPY cic-eth/scripts/ scripts/
# # ini files in config directory defines the configurable parameters for the application
# # they can all be overridden by environment variables
# # to generate a list of environment variables from configuration, use: confini-dump -z <dir> (executable provided by confini package)
@@ -51,3 +65,4 @@ COPY cic-eth/cic_eth/db/migrations/ /usr/local/share/cic-eth/alembic/
COPY cic-eth/crypto_dev_signer_config/ /usr/local/etc/crypto-dev-signer/
COPY util/liveness/health.sh /usr/local/bin/health.sh

View File

@@ -22,3 +22,4 @@ sarafu-faucet==0.0.3a3
erc20-faucet==0.2.1a4
coincurve==15.0.0
potaahto~=0.0.1a2
pycryptodome==3.10.1

View File

@@ -11,17 +11,6 @@ while True:
requirements.append(l.rstrip())
f.close()
test_requirements = []
f = open('test_requirements.txt', 'r')
while True:
l = f.readline()
if l == '':
break
test_requirements.append(l.rstrip())
f.close()
setup(
install_requires=requirements,
tests_require=test_requirements,
install_requires=requirements
)

View File

@@ -4,4 +4,3 @@ pytest-mock==3.3.1
pytest-cov==2.10.1
eth-tester==0.5.0b3
py-evm==0.3.0a20
giftable-erc20-token==0.0.8a9

View File

@@ -4,7 +4,7 @@ import sys
import logging
# external imports
from chainlib.eth.erc20 import ERC20
from eth_erc20 import ERC20
# local imports
from cic_eth.api import Api

View File

@@ -14,9 +14,9 @@ from chainlib.eth.tx import (
Tx,
)
from chainlib.eth.block import Block
from chainlib.eth.erc20 import ERC20
from eth_erc20 import ERC20
from sarafu_faucet import MinterFaucet
from eth_accounts_index import AccountRegistry
from eth_accounts_index.registry import AccountRegistry
from potaahto.symbols import snake_and_camel
from hexathon import add_0x
@@ -26,7 +26,6 @@ from cic_eth.runnable.daemons.filters.callback import CallbackFilter
logg = logging.getLogger()
@pytest.mark.skip()
def test_transfer_tx(
default_chain_spec,
init_database,
@@ -66,7 +65,6 @@ def test_transfer_tx(
assert transfer_type == 'transfer'
@pytest.mark.skip()
def test_transfer_from_tx(
default_chain_spec,
init_database,

View File

@@ -4,7 +4,7 @@ import logging
# external imports
import pytest
from chainlib.eth.nonce import RPCNonceOracle
from chainlib.eth.erc20 import ERC20
from eth_erc20 import ERC20
from chainlib.eth.tx import receipt
# local imports

View File

@@ -9,7 +9,7 @@ import celery
from chainlib.connection import RPCConnection
from chainlib.eth.nonce import RPCNonceOracle
from chainlib.eth.tx import receipt
from eth_accounts_index import AccountRegistry
from eth_accounts_index.registry import AccountRegistry
from hexathon import strip_0x
from chainqueue.db.enum import StatusEnum
from chainqueue.db.models.otx import Otx

View File

@@ -4,7 +4,7 @@ import logging
# external imports
import pytest
import celery
from chainlib.eth.erc20 import ERC20
from eth_erc20 import ERC20
from chainlib.eth.nonce import RPCNonceOracle
from chainlib.eth.tx import (
receipt,

View File

@@ -3,7 +3,7 @@ from chainlib.eth.nonce import RPCNonceOracle
from chainlib.eth.tx import (
receipt,
)
from eth_address_declarator import AddressDeclarator
from eth_address_declarator import Declarator
from hexathon import add_0x
# local imports
@@ -23,7 +23,7 @@ def test_translate(
nonce_oracle = RPCNonceOracle(contract_roles['CONTRACT_DEPLOYER'], eth_rpc)
c = AddressDeclarator(default_chain_spec, signer=eth_signer, nonce_oracle=nonce_oracle)
c = Declarator(default_chain_spec, signer=eth_signer, nonce_oracle=nonce_oracle)
description = 'alice'.encode('utf-8').ljust(32, b'\x00').hex()
(tx_hash_hex, o) = c.add_declaration(address_declarator, contract_roles['CONTRACT_DEPLOYER'], agent_roles['ALICE'], add_0x(description))

View File

@@ -8,7 +8,7 @@ from chainlib.eth.tx import (
count,
receipt,
)
from chainlib.eth.erc20 import ERC20
from eth_erc20 import ERC20
from chainlib.eth.nonce import RPCNonceOracle
# local imports

51
apps/cic-meta/bin/get.js Executable file
View File

@@ -0,0 +1,51 @@
#!/usr/bin/env node
const colors = require('colors');
const {Meta} = require("../dist");
let { argv } = require('yargs')
.usage('Usage: $0 -m http://localhost:63380 -n publickeys')
.example(
'$0 -m http://localhost:63380 -n publickeys',
'Fetches the public keys blob from the meta server'
)
.option('m', {
alias: 'metaurl',
describe: 'The URL for the meta service',
demandOption: 'The meta url is required',
type: 'string',
nargs: 1,
})
.option('n', {
alias: 'name',
describe: 'The name of the resource to be fetched from the meta service',
demandOption: 'The name of the resource is required',
type: 'string',
nargs: 1,
})
.option('t', {
alias: 'type',
describe: 'The type of resource to be fetched from the meta service\n' +
'Options: `user`, `phone` and `custom`\n' +
'Defaults to `custom`',
type: 'string',
nargs: 1,
})
.epilog('Grassroots Economics (c) 2021')
.wrap(null);
const metaUrl = argv.m;
const resourceName = argv.n;
let type = argv.t;
if (type === undefined) {
type = 'custom'
}
(async () => {
const identifier = await Meta.getIdentifier(resourceName, type);
console.log(colors.cyan(`Meta server storage identifier: ${identifier}`));
const metaResponse = await Meta.get(identifier, metaUrl);
if (typeof metaResponse !== "object") {
console.error(colors.red('Metadata get failed!'));
}
console.log(colors.green(metaResponse));
})();

81
apps/cic-meta/bin/set.js Executable file
View File

@@ -0,0 +1,81 @@
#!/usr/bin/env node
const fs = require("fs");
const colors = require('colors');
const {Meta} = require("../dist");
let { argv } = require('yargs')
.usage('Usage: $0 -m http://localhost:63380 -k ./privatekeys.asc -n publickeys -r ./publickeys.asc')
.example(
'$0 -m http://localhost:63380 -k ./privatekeys.asc -n publickeys -r ./publickeys.asc',
'Updates the public keys blob to the meta server'
)
.option('m', {
alias: 'metaurl',
describe: 'The URL for the meta service',
demandOption: 'The meta url is required',
type: 'string',
nargs: 1,
})
.option('k', {
alias: 'privatekey',
describe: 'The PGP private key blob file used to sign the changes to the meta service',
demandOption: 'The private key file is required',
type: 'string',
nargs: 1,
})
.option('n', {
alias: 'name',
describe: 'The name of the resource to be set or updated to the meta service',
demandOption: 'The name of the resource is required',
type: 'string',
nargs: 1,
})
.option('r', {
alias: 'resource',
describe: 'The resource file to be set or updated to the meta service',
demandOption: 'The resource file is required',
type: 'string',
nargs: 1,
})
.option('t', {
alias: 'type',
describe: 'The type of resource to be set or updated to the meta service\n' +
'Options: `user`, `phone` and `custom`\n' +
'Defaults to `custom`',
type: 'string',
nargs: 1,
})
.epilog('Grassroots Economics (c) 2021')
.wrap(null);
const metaUrl = argv.m;
const privateKeyFile = argv.k;
const resourceName = argv.n;
const resourceFile = argv.r;
let type = argv.t;
if (type === undefined) {
type = 'custom'
}
const privateKey = readFile(privateKeyFile);
const resource = readFile(resourceFile);
(async () => {
if (privateKey && resource) {
const identifier = await Meta.getIdentifier(resourceName, type);
console.log(colors.cyan(`Meta server storage identifier: ${identifier}`));
const meta = new Meta(metaUrl, privateKey);
meta.onload = async (status) => {
const response = await meta.set(identifier, resource)
console.log(colors.green(response));
}
}
})();
function readFile(filename) {
if(!fs.existsSync(filename)) {
console.log(colors.red(`File ${filename} not found`));
return;
}
return fs.readFileSync(filename, {encoding: 'utf8', flag: 'r'});
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,14 @@
{
"name": "cic-client-meta",
"version": "0.0.7-alpha.8",
"name": "@cicnet/cic-client-meta",
"version": "0.0.11",
"description": "Signed CRDT metadata graphs for the CIC network",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"bin": {
"meta-set": "bin/set.js",
"meta-get": "bin/get.js"
},
"preferGlobal": true,
"scripts": {
"test": "mocha -r node_modules/node-localstorage/register -r ts-node/register tests/*.ts",
"build": "node_modules/typescript/bin/tsc -d --outDir dist src/index.ts",
@@ -11,12 +16,14 @@
"pack": "node_modules/typescript/bin/tsc -d --outDir dist && webpack",
"clean": "rm -rf dist",
"prepare": "npm run build && npm run build-server",
"start": "./node_modules/ts-node/dist/bin.js ./scripts/server/server.ts"
"start": "./node_modules/ts-node/dist/bin.js ./scripts/server/server.ts",
"publish": "npm publish --access public"
},
"dependencies": {
"@cicnet/crdt-meta": "^0.0.10",
"@ethereumjs/tx": "^3.0.0-beta.1",
"automerge": "^0.14.1",
"crdt-meta": "0.0.8",
"colors": "^1.4.0",
"ethereumjs-wallet": "^1.0.1",
"ini": "^1.3.8",
"openpgp": "^4.10.8",

View File

@@ -1,4 +1,4 @@
import { Config } from 'crdt-meta';
import { Config } from '@cicnet/crdt-meta';
const fs = require('fs');
if (process.argv[2] === undefined) {

View File

@@ -1,7 +1,7 @@
import * as Automerge from 'automerge';
import * as pgp from 'openpgp';
import { Envelope, Syncable } from 'crdt-meta';
import { Envelope, Syncable } from '@cicnet/crdt-meta';
function handleNoMergeGet(db, digest, keystore) {

View File

@@ -3,7 +3,8 @@ import * as fs from 'fs';
import * as path from 'path';
import * as handlers from './handlers';
import { PGPKeyStore, PGPSigner, Config, SqliteAdapter, PostgresAdapter } from 'crdt-meta';
import { PGPKeyStore, PGPSigner, Config } from '@cicnet/crdt-meta';
import { SqliteAdapter, PostgresAdapter } from '../../src/db';
import { standardArgs } from './args';

View File

@@ -0,0 +1,27 @@
import {Addressable, mergeKey, Syncable} from "@cicnet/crdt-meta";
class Custom extends Syncable implements Addressable {
name: string
value: Object
constructor(name:string, v:Object={}) {
super('', v);
Custom.toKey(name).then((cid) => {
this.id = cid;
this.value = v;
});
}
public static async toKey(item:string, identifier: string = ':cic.custom') {
return await mergeKey(Buffer.from(item), Buffer.from(identifier));
}
public key(): string {
return this.id;
}
}
export {
Custom,
}

90
apps/cic-meta/src/db.ts Normal file
View File

@@ -0,0 +1,90 @@
import * as pg from 'pg';
import * as sqlite from 'sqlite3';
type DbConfig = {
name: string
host: string
port: number
user: string
password: string
}
interface DbAdapter {
query: (s:string, callback:(e:any, rs:any) => void) => void
close: () => void
}
const re_creatematch = /^(CREATE)/i
const re_getmatch = /^(SELECT)/i;
const re_setmatch = /^(INSERT|UPDATE)/i;
class SqliteAdapter implements DbAdapter {
db: any
constructor(dbConfig:DbConfig, callback?:(any) => void) {
this.db = new sqlite.Database(dbConfig.name); //, callback);
}
public query(s:string, callback:(e:any, rs?:any) => void): void {
const local_callback = (e, rs) => {
let r = undefined;
if (rs !== undefined) {
r = {
rowCount: rs.length,
rows: rs,
}
}
callback(e, r);
};
if (s.match(re_getmatch)) {
this.db.all(s, local_callback);
} else if (s.match(re_setmatch)) {
this.db.run(s, local_callback);
} else if (s.match(re_creatematch)) {
this.db.run(s, callback);
} else {
throw 'unhandled query';
}
}
public close() {
this.db.close();
}
}
class PostgresAdapter implements DbAdapter {
db: any
constructor(dbConfig:DbConfig) {
let o = dbConfig;
o['database'] = o.name;
this.db = new pg.Pool(o);
return this.db;
}
public query(s:string, callback:(e:any, rs:any) => void): void {
this.db.query(s, (e, rs) => {
let r = {
length: rs.rowCount,
}
rs.length = rs.rowCount;
if (e === undefined) {
e = null;
}
console.debug(e, rs);
callback(e, rs);
});
}
public close() {
this.db.end();
}
}
export {
DbConfig,
SqliteAdapter,
PostgresAdapter,
}

View File

@@ -1,2 +1,4 @@
export { User } from './user';
export { Phone } from './phone';
export { Custom } from './custom';
export { Meta } from './meta';

126
apps/cic-meta/src/meta.ts Normal file
View File

@@ -0,0 +1,126 @@
import {ArgPair, Envelope, Syncable, MutablePgpKeyStore, PGPSigner} from "@cicnet/crdt-meta";
import {User} from "./user";
import {Phone} from "./phone";
import {Custom} from "./custom";
const fetch = require("node-fetch");
const headers = {
'Content-Type': 'application/json;charset=utf-8',
'x-cic-automerge': 'client'
};
const options = {
headers: headers,
};
class Meta {
keystore: MutablePgpKeyStore = new MutablePgpKeyStore();
signer: PGPSigner = new PGPSigner(this.keystore);
metaUrl: string;
private privateKey: string;
onload: (status: boolean) => void;
constructor(metaUrl: string, privateKey: any) {
this.metaUrl = metaUrl;
this.privateKey = privateKey;
this.keystore.loadKeyring().then(() => {
this.keystore.importPrivateKey(privateKey).then(() => this.onload(true));
});
}
async set(identifier: string, data: Object): Promise<any> {
let syncable: Syncable;
const response = await Meta.get(identifier, this.metaUrl);
if (response === `Request to ${this.metaUrl}/${identifier} failed. Connection error.`) {
return response;
} else if (typeof response !== "object" || typeof data !== "object") {
syncable = new Syncable(identifier, data);
const res = await this.updateMeta(syncable, identifier);
return `${res.status}: ${res.statusText}`;
} else {
syncable = await Meta.get(identifier, this.metaUrl);
let update: Array<ArgPair> = [];
for (const prop in data) {
update.push(new ArgPair(prop, data[prop]));
}
syncable.update(update, 'client-branch');
const res = await this.updateMeta(syncable, identifier);
return `${res.status}: ${res.statusText}`;
}
}
async updateMeta(syncable: Syncable, identifier: string): Promise<any> {
const envelope: Envelope = await this.wrap(syncable);
const reqBody: string = envelope.toJSON();
const putOptions = {
method: 'PUT',
headers: headers,
body: reqBody
};
return await fetch(`${this.metaUrl}/${identifier}`, putOptions).then(async response => {
if (response.ok) {
return Promise.resolve({
status: response.status,
statusText: response.statusText + ', Metadata updated successfully!'
});
} else {
return Promise.reject({
status: response.status,
statusText: response.statusText
});
}
});
}
static async get(identifier: string, metaUrl: string): Promise<any> {
const response = await fetch(`${metaUrl}/${identifier}`, options).then(response => {
if (response.ok) {
return (response.json());
} else {
return Promise.reject({
status: response.status,
statusText: response.statusText
});
}
}).catch(error => {
if (error.code === 'ECONNREFUSED') {
return `Request to ${metaUrl}/${identifier} failed. Connection error.`
}
return `${error.status}: ${error.statusText}`;
});
if (typeof response !== "object") {
return response;
}
return Envelope.fromJSON(JSON.stringify(response)).unwrap();
}
static async getIdentifier(name: string, type: string = 'custom'): Promise<string> {
let identifier: string;
type = type.toLowerCase();
if (type === 'user') {
identifier = await User.toKey(name);
} else if (type === 'phone') {
identifier = await Phone.toKey(name);
} else {
identifier = await Custom.toKey(name);
}
return identifier;
}
private wrap(syncable: Syncable): Promise<Envelope> {
return new Promise<Envelope>(async (resolve, reject) => {
syncable.setSigner(this.signer);
syncable.onwrap = async (env) => {
if (env === undefined) {
reject();
return;
}
resolve(env);
};
syncable.sign();
});
}
}
export {
Meta,
}

View File

@@ -1,4 +1,4 @@
import { Syncable, Addressable, mergeKey } from 'crdt-meta';
import { Syncable, Addressable, mergeKey } from '@cicnet/crdt-meta';
class Phone extends Syncable implements Addressable {

View File

@@ -1,4 +1,4 @@
import { Syncable, Addressable, toAddressKey } from 'crdt-meta';
import { Syncable, Addressable, toAddressKey } from '@cicnet/crdt-meta';
const keySalt = new TextEncoder().encode(':cic.person');
class User extends Syncable implements Addressable {

View File

@@ -4,7 +4,8 @@ import pgp = require('openpgp');
import sqlite = require('sqlite3');
import * as handlers from '../scripts/server/handlers';
import { Envelope, Syncable, ArgPair, PGPKeyStore, PGPSigner, KeyStore, Signer, SqliteAdapter } from 'crdt-meta';
import { Envelope, Syncable, ArgPair, PGPKeyStore, PGPSigner, KeyStore, Signer } from '@cicnet/crdt-meta';
import { SqliteAdapter } from '../src/db';
function createKeystore() {
const pksa = fs.readFileSync(__dirname + '/privatekeys.asc', 'utf-8');

View File

@@ -19,6 +19,7 @@
"include": [
"src/**/*",
"scripts/server/*",
"index.ts"
"index.ts",
"bin"
]
}

View File

@@ -9,3 +9,7 @@ class AlreadyInitializedError(Exception):
class PleaseCommitFirstError(Exception):
"""Raised when there exists uncommitted changes in the code while trying to build out the package."""
pass
class NotificationSendError(Exception):
"""Raised when a notification failed to due to some error as per the service responsible for dispatching the notification."""

View File

@@ -0,0 +1,19 @@
# standard imports
from enum import IntEnum
class AfricasTalkingStatusCodes(IntEnum):
PROCESSED = 100
SENT = 101
QUEUED = 102
RISK_HOLD = 401
INVALID_SENDER_ID = 402
INVALID_PHONE_NUMBER = 403
UNSUPPORTED_NUMBER_TYPE = 404
INSUFFICIENT_BALANCE = 405
USER_IN_BLACKLIST = 406
COULD_NOT_ROUTE = 407
INTERNAL_SERVER_ERROR = 500
GATEWAY_ERROR = 501
REJECTED_BY_GATEWAY = 502

View File

@@ -6,7 +6,8 @@ import celery
import africastalking
# local imports
from cic_notify.error import NotInitializedError, AlreadyInitializedError
from cic_notify.error import NotInitializedError, AlreadyInitializedError, NotificationSendError
from cic_notify.ext.enums import AfricasTalkingStatusCodes
logg = logging.getLogger()
celery_app = celery.current_app
@@ -50,10 +51,27 @@ class AfricasTalkingNotifier:
if self.sender_id:
response = self.api_client.send(message=message, recipients=[recipient], sender_id=self.sender_id)
logg.debug(f'Africastalking response sender-id {response}')
else:
response = self.api_client.send(message=message, recipients=[recipient])
logg.debug(f'africastalking response no-sender-id {response}')
recipients = response.get('Recipients')
if len(recipients) != 1:
status = response.get('SMSMessageData').get('Message')
raise NotificationSendError(f'Unexpected number of recipients: {len(recipients)}. Status: {status}')
status_code = recipients[0].get('statusCode')
status = recipients[0].get('status')
if status_code not in [
AfricasTalkingStatusCodes.PROCESSED.value,
AfricasTalkingStatusCodes.SENT.value,
AfricasTalkingStatusCodes.QUEUED.value
]:
raise NotificationSendError(f'Sending notification failed due to: {status}')
@celery_app.task
def send(message, recipient):

View File

@@ -9,7 +9,7 @@ import semver
logg = logging.getLogger()
version = (0, 4, 0, 'alpha.4')
version = (0, 4, 0, 'alpha.5')
version_object = semver.VersionInfo(
major=version[0],

View File

@@ -102,7 +102,7 @@ class MetadataRequestsHandler(Metadata):
'digest': json.loads(data).get('digest'),
}
}
formatted_data = json.dumps(formatted_data).encode('utf-8')
formatted_data = json.dumps(formatted_data)
result = make_request(method='PUT', url=self.url, data=formatted_data, headers=self.headers)
logg.info(f'signed metadata submission status: {result.status_code}.')
metadata_http_error_handler(result=result)
@@ -116,8 +116,10 @@ class MetadataRequestsHandler(Metadata):
"""This function is responsible for querying the metadata server for data corresponding to a unique pointer."""
result = make_request(method='GET', url=self.url)
metadata_http_error_handler(result=result)
response_data = result.content
data = json.loads(response_data.decode('utf-8'))
response_data = result.json()
data = json.loads(response_data)
if not isinstance(data, dict):
raise ValueError(f'Invalid data object: {data}.')
if result.status_code == 200 and self.cic_type == ':cic.person':
person = Person()
deserialized_person = person.deserialize(person_data=data)

View File

@@ -1,7 +1,7 @@
en:
account_successfully_created: |-
Hello, you have been registered on Sarafu Network! Your balance is %{balance} %{token_symbol}. To use dial *483*46#. For help 0757628885.
You have been registered on Sarafu Network! To use dial *384*96# on Safaricom and *483*96# on other networks. For help %{support_phone}.
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 https://www.grassrootseconomics.org/terms-and-conditions.
By using the service, you agree to the terms and conditions at http://grassecon.org/tos

View File

@@ -1,7 +1,7 @@
sw:
account_successfully_created: |-
Habari, umesajiliwa kwa huduma ya sarafu! Salio lako ni %{token_symbol} %{balance}. Kutumia bonyeza *483*46#. Kwa Usaidizi 0757628885.
Umesajiliwa kwa huduma ya Sarafu! Kutumia bonyeza *384*96# Safaricom ama *483*46# kwa utandao tofauti. Kwa Usaidizi %{support_phone}.
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 https://www.grassrootseconomics.org/terms-and-conditions.
Kwa kutumia hii huduma, umekubali sheria na masharti yafuatayo http://grassecon.org/tos

View File

@@ -1,29 +1,30 @@
en:
kenya:
initial_language_selection: |-
CON Welcome to Sarafu
CON Welcome to Sarafu Network
1. English
2. Kiswahili
3. Help
initial_pin_entry: |-
CON Please enter a PIN to manage your account.
CON Please enter a new four number PIN for your account.
0. Back
initial_pin_confirmation: |-
CON Enter your PIN again
CON Enter your four number PIN again
0. Back
enter_given_name: |-
CON Enter first name
0. Back
enter_family_name: |-
CON Enter last name
CON Enter family name
0. Back
enter_gender: |-
CON Enter gender
1. Male
2. Female
3. Other
0. Back
enter_location: |-
CON Enter location
CON Enter your location
0. Back
enter_products: |-
CON Please enter a product or service you offer
@@ -83,34 +84,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}
taxes: %{tax} %{token_symbol}
bonsuses: %{bonus} %{token_symbol}
fees: %{tax} %{token_symbol}
rewards: %{bonus} %{token_symbol}
0. Back
first_transaction_set: |-
CON %{first_transaction_set}
@@ -140,9 +141,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: |-
@@ -169,4 +170,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.

View File

@@ -1,12 +1,12 @@
sw:
kenya:
initial_language_selection: |-
CON Welcome to Sarafu
CON Karibu Sarafu Network
1. English
2. Kiswahili
3. Help
initial_pin_entry: |-
CON Tafadhali weka PIN ili kudhibiti akaunti yako.
CON Tafadhali weka pin mpya yenye nambari nne kwa akaunti yako
0. Nyuma
initial_pin_confirmation: |-
CON Weka PIN yako tena
@@ -21,12 +21,13 @@ sw:
CON Weka jinsia yako
1. Mwanaume
2. Mwanamke
3. Nyngine
0. Nyuma
enter_location: |-
CON Weka eneo lako
0. Nyuma
enter_products: |-
CON Tafadhali weka bidhaa ama huduma unauza
CON Weka bidhaa ama huduma unauza
0. Nyuma
start: |-
CON Salio %{account_balance} %{account_token_name}
@@ -155,7 +156,7 @@ sw:
99. Ondoka
exit_insufficient_balance: |-
CON Malipo ya %{amount} %{token_symbol} kwa %{recipient_information} halijakamilika kwa sababu salio lako haitoshi.
Akaunti yako ya Sarafu-Network ina salio ifuatayo: %{token_balance}
Akaunti yako ya Sarafu ina salio ifuatayo: %{token_balance}
00. Nyuma
99. Ondoka
invalid_service_code: |-
@@ -169,4 +170,4 @@ sw:
00. Nyuma
99. Ondoka
account_creation_prompt: |-
Akaunti yako ya Sarafu inatayarishwa. Utapokea ujumbe wa SMS akaunti yako ikiwa tayari.
Akaunti yako ya Sarafu inatayarishwa. Utapokea ujumbe wa SMS akaunti yako ikiwa tayari.

View File

@@ -87,7 +87,6 @@ COPY contract-migration/testdata/pgp testdata/pgp
COPY contract-migration/sarafu_declaration.json sarafu_declaration.json
COPY contract-migration/keystore keystore
COPY contract-migration/envlist .
COPY contract-migration/scripts scripts/
# A shared output dir for environment configs
RUN mkdir -p /tmp/cic/config

View File

@@ -0,0 +1,76 @@
# external imports
from chainlib.jsonrpc import JSONRPCException
from eth_erc20 import ERC20
from eth_accounts_index import AccountsIndex
from eth_token_index import TokenUniqueSymbolIndex
class ERC20Token:
def __init__(self, chain_spec, address, conn):
self.__address = address
c = ERC20(chain_spec)
o = c.symbol(address)
r = conn.do(o)
self.__symbol = c.parse_symbol(r)
o = c.decimals(address)
r = conn.do(o)
self.__decimals = c.parse_decimals(r)
def symbol(self):
return self.__symbol
def decimals(self):
return self.__decimals
class IndexCache:
def __init__(self, chain_spec, address):
self.address = address
self.chain_spec = chain_spec
def parse(self, r):
return r
def get(self, conn):
entries = []
i = 0
while True:
o = self.o.entry(self.address, i)
try:
r = conn.do(o)
entries.append(self.parse(r, conn))
except JSONRPCException:
return entries
i += 1
class AccountRegistryCache(IndexCache):
def __init__(self, chain_spec, address):
super(AccountRegistryCache, self).__init__(chain_spec, address)
self.o = AccountsIndex(chain_spec)
self.get_accounts = self.get
def parse(self, r, conn):
return self.o.parse_account(r)
class TokenRegistryCache(IndexCache):
def __init__(self, chain_spec, address):
super(TokenRegistryCache, self).__init__(chain_spec, address)
self.o = TokenUniqueSymbolIndex(chain_spec)
self.get_tokens = self.get
def parse(self, r, conn):
token_address = self.o.parse_entry(r)
return ERC20Token(self.chain_spec, token_address, conn)

View File

@@ -1,101 +0,0 @@
# standard imports
import os
import logging
import re
import sys
import json
# external imports
import redis
import celery
from chainsyncer.backend import MemBackend
from chainsyncer.driver import HeadSyncer
from chainlib.eth.connection import HTTPConnection
from chainlib.eth.gas import DefaultGasOracle
from chainlib.eth.nonce import DefaultNonceOracle
from chainlib.eth.block import block_latest
from hexathon import strip_0x
# local imports
import common
from cmd.traffic import (
TrafficItem,
TrafficRouter,
TrafficProvisioner,
TrafficSyncHandler,
)
from cmd.traffic import add_args as add_traffic_args
# common basics
script_dir = os.path.realpath(os.path.dirname(__file__))
logg = common.log.create()
argparser = common.argparse.create(script_dir, common.argparse.full_template)
argparser = common.argparse.add(argparser, add_traffic_args, 'traffic')
args = common.argparse.parse(argparser, logg)
config = common.config.create(args.c, args, args.env_prefix)
# map custom args to local config entries
batchsize = args.batch_size
if batchsize < 1:
batchsize = 1
logg.info('batch size {}'.format(batchsize))
config.add(batchsize, '_BATCH_SIZE', True)
config.add(args.redis_host_callback, '_REDIS_HOST_CALLBACK', True)
config.add(args.redis_port_callback, '_REDIS_PORT_CALLBACK', True)
config.add(args.y, '_KEYSTORE_FILE', True)
config.add(args.q, '_CELERY_QUEUE', True)
common.config.log(config)
def main():
# create signer (not currently in use, but needs to be accessible for custom traffic item generators)
(signer_address, signer) = common.signer.from_keystore(config.get('_KEYSTORE_FILE'))
# connect to celery
celery.Celery(broker=config.get('CELERY_BROKER_URL'), backend=config.get('CELERY_RESULT_URL'))
# set up registry
w3 = common.rpc.create(config.get('ETH_PROVIDER')) # replace with HTTPConnection when registry has been so refactored
registry = common.registry.init_legacy(config, w3)
# Connect to blockchain with chainlib
conn = HTTPConnection(config.get('ETH_PROVIDER'))
gas_oracle = DefaultGasOracle(conn)
nonce_oracle = DefaultNonceOracle(signer_address, conn)
# Set up magic traffic handler
traffic_router = TrafficRouter()
traffic_router.apply_import_dict(config.all(), config)
handler = TrafficSyncHandler(config, traffic_router)
# Set up syncer
syncer_backend = MemBackend(config.get('CIC_CHAIN_SPEC'), 0)
o = block_latest()
r = conn.do(o)
block_offset = int(strip_0x(r), 16) + 1
syncer_backend.set(block_offset, 0)
# Set up provisioner for common task input data
TrafficProvisioner.oracles['token']= common.registry.TokenOracle(w3, config.get('CIC_CHAIN_SPEC'), registry)
TrafficProvisioner.oracles['account'] = common.registry.AccountsOracle(w3, config.get('CIC_CHAIN_SPEC'), registry)
TrafficProvisioner.default_aux = {
'chain_spec': config.get('CIC_CHAIN_SPEC'),
'registry': registry,
'redis_host_callback': config.get('_REDIS_HOST_CALLBACK'),
'redis_port_callback': config.get('_REDIS_PORT_CALLBACK'),
'redis_db': config.get('REDIS_DB'),
'api_queue': config.get('_CELERY_QUEUE'),
}
syncer = HeadSyncer(syncer_backend, loop_callback=handler.refresh)
syncer.add_filter(handler)
syncer.loop(1, conn)
if __name__ == '__main__':
main()

View File

@@ -1,7 +0,0 @@
{
"dependencies": {
"cic-client-meta": "^0.0.7-alpha.8",
"crdt-meta": "0.0.8",
"vcard-parser": "^1.0.0"
}
}

View File

@@ -0,0 +1,21 @@
.data_seeding_variables:
variables:
APP_NAME: data-seeding
DOCKERFILE_PATH: $APP_NAME/docker/Dockerfile
.data_seeding_changes_target:
rules:
- changes:
- $CONTEXT/$APP_NAME/*
build-mr-data-seeding:
extends:
- .data_seeding_changes_target
- .py_build_merge_request
- .data_seeding_variables
build-push-data-seeding:
extends:
- .py_build_push
- .data_seeding_variables

View File

@@ -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`
@@ -128,12 +123,16 @@ The keystore file used for transferring external opening balances tracker is rel
All external balance transactions are saved in raw wire format in `<datadir>/txs`, with transaction hash as file name.
If the contract migrations have been executed with the default "giftable" token contract, then the `token_symbol` in the `import_balance` scripts should be set to `GFT`.
#### Alternative 1 - Sovereign wallet import - `eth`
First, make a note of the **block height** before running anything.
First, make a note of the **block height** before running anything:
`eth-info -p http://localhost:63545`
To import, run to _completion_:
@@ -143,7 +142,7 @@ After the script completes, keystore files for all generated accouts will be fou
Then run:
`python eth/import_balance.py -v -c config -r <cic_registry_address> -p <eth_provider> --offset <block_height_at_start> -y ../keystore/UTC--2021-01-08T17-18-44.521011372Z--eb3907ecad74a0013c259d5874ae7f22dcbcc95c <datadir>`
`python eth/import_balance.py -v -c config -r <cic_registry_address> -p <eth_provider> --token-symbol <token_symbol> --offset <block_height_at_start> -y ../keystore/UTC--2021-01-08T17-18-44.521011372Z--eb3907ecad74a0013c259d5874ae7f22dcbcc95c <datadir>`
@@ -151,7 +150,7 @@ Then run:
Run in sequence, in first terminal:
`python cic_eth/import_balance.py -v -c config -p <eth_provider> -r <cic_registry_address> -y ../keystore/UTC--2021-01-08T17-18-44.521011372Z--eb3907ecad74a0013c259d5874ae7f22dcbcc95c --head out`
`python cic_eth/import_balance.py -v -c config -p <eth_provider> -r <cic_registry_address> --token-symbol <token_symbol> -y ../keystore/UTC--2021-01-08T17-18-44.521011372Z--eb3907ecad74a0013c259d5874ae7f22dcbcc95c --head out`
In another terminal:
@@ -168,14 +167,40 @@ If you have previously run the `cic_ussd` import incompletely, it could be a goo
Then, in sequence, run in first terminal:
`python cic_eth/import_balance.py -v -c config -p <eth_provider> -r <cic_registry_address> -y ../keystore/UTC--2021-01-08T17-18-44.521011372Z--eb3907ecad74a0013c259d5874ae7f22dcbcc95c out`
`python cic_eth/import_balance.py -v -c config -p <eth_provider> -r <cic_registry_address> --token-symbol <token_symbol> -y ../keystore/UTC--2021-01-08T17-18-44.521011372Z--eb3907ecad74a0013c259d5874ae7f22dcbcc95c out`
In second terminal:
`python cic_ussd/import_users.py -v -c config out`
### Step 4 - Metadata import (optional)
The metadata import scripts can be run at any time after step 1 has been completed.
#### Importing user metadata
To import the main user metadata structs, run:
`node cic_meta/import_meta.js <datadir> <number_of_users>`
Monitors a folder for output from the `import_users.py` script, adding the metadata found to the `cic-meta` service.
If _number of users_ is omitted the script will run until manually interrupted.
#### Importing phone pointer
`node cic_meta/import_meta_phone.js <datadir> <number_of_users>`
If you imported using `cic_ussd`, the phone pointer is _already added_ and this script will do nothing.
##### 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:
@@ -199,29 +224,6 @@ The balance script is a celery task worker, and will not exit by itself in its c
The connection parameters for the `cic-ussd-server` is currently _hardcoded_ in the `import_users.py` script file.
### Step 4 - Metadata import (optional)
The metadata import scripts can be run at any time after step 1 has been completed.
#### Importing user metadata
To import the main user metadata structs, run:
`node cic_meta/import_meta.js <datadir> <number_of_users>`
Monitors a folder for output from the `import_users.py` script, adding the metadata found to the `cic-meta` service.
If _number of users_ is omitted the script will run until manually interrupted.
#### Importing phone pointer
`node cic_meta/import_meta_phone.js <datadir> <number_of_users>`
If you imported using `cic_ussd`, the phone pointer is _already added_ and this script will do nothing.
### Step 5 - Verify
`python verify.py -v -c config -r <cic_registry_address> -p <eth_provider> <datadir>`

View File

@@ -171,6 +171,7 @@ 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)

View File

@@ -163,9 +163,9 @@ class TrafficProvisioner:
"""Aux parameter template to be passed to the traffic generator module"""
def __init__(self):
self.tokens = self.oracles['token'].get_tokens()
self.accounts = self.oracles['account'].get_accounts()
def __init__(self, conn):
self.tokens = self.oracles['token'].get_tokens(conn)
self.accounts = self.oracles['account'].get_accounts(conn)
self.aux = copy.copy(self.default_aux)
self.__balances = {}
for a in self.accounts:
@@ -277,13 +277,14 @@ class TrafficSyncHandler:
:type traffic_router: TrafficRouter
:raises Exception: Any Exception redis may raise on connection attempt.
"""
def __init__(self, config, traffic_router):
def __init__(self, config, traffic_router, conn):
self.traffic_router = traffic_router
self.redis_channel = str(uuid.uuid4())
self.pubsub = self.__connect_redis(self.redis_channel, config)
self.traffic_items = {}
self.config = config
self.init = False
self.conn = conn
# connects to redis
@@ -307,7 +308,7 @@ class TrafficSyncHandler:
:param tx_index: Syncer block transaction index at time of call.
:type tx_index: number
"""
traffic_provisioner = TrafficProvisioner()
traffic_provisioner = TrafficProvisioner(self.conn)
traffic_provisioner.add_aux('redis_channel', self.redis_channel)
refresh_accounts = None
@@ -343,7 +344,7 @@ class TrafficSyncHandler:
sender = traffic_provisioner.accounts[sender_index]
#balance_full = balances[sender][token_pair[0].symbol()]
if len(sender_indices) == 1:
sender_indices[m] = sender_sender_indices[len(senders)-1]
sender_indices[sender_index] = sender_indices[len(sender_indices)-1]
sender_indices = sender_indices[:len(sender_indices)-1]
balance_full = traffic_provisioner.balance(sender, token_pair[0])
@@ -351,7 +352,14 @@ class TrafficSyncHandler:
recipient_index = random.randint(0, len(traffic_provisioner.accounts)-1)
recipient = traffic_provisioner.accounts[recipient_index]
logg.debug('trigger item {} tokens {} sender {} recipient {} balance {}')
logg.debug('trigger item {} tokens {} sender {} recipient {} balance {}'.format(
traffic_item,
token_pair,
sender,
recipient,
balance_full,
)
)
(e, t, balance_result,) = traffic_item.method(
token_pair,
sender,
@@ -359,7 +367,6 @@ class TrafficSyncHandler:
balance_full,
traffic_provisioner.aux,
block_number,
tx_index,
)
traffic_provisioner.update_balance(sender, token_pair[0], balance_result)
sender_indices.append(recipient_index)

View File

@@ -3,7 +3,7 @@ import logging
import copy
# external imports
from cic_registry import CICRegistry
from cic_registry.registry import Registry
from eth_token_index import TokenUniqueSymbolIndex
from eth_accounts_index import AccountRegistry
from chainlib.chain import ChainSpec

View File

@@ -3,7 +3,7 @@ import logging
# external imports
from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer
from crypto_dev_signer.keystore import DictKeystore
from crypto_dev_signer.keystore.dict import DictKeystore
logg = logging.getLogger(__name__)

View File

@@ -11,7 +11,7 @@ queue = 'cic-eth'
name = 'account'
def do(token_pair, sender, recipient, sender_balance, aux, block_number, tx_index):
def do(token_pair, sender, recipient, sender_balance, aux, block_number):
"""Triggers creation and registration of new account through the custodial cic-eth component.
It expects the following aux parameters to exist:

View File

@@ -5,7 +5,7 @@ logging.basicConfig(level=logging.WARNING)
logg = logging.getLogger()
def do(token_pair, sender, recipient, sender_balance, aux, block_number, tx_index):
def do(token_pair, sender, recipient, sender_balance, aux, block_number):
"""Defines the function signature for a traffic generator. The method itself only logs the input parameters.
If the error position in the return tuple is not None, the calling code should consider the generation as failed, and not count it towards the limit of simultaneous traffic items that can be simultaneously in flight.
@@ -26,12 +26,10 @@ def do(token_pair, sender, recipient, sender_balance, aux, block_number, tx_inde
:type aux: dict
:param block_number: Syncer block number position at time of method call
:type block_number: number
:param tx_index: Syncer block transaction index position at time of method call
:type tx_index: number
:raises KeyError: Missing required aux element
:returns: Exception|None, task_id|None and adjusted_sender_balance respectively
:rtype: tuple
"""
logg.debug('running {} {} {} {} {} {} {} {}'.format(__name__, token_pair, sender, recipient, sender_balance, aux, block_number, tx_index))
logg.debug('running {} {} {} {} {} {} {}'.format(__name__, token_pair, sender, recipient, sender_balance, aux, block_number))
return (None, None, sender_balance, )

View File

@@ -12,7 +12,7 @@ queue = 'cic-eth'
name = 'erc20_transfer'
def do(token_pair, sender, recipient, sender_balance, aux, block_number, tx_index):
def do(token_pair, sender, recipient, sender_balance, aux, block_number):
"""Triggers an ERC20 token transfer through the custodial cic-eth component, with a randomly chosen amount in integer resolution.
It expects the following aux parameters to exist:
@@ -33,7 +33,7 @@ def do(token_pair, sender, recipient, sender_balance, aux, block_number, tx_inde
balance_units = int(sender_balance_value / decimals)
if balance_units <= 0:
return (AttributeError('sender {} has zero balance'), None, 0,)
return (AttributeError('sender {} has zero balance ({} / {})'.format(sender, sender_balance_value, decimals)), None, 0,)
spend_units = random.randint(1, balance_units)
spend_value = spend_units * decimals

View File

@@ -0,0 +1,129 @@
# standard imports
import os
import logging
import re
import sys
import json
# external imports
import redis
import celery
from cic_eth_registry.registry import CICRegistry
from chainsyncer.backend.memory import MemBackend
from chainsyncer.driver import HeadSyncer
from chainlib.eth.connection import EthHTTPConnection
from chainlib.chain import ChainSpec
from chainlib.eth.gas import RPCGasOracle
from chainlib.eth.nonce import RPCNonceOracle
from chainlib.eth.block import block_latest
from hexathon import strip_0x
from cic_base import (
argparse,
config,
log,
rpc,
signer as signer_funcs,
)
# local imports
#import common
from cmd.traffic import (
TrafficItem,
TrafficRouter,
TrafficProvisioner,
TrafficSyncHandler,
)
from cmd.traffic import add_args as add_traffic_args
from cmd.cache import (
AccountRegistryCache,
TokenRegistryCache,
)
# common basics
script_dir = os.path.realpath(os.path.dirname(__file__))
logg = log.create()
argparser = argparse.create(script_dir, argparse.full_template)
argparser = argparse.add(argparser, add_traffic_args, 'traffic')
args = argparse.parse(argparser, logg)
config = config.create(args.c, args, args.env_prefix)
# map custom args to local config entries
batchsize = args.batch_size
if batchsize < 1:
batchsize = 1
logg.info('batch size {}'.format(batchsize))
config.add(batchsize, '_BATCH_SIZE', True)
config.add(args.redis_host_callback, '_REDIS_HOST_CALLBACK', True)
config.add(args.redis_port_callback, '_REDIS_PORT_CALLBACK', True)
config.add(args.y, '_KEYSTORE_FILE', True)
config.add(args.q, '_CELERY_QUEUE', True)
logg.debug(config)
chain_spec = ChainSpec.from_chain_str(config.get('CIC_CHAIN_SPEC'))
def main():
# create signer (not currently in use, but needs to be accessible for custom traffic item generators)
(signer_address, signer) = signer_funcs.from_keystore(config.get('_KEYSTORE_FILE'))
# connect to celery
celery.Celery(broker=config.get('CELERY_BROKER_URL'), backend=config.get('CELERY_RESULT_URL'))
# set up registry
rpc.setup(config.get('CIC_CHAIN_SPEC'), config.get('ETH_PROVIDER')) # replace with HTTPConnection when registry has been so refactored
conn = EthHTTPConnection(config.get('ETH_PROVIDER'))
#registry = registry.init_legacy(config, w3)
CICRegistry.address = config.get('CIC_REGISTRY_ADDRESS')
registry = CICRegistry(chain_spec, conn)
# Connect to blockchain with chainlib
gas_oracle = RPCGasOracle(conn)
nonce_oracle = RPCNonceOracle(signer_address, conn)
# Set up magic traffic handler
traffic_router = TrafficRouter()
traffic_router.apply_import_dict(config.all(), config)
handler = TrafficSyncHandler(config, traffic_router, conn)
# Set up syncer
syncer_backend = MemBackend(config.get('CIC_CHAIN_SPEC'), 0)
o = block_latest()
r = conn.do(o)
block_offset = int(strip_0x(r), 16) + 1
syncer_backend.set(block_offset, 0)
# get relevant registry entries
token_registry = registry.lookup('TokenRegistry')
logg.info('using token registry {}'.format(token_registry))
token_cache = TokenRegistryCache(chain_spec, token_registry)
account_registry = registry.lookup('AccountRegistry')
logg.info('using account registry {}'.format(account_registry))
account_cache = AccountRegistryCache(chain_spec, account_registry)
# Set up provisioner for common task input data
#TrafficProvisioner.oracles['token']= common.registry.TokenOracle(w3, config.get('CIC_CHAIN_SPEC'), registry)
#TrafficProvisioner.oracles['account'] = common.registry.AccountsOracle(w3, config.get('CIC_CHAIN_SPEC'), registry)
TrafficProvisioner.oracles['token'] = token_cache
TrafficProvisioner.oracles['account'] = account_cache
TrafficProvisioner.default_aux = {
'chain_spec': config.get('CIC_CHAIN_SPEC'),
'registry': registry,
'redis_host_callback': config.get('_REDIS_HOST_CALLBACK'),
'redis_port_callback': config.get('_REDIS_PORT_CALLBACK'),
'redis_db': config.get('REDIS_DB'),
'api_queue': config.get('_CELERY_QUEUE'),
}
syncer = HeadSyncer(syncer_backend, block_callback=handler.refresh)
syncer.add_filter(handler)
syncer.loop(1, conn)
if __name__ == '__main__':
main()

View File

@@ -2,8 +2,8 @@ const fs = require('fs');
const path = require('path');
const http = require('http');
const cic = require('cic-client-meta');
const crdt = require('crdt-meta');
const cic = require('@cicnet/cic-client-meta');
const crdt = require('@cicnet/crdt-meta');
//const conf = JSON.parse(fs.readFileSync('./cic.conf'));

View File

@@ -2,12 +2,12 @@ const fs = require('fs');
const path = require('path');
const http = require('http');
const cic = require('cic-client-meta');
const vcfp = require('vcard-parser');
const cic = require('@cicnet/cic-client-meta');
const crdt = require('@cicnet/crdt-meta');
//const conf = JSON.parse(fs.readFileSync('./cic.conf'));
const config = new cic.Config('./config');
const config = new crdt.Config('./config');
config.process();
console.log(config);
@@ -42,24 +42,18 @@ function sendit(uid, envelope) {
}
function doOne(keystore, filePath, identifier) {
const signer = new cic.PGPSigner(keystore);
const signer = new crdt.PGPSigner(keystore);
const o = JSON.parse(fs.readFileSync(filePath).toString());
//const b = Buffer.from(j['vcard'], 'base64');
//const s = b.toString();
//const o = vcfp.parse(s);
//const phone = o.tel[0].value;
//cic.Phone.toKey(phone).then((uid) => {
//const o = fs.readFileSync(filePath, 'utf-8');
const s = new cic.Syncable(identifier, o);
s.setSigner(signer);
s.onwrap = (env) => {
sendit(identifier, env);
};
s.sign();
//});
cic.Custom.toKey(identifier).then((uid) => {
const s = new crdt.Syncable(uid, o);
s.setSigner(signer);
s.onwrap = (env) => {
sendit(identifier, env);
};
s.sign();
});
}
const privateKeyPath = path.join(config.get('PGP_EXPORTS_DIR'), config.get('PGP_PRIVATE_KEY_FILE'));
@@ -67,7 +61,7 @@ const publicKeyPath = path.join(config.get('PGP_EXPORTS_DIR'), config.get('PGP_P
pk = fs.readFileSync(privateKeyPath);
pubk = fs.readFileSync(publicKeyPath);
new cic.PGPKeyStore(
new crdt.PGPKeyStore(
config.get('PGP_PASSPHRASE'),
pk,
pubk,
@@ -94,7 +88,7 @@ function importMetaCustom(keystore) {
err, files = fs.readdirSync(workDir);
} catch {
console.error('source directory not yet ready', workDir);
setTimeout(importMetaPhone, batchDelay, keystore);
setTimeout(importMetaCustom, batchDelay, keystore);
return;
}
let limit = batchSize;
@@ -128,7 +122,7 @@ function importMetaCustom(keystore) {
if (batchCount == batchSize) {
console.debug('reached batch size, breathing');
batchCount=0;
setTimeout(importMeta, batchDelay, keystore);
setTimeout(importMetaCustom, batchDelay, keystore);
return;
}
}

View File

@@ -2,9 +2,9 @@ const fs = require('fs');
const path = require('path');
const http = require('http');
const cic = require('cic-client-meta');
const cic = require('@cicnet/cic-client-meta');
const vcfp = require('vcard-parser');
const crdt = require('crdt-meta');
const crdt = require('@cicnet/crdt-meta');
//const conf = JSON.parse(fs.readFileSync('./cic.conf'));

View File

@@ -1,5 +1,5 @@
[pgp]
exports_dir = ../testdata/pgp
exports_dir = ../contract-migration/testdata/pgp
private_key_file = privatekeys_meta.asc
public_key_file = publickeys_meta.asc
passphrase = merman

View File

@@ -1,4 +1,4 @@
[traffic]
#local.noop_traffic = 2
local.account = 2
#local.account = 2
local.transfer = 2

View File

@@ -105,7 +105,7 @@ def genId(addr, typ):
def genDate():
ts = random.randint(ts_then, ts_now)
return datetime.datetime.fromtimestamp(ts).timestamp()
return int(datetime.datetime.fromtimestamp(ts).timestamp())
def genPhone():
@@ -193,6 +193,7 @@ 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')
@@ -223,6 +224,8 @@ if __name__ == '__main__':
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()

View File

@@ -0,0 +1,43 @@
# syntax = docker/dockerfile:1.2
FROM python:3.8.6-slim-buster as compile-image
RUN apt-get update
RUN apt-get install -y --no-install-recommends git gcc g++ libpq-dev gawk jq telnet wget openssl iputils-ping gnupg socat bash procps make python2 cargo
WORKDIR /root
RUN mkdir -vp /usr/local/etc/cic
COPY data-seeding/requirements.txt .
ARG EXTRA_INDEX_URL="https://pip.grassrootseconomics.net:8433"
RUN pip install --extra-index-url $EXTRA_INDEX_URL -r requirements.txt
# -------------- begin runtime container ----------------
FROM python:3.8.6-slim-buster as runtime-image
RUN apt-get update
RUN apt-get install -y --no-install-recommends gnupg libpq-dev
RUN apt-get install -y jq bash iputils-ping socat telnet dnsutils
COPY --from=compile-image /usr/local/bin/ /usr/local/bin/
COPY --from=compile-image /usr/local/etc/cic/ /usr/local/etc/cic/
COPY --from=compile-image /usr/local/lib/python3.8/site-packages/ \
/usr/local/lib/python3.8/site-packages/
WORKDIR root/
ENV EXTRA_INDEX_URL https://pip.grassrootseconomics.net:8433
# RUN useradd -u 1001 --create-home grassroots
# RUN adduser grassroots sudo && \
# echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
# WORKDIR /home/grassroots
COPY data-seeding/ .
# we copied these from the root build container.
# this is dumb though...I guess the compile image should have the same user
# RUN chown grassroots:grassroots -R /usr/local/lib/python3.8/site-packages/
# USER grassroots
ENTRYPOINT [ ]

View File

@@ -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 import AccountRegistry
from eth_accounts_index.registry 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

View File

@@ -5,10 +5,49 @@
"packages": {
"": {
"dependencies": {
"cic-client-meta": "0.0.7-alpha.6",
"@cicnet/cic-client-meta": "^0.0.11",
"@cicnet/crdt-meta": "^0.0.10",
"vcard-parser": "^1.0.0"
}
},
"node_modules/@cicnet/cic-client-meta": {
"version": "0.0.11",
"resolved": "https://registry.npmjs.org/@cicnet/cic-client-meta/-/cic-client-meta-0.0.11.tgz",
"integrity": "sha512-RL9CPXkWQBQzaqMoldnENC/xqinIMyWNSsejs9+qkFOWbAvC4inLdpjjCaooyvLpIZGHF9cLjxHiAdBMR9L8sQ==",
"dependencies": {
"@cicnet/crdt-meta": "^0.0.10",
"@ethereumjs/tx": "^3.0.0-beta.1",
"automerge": "^0.14.1",
"colors": "^1.4.0",
"ethereumjs-wallet": "^1.0.1",
"ini": "^1.3.8",
"openpgp": "^4.10.8",
"pg": "^8.4.2",
"sqlite3": "^5.0.0",
"yargs": "^16.1.0"
},
"bin": {
"meta-get": "bin/get.js",
"meta-set": "bin/set.js"
},
"engines": {
"node": ">=14.16.1"
}
},
"node_modules/@cicnet/crdt-meta": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/@cicnet/crdt-meta/-/crdt-meta-0.0.10.tgz",
"integrity": "sha512-f+H6BQA2tE718KuNYiNzrDJN4wY00zeuhXM6aPKJUX6nryzX9g2r0yf8iDhkz+Fts1R6M7Riz73MfFEa8fgvsw==",
"dependencies": {
"automerge": "^0.14.2",
"ini": "^1.3.8",
"openpgp": "^4.10.8",
"readline-sync": "^1.4.10"
},
"engines": {
"node": ">=14.16.1"
}
},
"node_modules/@ethereumjs/common": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.2.0.tgz",
@@ -317,24 +356,6 @@
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
},
"node_modules/cic-client-meta": {
"version": "0.0.7-alpha.6",
"resolved": "https://registry.npmjs.org/cic-client-meta/-/cic-client-meta-0.0.7-alpha.6.tgz",
"integrity": "sha512-oIN1aHkPHfsxJKDV6k4f1kX2tcppw3Q+D1b4BoPh0hYjNKNb7gImBMWnGsy8uiD9W6SNYE4sIXyrtct8mvrhsw==",
"dependencies": {
"@ethereumjs/tx": "^3.0.0-beta.1",
"automerge": "^0.14.1",
"ethereumjs-wallet": "^1.0.1",
"ini": "^1.3.5",
"openpgp": "^4.10.8",
"pg": "^8.4.2",
"sqlite3": "^5.0.0",
"yargs": "^16.1.0"
},
"engines": {
"node": "~14.16.1"
}
},
"node_modules/cipher-base": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
@@ -418,6 +439,14 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/colors": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
"integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
"engines": {
"node": ">=0.1.90"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -1555,6 +1584,14 @@
"node": ">= 6"
}
},
"node_modules/readline-sync": {
"version": "1.4.10",
"resolved": "https://registry.npmjs.org/readline-sync/-/readline-sync-1.4.10.tgz",
"integrity": "sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==",
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/request": {
"version": "2.88.2",
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
@@ -2085,6 +2122,34 @@
}
},
"dependencies": {
"@cicnet/cic-client-meta": {
"version": "0.0.11",
"resolved": "https://registry.npmjs.org/@cicnet/cic-client-meta/-/cic-client-meta-0.0.11.tgz",
"integrity": "sha512-RL9CPXkWQBQzaqMoldnENC/xqinIMyWNSsejs9+qkFOWbAvC4inLdpjjCaooyvLpIZGHF9cLjxHiAdBMR9L8sQ==",
"requires": {
"@cicnet/crdt-meta": "^0.0.10",
"@ethereumjs/tx": "^3.0.0-beta.1",
"automerge": "^0.14.1",
"colors": "^1.4.0",
"ethereumjs-wallet": "^1.0.1",
"ini": "^1.3.8",
"openpgp": "^4.10.8",
"pg": "^8.4.2",
"sqlite3": "^5.0.0",
"yargs": "^16.1.0"
}
},
"@cicnet/crdt-meta": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/@cicnet/crdt-meta/-/crdt-meta-0.0.10.tgz",
"integrity": "sha512-f+H6BQA2tE718KuNYiNzrDJN4wY00zeuhXM6aPKJUX6nryzX9g2r0yf8iDhkz+Fts1R6M7Riz73MfFEa8fgvsw==",
"requires": {
"automerge": "^0.14.2",
"ini": "^1.3.8",
"openpgp": "^4.10.8",
"readline-sync": "^1.4.10"
}
},
"@ethereumjs/common": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.2.0.tgz",
@@ -2112,9 +2177,9 @@
}
},
"@types/node": {
"version": "14.14.39",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.39.tgz",
"integrity": "sha512-Qipn7rfTxGEDqZiezH+wxqWYR8vcXq5LRpZrETD19Gs4o8LbklbmqotSUsMU+s5G3PJwMRDfNEYoxrcBwIxOuw=="
"version": "14.14.41",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.41.tgz",
"integrity": "sha512-dueRKfaJL4RTtSa7bWeTK1M+VH+Gns73oCgzvYfHZywRCoPSd8EkXBL0mZ9unPTveBn+D9phZBaxuzpwjWkW0g=="
},
"@types/pbkdf2": {
"version": "3.1.0",
@@ -2379,22 +2444,6 @@
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
},
"cic-client-meta": {
"version": "0.0.7-alpha.8",
"resolved": "https://registry.npmjs.org/cic-client-meta/-/cic-client-meta-0.0.7-alpha.8.tgz",
"integrity": "sha512-NtU4b4dptG2gsKXIvAv1xCxxxhrr801tb8+Co1O+VLx+wvxFyPRxqa2f2eN5nrSnFnljNsWWpE6K5bJZb1+Rqw==",
"requires": {
"@ethereumjs/tx": "^3.0.0-beta.1",
"automerge": "^0.14.1",
"crdt-meta": "0.0.8",
"ethereumjs-wallet": "^1.0.1",
"ini": "^1.3.8",
"openpgp": "^4.10.8",
"pg": "^8.4.2",
"sqlite3": "^5.0.0",
"yargs": "^16.1.0"
}
},
"cipher-base": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
@@ -2462,6 +2511,11 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"colors": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
"integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA=="
},
"combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -2495,18 +2549,6 @@
"printj": "~1.1.0"
}
},
"crdt-meta": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/crdt-meta/-/crdt-meta-0.0.8.tgz",
"integrity": "sha512-CS0sS0L2QWthz7vmu6vzl3p4kcpJ+IKILBJ4tbgN4A3iNG8wnBeuDIv/z3KFFQjcfuP4QAh6E9LywKUTxtDc3g==",
"requires": {
"automerge": "^0.14.2",
"ini": "^1.3.8",
"openpgp": "^4.10.8",
"pg": "^8.5.1",
"sqlite3": "^5.0.2"
}
},
"create-hash": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
@@ -3415,6 +3457,11 @@
"util-deprecate": "^1.0.1"
}
},
"readline-sync": {
"version": "1.4.10",
"resolved": "https://registry.npmjs.org/readline-sync/-/readline-sync-1.4.10.tgz",
"integrity": "sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw=="
},
"request": {
"version": "2.88.2",
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",

View File

@@ -0,0 +1,7 @@
{
"dependencies": {
"@cicnet/cic-client-meta": "^0.0.11",
"@cicnet/crdt-meta": "^0.0.10",
"vcard-parser": "^1.0.0"
}
}

View File

@@ -1,5 +1,5 @@
cic-base[full_graph]==0.1.2b9
cic-base[full_graph]==0.1.2b11
sarafu-faucet==0.0.3a3
cic-eth==0.11.0b13
cic-eth==0.11.0b14
cic-types==0.1.0a11
crypto-dev-signer==0.4.14b3

View File

@@ -72,6 +72,7 @@ argparser.add_argument('--ussd-provider', type=str, dest='ussd_provider', defaul
argparser.add_argument('--skip-custodial', dest='skip_custodial', action='store_true', help='skip all custodial verifications')
argparser.add_argument('--exclude', action='append', type=str, default=[], help='skip specified verification')
argparser.add_argument('--include', action='append', type=str, help='include specified verification')
argparser.add_argument('--token-symbol', default='SRF', type=str, dest='token_symbol', help='Token symbol to use for trnsactions')
argparser.add_argument('-r', '--registry-address', type=str, dest='r', help='CIC Registry address')
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('-x', '--exit-on-error', dest='x', action='store_true', help='Halt exection on error')
@@ -101,6 +102,8 @@ config.censor('PASSWORD', 'SSL')
config.add(args.meta_provider, '_META_PROVIDER', True)
config.add(args.ussd_provider, '_USSD_PROVIDER', True)
token_symbol = args.token_symbol
logg.debug('config loaded from {}:\n{}'.format(config_dir, config))
celery_app = celery.Celery(backend=config.get('CELERY_RESULT_URL'), broker=config.get('CELERY_BROKER_URL'))
@@ -273,7 +276,10 @@ class Verifier:
def verify_balance(self, address, balance):
o = self.erc20_tx_factory.balance(self.token_address, address)
r = self.conn.do(o)
actual_balance = int(strip_0x(r), 16)
try:
actual_balance = int(strip_0x(r), 16)
except ValueError:
actual_balance = int(r)
balance = int(balance / 1000000) * 1000000
logg.debug('balance for {}: {}'.format(address, balance))
if balance != actual_balance:
@@ -461,7 +467,7 @@ def main():
tx = txf.template(ZERO_ADDRESS, token_index_address)
data = add_0x(registry_addressof_method)
h = hashlib.new('sha256')
h.update(b'SRF')
h.update(token_symbol.encode('utf-8'))
z = h.digest()
data += eth_abi.encode_single('bytes32', z).hex()
txf.set_code(tx, data)

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