Compare commits

..

5 Commits

39 changed files with 202 additions and 3049 deletions

5
.gitignore vendored
View File

@@ -8,8 +8,3 @@ gmon.out
*.egg-info
dist/
build/
**/*sqlite
**/.nyc_output
**/coverage
**/.venv
.idea

View File

@@ -194,7 +194,6 @@ def main():
except UnknownContractError as e:
logg.exception('Registry contract connection failed for {}: {}'.format(config.get('CIC_REGISTRY_ADDRESS'), e))
sys.exit(1)
logg.info('connected contract registry {}'.format(config.get('CIC_REGISTRY_ADDRESS')))
trusted_addresses_src = config.get('CIC_TRUST_ADDRESS')
if trusted_addresses_src == None:

View File

@@ -18,8 +18,8 @@ chainlib~=0.0.3rc2
hexathon~=0.0.1a7
chainsyncer[sql]~=0.0.2a5
chainqueue~=0.0.2b3
sarafu-faucet~=0.0.3a3
erc20-faucet~=0.2.1a5
sarafu-faucet==0.0.3a3
erc20-faucet==0.2.1a4
coincurve==15.0.0
potaahto~=0.0.1a2
pycryptodome==3.10.1

View File

@@ -3,5 +3,3 @@ dist
dist-web
dist-server
scratch
coverage
.nyc_output

View File

@@ -3,38 +3,17 @@
variables:
APP_NAME: cic-meta
DOCKERFILE_PATH: $APP_NAME/docker/Dockerfile
IMAGE_TAG: $CI_REGISTRY_IMAGE/$APP_NAME:unittest-$CI_COMMIT_SHORT_SHA
.cic_meta_changes_target:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
# - changes:
# - $CONTEXT/$APP_NAME/*
- when: always
- changes:
- $CONTEXT/$APP_NAME/*
cic-meta-build-mr:
stage: build
extends:
- .cic_meta_variables
- .cic_meta_changes_target
script:
- mkdir -p /kaniko/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > "/kaniko/.docker/config.json"
# - /kaniko/executor --context $CONTEXT --dockerfile $DOCKERFILE_PATH $KANIKO_CACHE_ARGS --destination $IMAGE_TAG
- /kaniko/executor --context $CONTEXT --dockerfile $DOCKERFILE_PATH $KANIKO_CACHE_ARGS --destination $IMAGE_TAG
test-mr-cic-meta:
build-mr-cic-meta:
extends:
- .cic_meta_variables
- .cic_meta_changes_target
stage: test
image: $IMAGE_TAG
script:
- cd /tmp/src/cic-meta
- npm install --dev
- npm run test
- npm run test:coverage
needs: ["cic-meta-build-mr"]
- .py_build_merge_request
- .cic_meta_variables
build-push-cic-meta:
extends:

View File

@@ -4,28 +4,29 @@ WORKDIR /tmp/src/cic-meta
RUN apk add --no-cache postgresql bash
# required to build the cic-client-meta module
COPY cic-meta/package.json \
./
COPY cic-meta/src/ src/
COPY cic-meta/tests/ tests/
COPY cic-meta/scripts/ scripts/
# copy the dependencies
COPY cic-meta/package.json .
COPY cic-meta/tsconfig.json .
COPY cic-meta/webpack.config.js .
RUN npm install
COPY cic-meta/tests/ tests/
# see exports_dir gpg.ini
COPY cic-meta/tests/*.asc /root/pgp/
RUN alias tsc=node_modules/typescript/bin/tsc
# copy runtime configs
COPY cic-meta/.config/ /usr/local/etc/cic-meta/
# COPY cic-meta/scripts/server/initdb/server.postgres.sql /usr/local/share/cic-meta/sql/server.sql
# db migrations
COPY cic-meta/docker/db.sh ./db.sh
RUN chmod 755 ./db.sh
RUN alias tsc=node_modules/typescript/bin/tsc
#RUN alias ts-node=/tmp/src/cic-meta/node_modules/ts-node/dist/bin.js
#ENTRYPOINT [ "./node_modules/ts-node/dist/bin.js", "./scripts/server/server.ts" ]
COPY cic-meta/docker/start_server.sh ./start_server.sh
RUN chmod 755 ./start_server.sh
ENTRYPOINT ["sh", "./start_server.sh"]

File diff suppressed because it is too large Load Diff

View File

@@ -11,7 +11,6 @@
"preferGlobal": true,
"scripts": {
"test": "mocha -r node_modules/node-localstorage/register -r ts-node/register tests/*.ts",
"test:coverage": "nyc mocha tests/*.ts --timeout 3000 --check-coverage=true",
"build": "node_modules/typescript/bin/tsc -d --outDir dist src/index.ts",
"build-server": "tsc -d --outDir dist-server scripts/server/*.ts",
"pack": "node_modules/typescript/bin/tsc -d --outDir dist && webpack",
@@ -35,9 +34,7 @@
"devDependencies": {
"@types/mocha": "^8.0.3",
"mocha": "^8.2.0",
"nock": "^13.1.0",
"node-localstorage": "^2.1.6",
"nyc": "^15.1.0",
"ts-node": "^9.0.0",
"typescript": "^4.0.5",
"webpack": "^5.4.0",
@@ -53,26 +50,5 @@
"license": "GPL-3.0-or-later",
"engines": {
"node": ">=14.16.1"
},
"nyc": {
"include": [
"src/**/*.ts"
],
"extension": [
".ts"
],
"require": [
"ts-node/register"
],
"reporter": [
"text",
"html"
],
"sourceMap": true,
"instrument": true,
"branches": ">80",
"lines": ">80",
"functions": ">80",
"statements": ">80"
}
}

View File

@@ -204,7 +204,7 @@ async function processRequest(req, res) {
}
if (content === undefined) {
console.error('empty content', data);
console.error('empty onctent', data);
res.writeHead(400, {"Content-Type": "text/plain"});
res.end();
return;

View File

@@ -9,7 +9,7 @@ class Custom extends Syncable implements Addressable {
super('', v);
Custom.toKey(name).then((cid) => {
this.id = cid;
this.name = name;
this.value = v;
});
}

View File

@@ -100,15 +100,13 @@ class Meta {
identifier = await User.toKey(name);
} else if (type === 'phone') {
identifier = await Phone.toKey(name);
} else if (type === 'custom') {
identifier = await Custom.toKey(name);
} else {
identifier = await Custom.toKey(name, type);
identifier = await Custom.toKey(name);
}
return identifier;
}
wrap(syncable: Syncable): Promise<Envelope> {
private wrap(syncable: Syncable): Promise<Envelope> {
return new Promise<Envelope>(async (resolve, reject) => {
syncable.setSigner(this.signer);
syncable.onwrap = async (env) => {

View File

@@ -1,49 +0,0 @@
import * as assert from 'assert';
import {Custom} from "../src";
const testName = 'areas';
const testObject = {
area: ['Nairobi', 'Mombasa', 'Kilifi']
}
const testNameKey = '8f3da0c90ba2b89ff217da96f6088cbaf987a1b58bc33c3a5e526e53cec7cfed';
const testIdentifier = ':cic.area'
const testIdentifierKey = 'da6194e6f33726546e82c328df4c120b844d6427859156518bd600765bf8b2b7';
describe('custom', () => {
context('with predefined data', () => {
it('should create a custom object', () => {
const custom = new Custom(testName, testObject);
setTimeout(() => {
assert.strictEqual(custom.name, testName);
assert.deepStrictEqual(custom.m.data, testObject);
assert.strictEqual(custom.key(), testNameKey)
}, 0);
});
});
context('without predefined data', () => {
it('should create a custom object', () => {
const custom = new Custom(testName);
setTimeout(() => {
assert.strictEqual(custom.name, testName);
assert.deepStrictEqual(custom.m.data, {});
assert.strictEqual(custom.key(), testNameKey)
}, 0);
});
});
describe('#toKey()', () => {
context('without a custom identifier', () => {
it('should generate a key from the custom name', async () => {
assert.strictEqual(await Custom.toKey(testName), testNameKey);
});
});
context('with a custom identifier', () => {
it('should generate a key from the custom name with a custom identifier', async () => {
assert.strictEqual(await Custom.toKey(testName, testIdentifier), testIdentifierKey);
});
});
});
});

View File

@@ -1,176 +0,0 @@
import * as assert from 'assert';
import * as fs from 'fs';
const nock = require('nock');
import {Meta} from "../src";
import {getResponse, metaData, networkErrorResponse, notFoundResponse, putResponse} from "./response";
import {Syncable} from "@cicnet/crdt-meta";
const metaUrl = 'https://meta.dev.grassrootseconomics.net';
const testAddress = '0xc1912fee45d61c87cc5ea59dae31190fffff232d';
const testAddressKey = 'a51472cb4df63b199a4de01335b1b4d1bbee27ff4f03340aa1d592f26c6acfe2';
const testPhone = '+254123456789';
const testPhoneKey = 'be3cc8212b7eb57c6217ddd42230bd8ccd2f01382bf8c1c77d3a683fa5a9bb16';
const testName = 'areas'
const testNameKey = '8f3da0c90ba2b89ff217da96f6088cbaf987a1b58bc33c3a5e526e53cec7cfed';
const testIdentifier = ':cic.area'
const testIdentifierKey = 'da6194e6f33726546e82c328df4c120b844d6427859156518bd600765bf8b2b7';
function readFile(filename) {
if(!fs.existsSync(filename)) {
console.error(`File ${filename} not found`);
return;
}
return fs.readFileSync(filename, {encoding: 'utf8', flag: 'r'});
}
const privateKey = readFile('./privatekeys.asc');
describe('meta', () => {
beforeEach(() => {
nock(metaUrl)
.get(`/${testAddressKey}`)
.reply(200, getResponse);
nock(metaUrl)
.get(`/${testPhoneKey}`)
.reply(200, getResponse);
nock(metaUrl)
.get(`/${testAddress}`)
.reply(404);
nock(metaUrl)
.get(`/${testIdentifier}`)
.replyWithError(networkErrorResponse);
nock(metaUrl)
.put(`/${testAddressKey}`)
.reply(200, putResponse);
nock(metaUrl)
.put(`/${testAddress}`)
.reply(404);
nock(metaUrl)
.post('/post')
.reply(500);
});
describe('#get()', () => {
it('should fetch data from the meta service', async () => {
const account = await Meta.get(testAddressKey, metaUrl);
assert.strictEqual(account.toJSON(account), getResponse.payload);
});
context('if item is not found', () => {
it('should respond with an error', async () => {
const account = await Meta.get(testAddress, metaUrl);
assert.strictEqual(account, `404: Not Found`);
});
});
context('in case of network error', () => {
it('should respond with an error', async () => {
const account = await Meta.get(testIdentifier, metaUrl);
assert.strictEqual(account, `Request to ${metaUrl}/${testIdentifier} failed. Connection error.`);
});
});
})
describe('#set()', () => {
context('object data', () => {
it('should set data to the meta server', () => {
const meta = new Meta(metaUrl, privateKey);
meta.onload = async (status) => {
const response = await meta.set(testAddressKey, metaData);
assert.strictEqual(response, `${putResponse.status}: ${putResponse.statusText}`);
}
});
});
context('string data', () => {
it('should set data to the meta server', () => {
const meta = new Meta(metaUrl, privateKey);
meta.onload = async (status) => {
const response = await meta.set(testPhoneKey, testAddress);
assert.strictEqual(response, `${putResponse.status}: ${putResponse.statusText}`);
}
});
});
context('in case of network error', () => {
it('should respond with an error', () => {
const meta = new Meta(metaUrl, privateKey);
meta.onload = async (status) => {
const response = await meta.set(testIdentifier, metaData);
assert.strictEqual(response, `Request to ${metaUrl}/${testIdentifier} failed. Connection error.`);
}
});
});
});
describe('#updateMeta()', () => {
it('should update data in the meta server', async () => {
const syncable = new Syncable(testAddressKey, metaData);
const meta = new Meta(metaUrl, privateKey);
meta.onload = async (status) => {
const response = await meta.updateMeta(syncable, testAddressKey);
assert.strictEqual(response, putResponse);
}
});
context('if item is not found', () => {
it('should respond with an error', () => {
const syncable = new Syncable(testAddress, metaData);
const meta = new Meta(metaUrl, privateKey);
meta.onload = async (status) => {
const response = await meta.updateMeta(syncable, testAddress);
assert.strictEqual(response, notFoundResponse);
}
});
});
});
describe('#wrap()', () => {
it('should sign a syncable object', function () {
const syncable = new Syncable(testAddressKey, metaData);
const meta = new Meta(metaUrl, privateKey);
meta.onload = async (status) => {
const response = await meta.wrap(syncable);
assert.strictEqual(response.toJSON(), getResponse);
}
});
})
describe('#getIdentifier()', () => {
context('without type', () => {
it('should return an identifier', async () => {
assert.strictEqual(await Meta.getIdentifier(testName), testNameKey);
});
});
context('with user type', () => {
it('should return an identifier', async () => {
assert.strictEqual(await Meta.getIdentifier(testAddress, 'user'), testAddressKey);
});
});
context('with phone type', () => {
it('should return an identifier', async () => {
assert.strictEqual(await Meta.getIdentifier(testPhone, 'phone'), testPhoneKey);
});
});
context('with custom type', () => {
it('should return an identifier', async () => {
assert.strictEqual(await Meta.getIdentifier(testName, 'custom'), testNameKey);
});
});
context('with unrecognised type', () => {
it('should return an identifier', async () => {
assert.strictEqual(await Meta.getIdentifier(testName, testIdentifier), testIdentifierKey);
});
});
});
});

View File

@@ -1,24 +0,0 @@
import * as assert from 'assert';
import {Phone} from "../src";
const testAddress = '0xc1912fee45d61c87cc5ea59dae31190fffff232d';
const testPhone = '+254123456789';
const testPhoneKey = 'be3cc8212b7eb57c6217ddd42230bd8ccd2f01382bf8c1c77d3a683fa5a9bb16';
describe('phone', () => {
it('should create a phone object', () => {
const phone = new Phone(testAddress, testPhone);
setTimeout(() => {
assert.strictEqual(phone.address, testAddress);
assert.strictEqual(phone.m.data.msisdn, testPhone);
assert.strictEqual(phone.key(), testPhoneKey)
}, 0);
});
describe('#toKey()', () => {
it('should generate a key from the phone number', async () => {
assert.strictEqual(await Phone.toKey(testPhone), testPhoneKey);
});
});
});

File diff suppressed because one or more lines are too long

View File

@@ -1,54 +0,0 @@
import * as assert from 'assert';
import { User } from "../src";
const testAddress = '0xc1912fee45d61c87cc5ea59dae31190fffff232d';
const testAddressKey = 'a51472cb4df63b199a4de01335b1b4d1bbee27ff4f03340aa1d592f26c6acfe2';
const testUser = {
user: {
firstName: 'Test',
lastName: 'User'
}
}
describe('user', () => {
context('without predefined data', () => {
it('should create a user object', () => {
const user = new User(testAddress);
setTimeout(() => {
assert.strictEqual(user.address, testAddress);
assert.strictEqual(user.key(), testAddressKey);
assert.strictEqual(user.m.data.user.firstName, '');
assert.strictEqual(user.m.data.user.lastName, '');
}, 0);
});
});
context('with predefined data', () => {
it('should create a user object', () => {
const user = new User(testAddress, testUser);
setTimeout(() => {
assert.strictEqual(user.address, testAddress);
assert.strictEqual(user.key(), testAddressKey);
assert.strictEqual(user.m.data.user.firstName, testUser.user.firstName);
assert.strictEqual(user.m.data.user.lastName, testUser.user.lastName);
}, 0);
});
});
describe('#setName()', () => {
it('should set user\'s names to metadata', () => {
const user = new User(testAddress);
user.setName(testUser.user.firstName, testUser.user.lastName);
assert.strictEqual(user.m.data.user.firstName, testUser.user.firstName);
assert.strictEqual(user.m.data.user.lastName, testUser.user.lastName);
});
});
describe('#toKey()', () => {
it('should generate a key from the user\'s address', async () => {
assert.strictEqual(await User.toKey(testAddress), testAddressKey);
});
});
});

View File

@@ -2,7 +2,7 @@
"compilerOptions": {
"baseUrl": ".",
"outDir": "./dist.browser",
"target": "es2015",
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"lib": ["es2016", "dom", "es5"],

View File

@@ -5,7 +5,6 @@ LOCALE_PATH=/usr/src/cic-ussd/var/lib/locale/
MAX_BODY_LENGTH=1024
PASSWORD_PEPPER=QYbzKff6NhiQzY3ygl2BkiKOpER8RE/Upqs/5aZWW+I=
SERVICE_CODE=*483*46#,*483*061#,*384*96#
SUPPORT_PHONE_NUMBER=0757628885
[phone_number]
REGION=KE

View File

@@ -5,7 +5,6 @@ LOCALE_PATH=var/lib/locale/
MAX_BODY_LENGTH=1024
PASSWORD_PEPPER=QYbzKff6NhiQzY3ygl2BkiKOpER8RE/Upqs/5aZWW+I=
SERVICE_CODE=*483*46#
SUPPORT_PHONE_NUMBER=0757628885
[ussd]
MENU_FILE=/usr/local/lib/python3.8/site-packages/cic_ussd/db/ussd_menu.json

View File

@@ -41,7 +41,3 @@ def get_user_by_phone_number(phone_number: str) -> Optional[Account]:
phone_number = process_phone_number(phone_number=phone_number, region='KE')
user = Account.session.query(Account).filter_by(phone_number=phone_number).first()
return user
class Support:
phone_number = None

View File

@@ -19,7 +19,7 @@ from cic_ussd.db.models.ussd_session import UssdSession
from cic_ussd.error import MetadataNotFoundError, SeppukuError
from cic_ussd.menu.ussd_menu import UssdMenu
from cic_ussd.metadata import blockchain_address_to_metadata_pointer
from cic_ussd.phone_number import get_user_by_phone_number, Support
from cic_ussd.phone_number import get_user_by_phone_number
from cic_ussd.redis import cache_data, create_cached_data_key, get_cached_data
from cic_ussd.state_machine import UssdStateMachine
from cic_ussd.conversions import to_wei, from_wei
@@ -270,24 +270,7 @@ def process_display_user_metadata(user: Account, display_key: str):
products=products
)
else:
# TODO [Philip]: All these translations could be moved to translation files.
logg.warning(f'Expected person metadata but found none in cache for key: {key}')
absent = ''
if user.preferred_language == 'en':
absent = 'Not provided'
elif user.preferred_language == 'sw':
absent = 'Haijawekwa'
return translation_for(
key=display_key,
preferred_language=user.preferred_language,
full_name=absent,
gender=absent,
location=absent,
products=absent
)
raise MetadataNotFoundError(f'Expected person metadata but found none in cache for key: {key}')
def process_account_statement(user: Account, display_key: str, ussd_session: dict):
@@ -423,6 +406,12 @@ def process_request(user_input: str, user: Account, ussd_session: Optional[dict]
:return: A ussd menu's corresponding text value.
:rtype: Document
"""
# retrieve metadata before any transition
key = generate_metadata_pointer(
identifier=blockchain_address_to_metadata_pointer(blockchain_address=user.blockchain_address),
cic_type=':cic.person'
)
person_metadata = get_cached_data(key=key)
if ussd_session:
if user_input == "0":
@@ -446,7 +435,7 @@ def process_request(user_input: str, user: Account, ussd_session: Optional[dict]
'exit_pin_mismatch',
'exit_invalid_request',
'exit_successful_transaction'
]:
] and person_metadata is not None:
return UssdMenu.find_by_name(name='start')
else:
return UssdMenu.find_by_name(name=last_state)
@@ -478,14 +467,6 @@ def next_state(ussd_session: dict, user: Account, user_input: str) -> str:
return new_state
def process_exit_invalid_menu_option(display_key: str, preferred_language: str):
return translation_for(
key=display_key,
preferred_language=preferred_language,
support_phone=Support.phone_number
)
def custom_display_text(
display_key: str,
menu_name: str,
@@ -522,7 +503,5 @@ def custom_display_text(
return process_account_statement(display_key=display_key, user=user, ussd_session=ussd_session)
elif menu_name == 'display_user_metadata':
return process_display_user_metadata(display_key=display_key, user=user)
elif menu_name == 'exit_invalid_menu_option':
return process_exit_invalid_menu_option(display_key=display_key, preferred_language=user.preferred_language)
else:
return translation_for(key=display_key, preferred_language=user.preferred_language)

View File

@@ -26,7 +26,7 @@ from cic_ussd.metadata.base import Metadata
from cic_ussd.operations import (define_response_with_content,
process_menu_interaction_requests,
define_multilingual_responses)
from cic_ussd.phone_number import process_phone_number, Support
from cic_ussd.phone_number import process_phone_number
from cic_ussd.processor import get_default_token_data
from cic_ussd.redis import cache_data, create_cached_data_key, InMemoryStore
from cic_ussd.requests import (get_request_endpoint,
@@ -126,8 +126,6 @@ else:
valid_service_codes = config.get('APP_SERVICE_CODE').split(",")
Support.phone_number = config.get('APP_SUPPORT_PHONE_NUMBER')
def application(env, start_response):
"""Loads python code for application to be accessible over web server
@@ -145,7 +143,7 @@ def application(env, start_response):
if get_request_method(env=env) == 'POST' and get_request_endpoint(env=env) == '/':
if env.get('CONTENT_TYPE') != 'application/x-www-form-urlencoded':
start_response('405 Urlencoded, please', errors_headers)
start_response('405 Play by the rules', errors_headers)
return []
post_data = env.get('wsgi.input').read()
@@ -213,9 +211,6 @@ def application(env, start_response):
return [response_bytes]
else:
logg.error('invalid query {}'.format(env))
for r in env:
logg.debug('{}: {}'.format(r, env))
start_response('405 Play by the rules', errors_headers)
return []

View File

@@ -11,7 +11,7 @@ from cic_ussd.balance import BalanceManager, compute_operational_balance
from cic_ussd.chain import Chain
from cic_ussd.db.models.account import AccountStatus, Account
from cic_ussd.operations import save_to_in_memory_ussd_session_data
from cic_ussd.phone_number import get_user_by_phone_number, process_phone_number
from cic_ussd.phone_number import get_user_by_phone_number
from cic_ussd.processor import retrieve_token_symbol
from cic_ussd.redis import create_cached_data_key, get_cached_data
from cic_ussd.transactions import OutgoingTransactionProcessor
@@ -30,7 +30,7 @@ def is_valid_recipient(state_machine_data: Tuple[str, dict, Account]) -> bool:
"""
user_input, ussd_session, user = state_machine_data
recipient = get_user_by_phone_number(phone_number=user_input)
is_not_initiator = process_phone_number(user_input, 'KE') != user.phone_number
is_not_initiator = user_input != user.phone_number
has_active_account_status = user.get_account_status() == AccountStatus.ACTIVE.name
return is_not_initiator and has_active_account_status and recipient is not None

View File

@@ -1,4 +1,4 @@
cic_base[full_graph]~=0.1.2b17
cic-eth~=0.11.0b17
cic_base[full_graph]~=0.1.2b14
cic-eth~=0.11.0b15
cic-notify~=0.4.0a5
cic-types~=0.1.0a10

View File

@@ -36,12 +36,26 @@
"source": "initial_pin_entry",
"dest": "exit_invalid_pin"
},
{
"trigger": "scan_data",
"source": "initial_pin_confirmation",
"dest": "start",
"conditions": [
"cic_ussd.state_machine.logic.pin.pins_match",
"cic_ussd.state_machine.logic.validator.has_cached_user_metadata"
],
"after": [
"cic_ussd.state_machine.logic.pin.complete_pin_change",
"cic_ussd.state_machine.logic.user.get_user_metadata",
"cic_ussd.state_machine.logic.user.update_account_status_to_active"
]
},
{
"trigger": "scan_data",
"source": "initial_pin_confirmation",
"unless": "cic_ussd.state_machine.logic.validator.has_cached_user_metadata",
"conditions": "cic_ussd.state_machine.logic.pin.pins_match",
"dest": "start",
"dest": "enter_given_name",
"after": [
"cic_ussd.state_machine.logic.pin.complete_pin_change",
"cic_ussd.state_machine.logic.user.update_account_status_to_active"

View File

@@ -51,13 +51,19 @@ ENV PATH $NVM_DIR/versions/node//v$NODE_VERSION/bin:$PATH
# WORKDIR /home/grassroots
# USER grassroots
COPY contract-migration/requirements.txt .
ARG pip_extra_args=""
ARG pip_index_url=https://pypi.org/simple
ARG pip_extra_index_url=https://pip.grassrootseconomics.net:8433
RUN pip install --index-url https://pypi.org/simple \
--extra-index-url $pip_extra_index_url -r requirements.txt
ARG cic_base_version=0.1.2b15
ARG cic_eth_version=0.11.0b16
ARG sarafu_token_version=0.0.1a8
ARG sarafu_faucet_version=0.0.3a3
RUN pip install --index-url https://pypi.org/simple --extra-index-url $pip_extra_index_url \
cic-base[full_graph]==$cic_base_version \
cic-eth==$cic_eth_version \
sarafu-faucet==$sarafu_faucet_version \
sarafu-token==$sarafu_token_version \
cic-eth==$cic_eth_version
# -------------- begin runtime container ----------------
FROM python:3.8.6-slim-buster as runtime-image

View File

@@ -1,4 +0,0 @@
cic-base[full_graph]==0.1.2b15
sarafu-faucet==0.0.3a3
sarafu-token==0.0.1a8
cic-eth==0.11.0b16

View File

@@ -76,7 +76,6 @@ if [[ -n "${ETH_PROVIDER}" ]]; then
>&2 echo "add deployer address as account index writer"
eth-accounts-index-writer $gas_price_arg -y $DEV_ETH_KEYSTORE_FILE -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -a $DEV_ACCOUNT_INDEX_ADDRESS -ww -vv $debug $DEV_ETH_ACCOUNT_CONTRACT_DEPLOYER
>&2 echo "deploy contract registry contract"
CIC_REGISTRY_ADDRESS=`eth-contract-registry-deploy $gas_price_arg -i $CIC_CHAIN_SPEC -y $DEV_ETH_KEYSTORE_FILE --identifier BancorRegistry --identifier AccountRegistry --identifier TokenRegistry --identifier AddressDeclarator --identifier Faucet --identifier TransferAuthorization -p $ETH_PROVIDER -vv -w`
eth-contract-registry-set $gas_price_arg -w -y $DEV_ETH_KEYSTORE_FILE -r $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -vv ContractRegistry $CIC_REGISTRY_ADDRESS
eth-contract-registry-set $gas_price_arg -w -y $DEV_ETH_KEYSTORE_FILE -r $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -vv AccountRegistry $DEV_ACCOUNT_INDEX_ADDRESS
@@ -88,7 +87,7 @@ if [[ -n "${ETH_PROVIDER}" ]]; then
eth-contract-registry-set $gas_price_arg -w -y $DEV_ETH_KEYSTORE_FILE -r $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -vv AddressDeclarator $DEV_DECLARATOR_ADDRESS
# Deploy transfer authorization contact
>&2 echo "deploy transfer auth contract"
>&2 echo "deploy address declarator contract"
DEV_TRANSFER_AUTHORIZATION_ADDRESS=`erc20-transfer-auth-deploy $gas_price_arg -y $DEV_ETH_KEYSTORE_FILE -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -w -vv`
eth-contract-registry-set $gas_price_arg -w -y $DEV_ETH_KEYSTORE_FILE -r $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -vv TransferAuthorization $DEV_TRANSFER_AUTHORIZATION_ADDRESS

View File

@@ -47,7 +47,7 @@ EOF
>&2 echo "create account for gas gifter"
old_gas_provider=$DEV_ETH_ACCOUNT_GAS_PROVIDER
DEV_ETH_ACCOUNT_GAS_GIFTER=`cic-eth-create --timeout 120 $debug --redis-host-callback=$REDIS_HOST --redis-port-callback=$REDIS_PORT --no-register`
DEV_ETH_ACCOUNT_GAS_GIFTER=`cic-eth-create $debug --redis-host-callback=$REDIS_HOST --redis-port-callback=$REDIS_PORT --no-register`
echo DEV_ETH_ACCOUNT_GAS_GIFTER=$DEV_ETH_ACCOUNT_GAS_GIFTER >> $env_out_file
cic-eth-tag -i $CIC_CHAIN_SPEC GAS_GIFTER $DEV_ETH_ACCOUNT_GAS_GIFTER

View File

@@ -136,7 +136,7 @@ First, make a note of the **block height** before running anything:
To import, run to _completion_:
`python eth/import_users.py -v -c config -p <eth_provider> -r <cic_registry_address> -y ../contract-migration/keystore/UTC--2021-01-08T17-18-44.521011372Z--eb3907ecad74a0013c259d5874ae7f22dcbcc95c <datadir>`
`python eth/import_users.py -v -c config -p <eth_provider> -r <cic_registry_address> -y ../keystore/UTC--2021-01-08T17-18-44.521011372Z--eb3907ecad74a0013c259d5874ae7f22dcbcc95c <datadir>`
After the script completes, keystore files for all generated accouts will be found in `<datadir>/keystore`, all with `foo` as password (would set it empty, but believe it or not some interfaces out there won't work unless you have one).
@@ -150,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> --token-symbol <token_symbol> -y ../contract-migration/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:
@@ -226,7 +226,7 @@ The connection parameters for the `cic-ussd-server` is currently _hardcoded_ in
### Step 5 - Verify
`python verify.py -v -c config -r <cic_registry_address> -p <eth_provider> --token-symbol <token_symbol> <datadir>`
`python verify.py -v -c config -r <cic_registry_address> -p <eth_provider> <datadir>`
Included checks:
* Private key is in cic-eth keystore
@@ -259,8 +259,4 @@ Should exit with code 0 if all input data is found in the respective services.
- Running the balance script should be _optional_ in all cases, but is currently required in the case of `cic_ussd` because it is needed to generate the metadata. An improvement would be moving the task to `import_users.py`, for a different queue than the balance tx handler.
- MacOS BigSur issue when installing psycopg2: ld: library not found for -lssl -> https://github.com/psycopg/psycopg2/issues/1115#issuecomment-831498953
- `cic_ussd` imports is poorly implemented, and consumes a lot of resources. Therefore it takes a long time to complete. Reducing the amount of polls for the phone pointer would go a long way to improve it.
- A strict constraint is maintained insistin the use of postgresql-12.

View File

@@ -114,7 +114,7 @@ def main():
conn = EthHTTPConnection(config.get('ETH_PROVIDER'))
ImportTask.balance_processor = BalanceProcessor(conn, chain_spec, config.get('CIC_REGISTRY_ADDRESS'), signer_address, signer)
ImportTask.balance_processor.init(token_symbol)
ImportTask.balance_processor.init()
# TODO get decimals from token
balances = {}
@@ -139,7 +139,6 @@ def main():
ImportTask.balances = balances
ImportTask.count = i
ImportTask.import_dir = user_dir
s = celery.signature(
'import_task.send_txs',

View File

@@ -39,7 +39,6 @@ elif args.vv:
config_dir = args.c
config = confini.Config(config_dir, os.environ.get('CONFINI_ENV_PREFIX'))
config.process()
logg.debug('config loaded from {}:\n{}'.format(args.c, config))
celery_app = celery.Celery(broker=config.get('CELERY_BROKER_URL'), backend=config.get('CELERY_RESULT_URL'))
@@ -63,6 +62,9 @@ def main():
)
s_import_pins.apply_async()
argv = ['worker', '-Q', 'cic-import-ussd', '--loglevel=DEBUG']
celery_app.worker_main(argv)
if __name__ == '__main__':
main()

View File

@@ -1,21 +1,29 @@
# standard imports
import argparse
import json
import logging
import os
import sys
import json
import logging
import argparse
import uuid
import datetime
import time
import urllib.request
import uuid
from urllib.parse import urlencode
from glob import glob
# external imports
import celery
import confini
import phonenumbers
# third-party imports
import redis
from chainlib.chain import ChainSpec
import confini
import celery
from hexathon import (
add_0x,
strip_0x,
)
from chainlib.eth.address import to_checksum
from cic_types.models.person import Person
from cic_eth.api.api_task import Api
from chainlib.chain import ChainSpec
from cic_types.processor import generate_metadata_pointer
import phonenumbers
logging.basicConfig(level=logging.WARNING)
logg = logging.getLogger()
@@ -79,13 +87,21 @@ 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):
url = 'http'
if ssl:
url += 's'
url += '://{}:{}'.format(host, port)
url += '/?username={}&password={}'.format(username, password)
url += '/?username={}&password={}'.format(username, password) #config.get('USSD_USER'), config.get('USSD_PASS'))
logg.info('ussd service url {}'.format(url))
logg.info('ussd phone {}'.format(phone))
@@ -98,10 +114,9 @@ def build_ussd_request(phone, host, port, service_code, username, password, ssl=
'text': service_code,
}
req = urllib.request.Request(url)
req.method=('POST')
data_str = urlencode(data)
data_str = json.dumps(data)
data_bytes = data_str.encode('utf-8')
req.add_header('Content-Type', 'application/x-www-form-urlencoded')
req.add_header('Content-Type', 'application/json')
req.data = data_bytes
return req

View File

@@ -31,9 +31,9 @@ elif args.vv:
config_dir = args.c
config = Config(config_dir, os.environ.get('CONFINI_ENV_PREFIX'))
config.process()
logg.debug('config loaded from {}:\n{}'.format(args.c, config))
ussd_data_dir = os.path.join(args.user_dir, 'ussd')
user_old_dir = os.path.join(args.user_dir, 'old')
os.stat(user_old_dir)
db_configs = {
'database': config.get('DATABASE_NAME'),
@@ -45,15 +45,18 @@ db_configs = {
celery_app = celery.Celery(broker=config.get('CELERY_BROKER_URL'), backend=config.get('CELERY_RESULT_URL'))
if __name__ == '__main__':
for x in os.walk(ussd_data_dir):
for x in os.walk(user_old_dir):
for y in x[2]:
if y[len(y) - 5:] == '.json':
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)
logg.debug(f'LOADING USSD DATA: {ussd_data}')
except json.decoder.JSONDecodeError as e:
f.close()
logg.error('load error for {}: {}'.format(y, e))

View File

@@ -6,7 +6,7 @@ from eth_contract_registry import Registry
from eth_token_index import TokenUniqueSymbolIndex
from chainlib.eth.gas import OverrideGasOracle
from chainlib.eth.nonce import OverrideNonceOracle
from eth_erc20 import ERC20
from chainlib.eth.erc20 import ERC20
from chainlib.eth.tx import (
count,
TxFormat,
@@ -37,7 +37,7 @@ class BalanceProcessor:
self.value_multiplier = 1
def init(self, token_symbol):
def init(self):
# Get Token registry address
registry = Registry(self.chain_spec)
o = registry.address_of(self.registry_address, 'TokenRegistry')
@@ -46,10 +46,10 @@ class BalanceProcessor:
logg.info('found token index address {}'.format(self.token_index_address))
token_registry = TokenUniqueSymbolIndex(self.chain_spec)
o = token_registry.address_of(self.token_index_address, token_symbol)
o = token_registry.address_of(self.token_index_address, 'SRF')
r = self.conn.do(o)
self.token_address = token_registry.parse_address_of(r)
logg.info('found {} token address {}'.format(token_symbol, self.token_address))
logg.info('found SRF token address {}'.format(self.token_address))
tx_factory = ERC20(self.chain_spec)
o = tx_factory.decimals(self.token_address)

View File

@@ -3,7 +3,6 @@ import argparse
import json
import logging
import os
import uuid
# third-party imports
import bcrypt
@@ -84,7 +83,7 @@ if __name__ == '__main__':
phone_object = phonenumbers.parse(u.tel)
phone = phonenumbers.format_number(phone_object, phonenumbers.PhoneNumberFormat.E164)
password_hash = uuid.uuid4().hex
password_hash = generate_password_hash()
pins_file.write(f'{phone},{password_hash}\n')
logg.info(f'Writing phone: {phone}, password_hash: {password_hash}')

View File

@@ -1,5 +1,5 @@
{
"name": "data-seeding",
"name": "scripts",
"lockfileVersion": 2,
"requires": true,
"packages": {
@@ -49,20 +49,20 @@
}
},
"node_modules/@ethereumjs/common": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.3.0.tgz",
"integrity": "sha512-Fmi15MdVptsC85n6NcUXIFiiXCXWEfZNgPWP+OGAQOC6ZtdzoNawtxH/cYpIgEgSuIzfOeX3VKQP/qVI1wISHg==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.2.0.tgz",
"integrity": "sha512-PyQiTG00MJtBRkJmv46ChZL8u2XWxNBeAthznAUIUiefxPAXjbkuiCZOuncgJS34/XkMbNc9zMt/PlgKRBElig==",
"dependencies": {
"crc-32": "^1.2.0",
"ethereumjs-util": "^7.0.10"
"ethereumjs-util": "^7.0.9"
}
},
"node_modules/@ethereumjs/tx": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.2.0.tgz",
"integrity": "sha512-D3X/XtZ3ldUg34hr99Jvj7NxW3NxVKdUKrwQnEWlAp4CmCQpvYoyn7NF4lk34rHEt7ScS+Agu01pcDHoOcd19A==",
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.1.3.tgz",
"integrity": "sha512-DJBu6cbwYtiPTFeCUR8DF5p+PF0jxs+0rALJZiEcTz2tiRPIEkM72GEbrkGuqzENLCzBrJHT43O0DxSYTqeo+g==",
"dependencies": {
"@ethereumjs/common": "^2.3.0",
"@ethereumjs/common": "^2.2.0",
"ethereumjs-util": "^7.0.10"
}
},
@@ -75,9 +75,9 @@
}
},
"node_modules/@types/node": {
"version": "15.12.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.0.tgz",
"integrity": "sha512-+aHJvoCsVhO2ZCuT4o5JtcPrCPyDE3+1nvbDprYes+pPkEsbjH7AGUCNtjMOXS0fqH14t+B7yLzaqSz92FPWyw=="
"version": "14.14.41",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.41.tgz",
"integrity": "sha512-dueRKfaJL4RTtSa7bWeTK1M+VH+Gns73oCgzvYfHZywRCoPSd8EkXBL0mZ9unPTveBn+D9phZBaxuzpwjWkW0g=="
},
"node_modules/@types/pbkdf2": {
"version": "3.1.0",
@@ -115,10 +115,6 @@
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/ansi-regex": {
@@ -138,9 +134,6 @@
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/aproba": {
@@ -810,9 +803,9 @@
}
},
"node_modules/glob": {
"version": "7.1.7",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
"integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -823,9 +816,6 @@
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/graceful-fs": {
@@ -846,7 +836,6 @@
"version": "5.1.5",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
"integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
"deprecated": "this library is no longer supported",
"optional": true,
"dependencies": {
"ajv": "^6.12.3",
@@ -920,9 +909,9 @@
}
},
"node_modules/ignore-walk": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.4.tgz",
"integrity": "sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ==",
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz",
"integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==",
"dependencies": {
"minimatch": "^3.0.4"
}
@@ -1048,7 +1037,6 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.1.tgz",
"integrity": "sha512-epq90L9jlFWCW7+pQa6JOnKn2Xgl2mtI664seYR6MHskvI9agt7AnDqmAlp9TqU4/caMYbA08Hi5DMZAl5zdkA==",
"hasInstallScript": true,
"dependencies": {
"node-addon-api": "^2.0.0",
"node-gyp-build": "^4.2.0"
@@ -1068,21 +1056,21 @@
}
},
"node_modules/mime-db": {
"version": "1.48.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz",
"integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==",
"version": "1.47.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz",
"integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==",
"optional": true,
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.31",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz",
"integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==",
"version": "2.1.30",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz",
"integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==",
"optional": true,
"dependencies": {
"mime-db": "1.48.0"
"mime-db": "1.47.0"
},
"engines": {
"node": ">= 0.6"
@@ -1227,7 +1215,6 @@
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz",
"integrity": "sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==",
"deprecated": "Please upgrade to @mapbox/node-pre-gyp: the non-scoped node-pre-gyp package is deprecated and only the @mapbox scoped package will recieve updates in the future",
"dependencies": {
"detect-libc": "^1.0.2",
"mkdirp": "^0.5.1",
@@ -1286,9 +1273,9 @@
}
},
"node_modules/npm-bundled": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz",
"integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz",
"integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==",
"dependencies": {
"npm-normalize-package-bin": "^1.0.1"
}
@@ -1439,14 +1426,6 @@
},
"engines": {
"node": ">= 8.0.0"
},
"peerDependencies": {
"pg-native": ">=2.0.0"
},
"peerDependenciesMeta": {
"pg-native": {
"optional": true
}
}
},
"node_modules/pg-connection-string": {
@@ -1465,10 +1444,7 @@
"node_modules/pg-pool": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.3.0.tgz",
"integrity": "sha512-0O5huCql8/D6PIRFAlmccjphLYWC+JIzvUhSzXSpGaf+tjTZc4nn+Lr7mLXBbFJfvwbP0ywDv73EiaBsxn7zdg==",
"peerDependencies": {
"pg": ">=8.0"
}
"integrity": "sha512-0O5huCql8/D6PIRFAlmccjphLYWC+JIzvUhSzXSpGaf+tjTZc4nn+Lr7mLXBbFJfvwbP0ywDv73EiaBsxn7zdg=="
},
"node_modules/pg-protocol": {
"version": "1.5.0",
@@ -1620,7 +1596,6 @@
"version": "2.88.2",
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
"integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
"deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142",
"optional": true,
"dependencies": {
"aws-sign2": "~0.7.0",
@@ -1695,21 +1670,7 @@
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
},
"node_modules/safer-buffer": {
"version": "2.1.2",
@@ -1730,7 +1691,6 @@
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.2.tgz",
"integrity": "sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg==",
"hasInstallScript": true,
"dependencies": {
"elliptic": "^6.5.2",
"node-addon-api": "^2.0.0",
@@ -1795,27 +1755,18 @@
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.0.2.tgz",
"integrity": "sha512-1SdTNo+BVU211Xj1csWa8lV6KM0CtucDwRyA0VHl91wEH1Mgh7RxUpI4rVvG7OhHrzCSGaVyW5g8vKvlrk9DJA==",
"hasInstallScript": true,
"dependencies": {
"node-addon-api": "^3.0.0",
"node-pre-gyp": "^0.11.0"
},
"optionalDependencies": {
"node-gyp": "3.x"
},
"peerDependencies": {
"node-gyp": "3.x"
},
"peerDependenciesMeta": {
"node-gyp": {
"optional": true
}
}
},
"node_modules/sqlite3/node_modules/node-addon-api": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz",
"integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A=="
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.1.0.tgz",
"integrity": "sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw=="
},
"node_modules/sshpk": {
"version": "1.16.1",
@@ -1833,6 +1784,11 @@
"safer-buffer": "^2.0.2",
"tweetnacl": "~0.14.0"
},
"bin": {
"sshpk-conv": "bin/sshpk-conv",
"sshpk-sign": "bin/sshpk-sign",
"sshpk-verify": "bin/sshpk-verify"
},
"engines": {
"node": ">=0.10.0"
}
@@ -1916,16 +1872,12 @@
"node_modules/transit-immutable-js": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/transit-immutable-js/-/transit-immutable-js-0.7.0.tgz",
"integrity": "sha1-mT4lCJtjEf9AIUD1VidtbSUwBdk=",
"peerDependencies": {
"immutable": ">= 3",
"transit-js": ">= 0.8"
}
"integrity": "sha1-mT4lCJtjEf9AIUD1VidtbSUwBdk="
},
"node_modules/transit-js": {
"version": "0.8.874",
"resolved": "https://registry.npmjs.org/transit-js/-/transit-js-0.8.874.tgz",
"integrity": "sha512-IDJJGKRzUbJHmN0P15HBBa05nbKor3r2MmG6aSt0UxXIlJZZKcddTk67/U7WyAeW9Hv/VYI02IqLzolsC4sbPA==",
"version": "0.8.867",
"resolved": "https://registry.npmjs.org/transit-js/-/transit-js-0.8.867.tgz",
"integrity": "sha512-rOwB4K0z/WZ+E2bV42iN9UV3mvGzmwSv/IpMOKdnFpawPAZT0d1L7f91Y+tZQF7lXSDGk+oln4XyIQXo+pyTGA==",
"engines": {
"node": ">= 0.10.0"
}
@@ -1971,7 +1923,6 @@
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
"bin": {
"uuid": "bin/uuid"
}
@@ -2026,9 +1977,6 @@
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/wrap-ansi/node_modules/ansi-regex": {
@@ -2203,20 +2151,20 @@
}
},
"@ethereumjs/common": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.3.0.tgz",
"integrity": "sha512-Fmi15MdVptsC85n6NcUXIFiiXCXWEfZNgPWP+OGAQOC6ZtdzoNawtxH/cYpIgEgSuIzfOeX3VKQP/qVI1wISHg==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.2.0.tgz",
"integrity": "sha512-PyQiTG00MJtBRkJmv46ChZL8u2XWxNBeAthznAUIUiefxPAXjbkuiCZOuncgJS34/XkMbNc9zMt/PlgKRBElig==",
"requires": {
"crc-32": "^1.2.0",
"ethereumjs-util": "^7.0.10"
"ethereumjs-util": "^7.0.9"
}
},
"@ethereumjs/tx": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.2.0.tgz",
"integrity": "sha512-D3X/XtZ3ldUg34hr99Jvj7NxW3NxVKdUKrwQnEWlAp4CmCQpvYoyn7NF4lk34rHEt7ScS+Agu01pcDHoOcd19A==",
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.1.3.tgz",
"integrity": "sha512-DJBu6cbwYtiPTFeCUR8DF5p+PF0jxs+0rALJZiEcTz2tiRPIEkM72GEbrkGuqzENLCzBrJHT43O0DxSYTqeo+g==",
"requires": {
"@ethereumjs/common": "^2.3.0",
"@ethereumjs/common": "^2.2.0",
"ethereumjs-util": "^7.0.10"
}
},
@@ -2229,9 +2177,9 @@
}
},
"@types/node": {
"version": "15.12.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.0.tgz",
"integrity": "sha512-+aHJvoCsVhO2ZCuT4o5JtcPrCPyDE3+1nvbDprYes+pPkEsbjH7AGUCNtjMOXS0fqH14t+B7yLzaqSz92FPWyw=="
"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",
@@ -2874,9 +2822,9 @@
}
},
"glob": {
"version": "7.1.7",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
"integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -2961,9 +2909,9 @@
}
},
"ignore-walk": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.4.tgz",
"integrity": "sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ==",
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz",
"integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==",
"requires": {
"minimatch": "^3.0.4"
}
@@ -3089,18 +3037,18 @@
}
},
"mime-db": {
"version": "1.48.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz",
"integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==",
"version": "1.47.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz",
"integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==",
"optional": true
},
"mime-types": {
"version": "2.1.31",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz",
"integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==",
"version": "2.1.30",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz",
"integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==",
"optional": true,
"requires": {
"mime-db": "1.48.0"
"mime-db": "1.47.0"
}
},
"minimalistic-assert": {
@@ -3261,9 +3209,9 @@
}
},
"npm-bundled": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz",
"integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz",
"integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==",
"requires": {
"npm-normalize-package-bin": "^1.0.1"
}
@@ -3402,8 +3350,7 @@
"pg-pool": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.3.0.tgz",
"integrity": "sha512-0O5huCql8/D6PIRFAlmccjphLYWC+JIzvUhSzXSpGaf+tjTZc4nn+Lr7mLXBbFJfvwbP0ywDv73EiaBsxn7zdg==",
"requires": {}
"integrity": "sha512-0O5huCql8/D6PIRFAlmccjphLYWC+JIzvUhSzXSpGaf+tjTZc4nn+Lr7mLXBbFJfvwbP0ywDv73EiaBsxn7zdg=="
},
"pg-protocol": {
"version": "1.5.0",
@@ -3663,9 +3610,9 @@
},
"dependencies": {
"node-addon-api": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz",
"integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A=="
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.1.0.tgz",
"integrity": "sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw=="
}
}
},
@@ -3749,13 +3696,12 @@
"transit-immutable-js": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/transit-immutable-js/-/transit-immutable-js-0.7.0.tgz",
"integrity": "sha1-mT4lCJtjEf9AIUD1VidtbSUwBdk=",
"requires": {}
"integrity": "sha1-mT4lCJtjEf9AIUD1VidtbSUwBdk="
},
"transit-js": {
"version": "0.8.874",
"resolved": "https://registry.npmjs.org/transit-js/-/transit-js-0.8.874.tgz",
"integrity": "sha512-IDJJGKRzUbJHmN0P15HBBa05nbKor3r2MmG6aSt0UxXIlJZZKcddTk67/U7WyAeW9Hv/VYI02IqLzolsC4sbPA=="
"version": "0.8.867",
"resolved": "https://registry.npmjs.org/transit-js/-/transit-js-0.8.867.tgz",
"integrity": "sha512-rOwB4K0z/WZ+E2bV42iN9UV3mvGzmwSv/IpMOKdnFpawPAZT0d1L7f91Y+tZQF7lXSDGk+oln4XyIQXo+pyTGA=="
},
"tunnel-agent": {
"version": "0.6.0",

View File

@@ -9,7 +9,6 @@ import sys
import urllib
import urllib.request
import uuid
import urllib.parse
# external imports
import celery
@@ -73,7 +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='GFT', type=str, dest='token_symbol', help='Token symbol to use for trnsactions')
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')
@@ -186,9 +185,9 @@ def send_ussd_request(address, data_dir):
}
req = urllib.request.Request(config.get('_USSD_PROVIDER'))
urlencoded_data = urllib.parse.urlencode(data)
data_bytes = urlencoded_data.encode('utf-8')
req.add_header('Content-Type', 'application/x-www-form-urlencoded')
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')
@@ -389,9 +388,10 @@ class Verifier:
def verify_ussd_pins(self, address, balance):
response_data = send_ussd_request(address, self.data_dir)
if response_data[:11] != 'CON Balance' and response_data[:9] != 'CON Salio':
if response_data[:11] != 'CON Balance':
raise VerifierError(response_data, 'pins')
def verify(self, address, balance, debug_stem=None):
for k in active_tests:

View File

@@ -484,7 +484,7 @@ services:
restart_policy:
condition: on-failure
volumes:
- ./apps/contract-migration/testdata/pgp/:/tmp/cic/pgp
- ${LOCAL_VOLUME_DIR:-/tmp/cic}/pgp:/tmp/cic/pgp
# command: "/root/start_server.sh -vv"
cic-user-ussd-server: