Compare commits

..

21 Commits

Author SHA1 Message Date
nolash
e52db0de35 Do not callback when none in token proofs check 2021-10-14 15:33:54 +02:00
nolash
cc58735ff1 Do not link null callbacks for token proof check 2021-10-14 15:31:58 +02:00
nolash
4b8eae41f7 Allow empty proofs in token info task 2021-10-14 12:22:41 +02:00
nolash
8d571b51c5 More deps upgrades 2021-10-14 05:28:17 +02:00
nolash
13970ec332 bump version cic-eth 2021-10-13 15:14:55 +02:00
nolash
7e6cc5714d Allow empty proof spec for tokens api 2021-10-13 15:13:43 +02:00
nolash
0f5bceb95b Bump version cic-eth 2021-10-13 14:24:29 +02:00
nolash
159fd07d63 Revert "Another attempt at config files, dockerfile change"
This reverts commit 9d3f8bf1e3.
2021-10-09 17:22:23 +02:00
nolash
9d3f8bf1e3 Another attempt at config files, dockerfile change 2021-10-09 17:12:29 +02:00
nolash
2229154c2b Add documentation 2021-10-09 16:55:05 +02:00
nolash
9637cb61f8 Add magic arg parsing for token lookup api 2021-10-09 16:21:03 +02:00
nolash
66f94ae694 Correct test default token task in unit test 2021-10-09 15:06:21 +02:00
nolash
1e65ea8511 reinstate celery seesion worker in token metadata test 2021-10-09 14:38:16 +02:00
nolash
21972e9df5 Complete token lookups with proofs 2021-10-09 14:18:10 +02:00
nolash
77fe41da4b Revert to multi-token task queue entry point for token api call 2021-10-09 13:39:33 +02:00
nolash
6098374a4e POC single token task chain for token proof lookups 2021-10-09 13:22:14 +02:00
nolash
f648d3ee68 WIP token info proof verify 2021-10-09 00:48:20 +02:00
nolash
7e2857d1e7 Add separate generic task for proof verification 2021-10-08 20:19:15 +02:00
nolash
df21db958b Move token tasks to cic_eth.eth.erc20 2021-10-08 19:21:47 +02:00
nolash
6ccffb15b6 Add trust check to token fetch in cic-eth task 2021-10-08 18:39:22 +02:00
nolash
53e9c63698 Add token api metadata getter (first without okota) 2021-10-08 16:04:13 +02:00
24 changed files with 239 additions and 92 deletions

View File

@@ -1,5 +1,4 @@
celery==4.4.7
erc20-demurrage-token~=0.0.5a3
cic-eth-registry~=0.6.1a5
chainlib~=0.0.9rc3
cic_eth~=0.12.4a9
erc20-demurrage-token~=0.0.3a1
cic-eth-registry>=0.6.1a2,<0.7.0
cic-eth[services]~=0.12.4a8

View File

@@ -3,4 +3,3 @@ cic-eth-registry>=0.6.1a5,<0.7.0
hexathon~=0.0.1a8
chainqueue>=0.0.4a6,<0.1.0
eth-erc20>=0.1.2a2,<0.2.0
chainlib-eth>=0.0.10a2,<0.1.0

View File

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

View File

@@ -1,3 +1,4 @@
celery==4.4.7
chainlib>=0.0.10a3,<0.1.0
chainlib-eth>=0.0.9rc4,<0.1.0
semver==2.13.0
crypto-dev-signer>=0.4.15rc2,<0.5.0

View File

@@ -13,5 +13,4 @@ erc20-faucet>=0.3.2a2,<0.4.0
erc20-transfer-authorization>=0.3.5a2,<0.4.0
sarafu-faucet>=0.0.7a2,<0.1.0
moolb~=0.1.1b2
chainlib-eth>=0.0.10a2,<0.1.0
okota>=0.2.4a6,<0.3.0

View File

@@ -1,7 +1,7 @@
chainqueue>=0.0.6a1,<0.1.0
crypto-dev-signer>=0.4.15rc2,<=0.4.15
chainqueue>=0.0.5a1,<0.1.0
cic-eth-registry>=0.6.1a5,<0.7.0
redis==3.5.3
hexathon~=0.0.1a8
pycryptodome==3.10.1
pyxdg==0.27
chainlib-eth>=0.0.10a2,<0.1.0

View File

@@ -3,6 +3,7 @@
# external imports
# local imports
from .base import Metadata
from .custom import CustomMetadata
from .person import PersonMetadata
from .phone import PhonePointerMetadata

View File

@@ -1,30 +1,99 @@
# standard imports
import json
import logging
import os
from typing import Dict, Union
# external imports
from cic_types.condiments import MetadataPointer
from cic_types.ext.metadata import MetadataRequestsHandler
from cic_types.processor import generate_metadata_pointer
# third-part imports
from cic_types.models.person import generate_metadata_pointer, Person
# local imports
from cic_ussd.cache import cache_data, get_cached_data
from cic_ussd.http.requests import error_handler, make_request
from cic_ussd.metadata.signer import Signer
logg = logging.getLogger(__file__)
class UssdMetadataHandler(MetadataRequestsHandler):
def __init__(self, cic_type: MetadataPointer, identifier: bytes):
super().__init__(cic_type, identifier)
class Metadata:
"""
:cvar base_url: The base url or the metadata server.
:type base_url: str
"""
def cache_metadata(self, data: str):
"""
:param data:
:type data:
:return:
:rtype:
"""
cache_data(self.metadata_pointer, data)
logg.debug(f'caching: {data} with key: {self.metadata_pointer}')
base_url = None
class MetadataRequestsHandler(Metadata):
def __init__(self, cic_type: str, identifier: bytes, engine: str = 'pgp'):
""""""
self.cic_type = cic_type
self.engine = engine
self.headers = {
'X-CIC-AUTOMERGE': 'server',
'Content-Type': 'application/json'
}
self.identifier = identifier
self.metadata_pointer = generate_metadata_pointer(
identifier=self.identifier,
cic_type=self.cic_type
)
if self.base_url:
self.url = os.path.join(self.base_url, self.metadata_pointer)
def create(self, data: Union[Dict, str]):
""""""
data = json.dumps(data).encode('utf-8')
result = make_request(method='POST', url=self.url, data=data, headers=self.headers)
error_handler(result=result)
metadata = result.json()
return self.edit(data=metadata)
def edit(self, data: Union[Dict, str]):
""""""
cic_meta_signer = Signer()
signature = cic_meta_signer.sign_digest(data=data)
algorithm = cic_meta_signer.get_operational_key().get('algo')
formatted_data = {
'm': json.dumps(data),
's': {
'engine': self.engine,
'algo': algorithm,
'data': signature,
'digest': data.get('digest'),
}
}
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}.')
error_handler(result=result)
try:
decoded_identifier = self.identifier.decode("utf-8")
except UnicodeDecodeError:
decoded_identifier = self.identifier.hex()
logg.info(f'identifier: {decoded_identifier}. metadata pointer: {self.metadata_pointer} set to: {data}.')
return result
def query(self):
""""""
result = make_request(method='GET', url=self.url)
error_handler(result=result)
result_data = result.json()
if not isinstance(result_data, dict):
raise ValueError(f'Invalid result data object: {result_data}.')
if result.status_code == 200:
if self.cic_type == ':cic.person':
person = Person()
person_data = person.deserialize(person_data=result_data)
serialized_person_data = person_data.serialize()
data = json.dumps(serialized_person_data)
else:
data = json.dumps(result_data)
cache_data(key=self.metadata_pointer, data=data)
logg.debug(f'caching: {data} with key: {self.metadata_pointer}')
return result_data
def get_cached_metadata(self):
""""""

View File

@@ -1,13 +1,12 @@
# standard imports
# external imports
from cic_types.condiments import MetadataPointer
# local imports
from .base import UssdMetadataHandler
from .base import MetadataRequestsHandler
class CustomMetadata(UssdMetadataHandler):
class CustomMetadata(MetadataRequestsHandler):
def __init__(self, identifier: bytes):
super().__init__(cic_type=MetadataPointer.CUSTOM, identifier=identifier)
super().__init__(cic_type=':cic.custom', identifier=identifier)

View File

@@ -1,13 +1,12 @@
# standard imports
# external imports
from cic_types.condiments import MetadataPointer
# local imports
from .base import UssdMetadataHandler
from .base import MetadataRequestsHandler
class PersonMetadata(UssdMetadataHandler):
class PersonMetadata(MetadataRequestsHandler):
def __init__(self, identifier: bytes):
super().__init__(cic_type=MetadataPointer.PERSON, identifier=identifier)
super().__init__(cic_type=':cic.person', identifier=identifier)

View File

@@ -2,13 +2,12 @@
import logging
# external imports
from cic_types.condiments import MetadataPointer
# local imports
from .base import UssdMetadataHandler
from .base import MetadataRequestsHandler
class PhonePointerMetadata(UssdMetadataHandler):
class PhonePointerMetadata(MetadataRequestsHandler):
def __init__(self, identifier: bytes):
super().__init__(cic_type=MetadataPointer.PHONE, identifier=identifier)
super().__init__(cic_type=':cic.phone', identifier=identifier)

View File

@@ -1,13 +1,13 @@
# standard imports
# external imports
from cic_types.condiments import MetadataPointer
import celery
# local imports
from .base import UssdMetadataHandler
from .base import MetadataRequestsHandler
class PreferencesMetadata(UssdMetadataHandler):
class PreferencesMetadata(MetadataRequestsHandler):
def __init__(self, identifier: bytes):
super().__init__(cic_type=MetadataPointer.PREFERENCES, identifier=identifier)
super().__init__(cic_type=':cic.preferences', identifier=identifier)

View File

@@ -0,0 +1,60 @@
# standard imports
import json
import logging
from typing import Optional
from urllib.request import Request, urlopen
# third-party imports
import gnupg
# local imports
logg = logging.getLogger()
class Signer:
"""
:cvar gpg_path:
:type gpg_path:
:cvar gpg_passphrase:
:type gpg_passphrase:
:cvar key_file_path:
:type key_file_path:
"""
gpg_path: str = None
gpg_passphrase: str = None
key_file_path: str = None
def __init__(self):
self.gpg = gnupg.GPG(gnupghome=self.gpg_path)
with open(self.key_file_path, 'r') as key_file:
self.key_data = key_file.read()
def get_operational_key(self):
"""
:return:
:rtype:
"""
# import key data into keyring
self.gpg.import_keys(key_data=self.key_data)
gpg_keys = self.gpg.list_keys()
key_algorithm = gpg_keys[0].get('algo')
key_id = gpg_keys[0].get("keyid")
logg.debug(f'using signing key: {key_id}, algorithm: {key_algorithm}')
return gpg_keys[0]
def sign_digest(self, data: dict):
"""
:param data:
:type data:
:return:
:rtype:
"""
digest = data['digest']
key_id = self.get_operational_key().get('keyid')
signature = self.gpg.sign(digest, passphrase=self.gpg_passphrase, keyid=key_id)
return str(signature)

View File

@@ -1,17 +1,15 @@
# standard imports
import json
import logging
# third-party imports
import celery
from cic_types.models.person import Person
# local imports
from cic_ussd.metadata import CustomMetadata, PersonMetadata, PhonePointerMetadata, PreferencesMetadata
from cic_ussd.tasks.base import CriticalMetadataTask
celery_app = celery.current_app
logg = logging.getLogger(__file__)
logg = logging.getLogger().getChild(__name__)
@celery_app.task
@@ -24,13 +22,7 @@ def query_person_metadata(blockchain_address: str):
"""
identifier = bytes.fromhex(blockchain_address)
person_metadata_client = PersonMetadata(identifier=identifier)
response = person_metadata_client.query()
data = response.json()
person = Person()
person_data = person.deserialize(person_data=data)
serialized_person_data = person_data.serialize()
data = json.dumps(serialized_person_data)
person_metadata_client.cache_metadata(data=data)
person_metadata_client.query()
@celery_app.task
@@ -84,9 +76,6 @@ def query_preferences_metadata(blockchain_address: str):
:type blockchain_address: str | Ox-hex
"""
identifier = bytes.fromhex(blockchain_address)
logg.debug(f'retrieving preferences metadata for address: {blockchain_address}.')
preferences_metadata_client = PreferencesMetadata(identifier=identifier)
response = preferences_metadata_client.query()
data = json.dumps(response.json())
preferences_metadata_client.cache_metadata(data)
return data
logg.debug(f'Retrieving preferences metadata for address: {blockchain_address}.')
person_metadata_client = PreferencesMetadata(identifier=identifier)
return person_metadata_client.query()

View File

@@ -4,10 +4,10 @@ billiard==3.6.4.0
bcrypt==3.2.0
celery==4.4.7
cffi==1.14.6
cic-eth[services]~=0.12.4a11
cic-eth[services]~=0.12.4a7
cic-notify~=0.4.0a10
cic-types~=0.2.0a3
confini>=0.3.6rc4,<0.5.0
cic-types~=0.1.0a15
confini>=0.4.1a1,<0.5.0
phonenumbers==8.12.12
psycopg2==2.8.6
python-i18n[YAML]==0.3.9

View File

@@ -5,25 +5,24 @@ import os
# external imports
import requests_mock
from chainlib.hash import strip_0x
from cic_types.condiments import MetadataPointer
from cic_types.processor import generate_metadata_pointer
# local imports
from cic_ussd.metadata.base import UssdMetadataHandler
from cic_ussd.metadata.base import MetadataRequestsHandler
# external imports
def test_ussd_metadata_handler(activated_account,
init_cache,
load_config,
person_metadata,
setup_metadata_request_handler,
setup_metadata_signer):
def test_metadata_requests_handler(activated_account,
init_cache,
load_config,
person_metadata,
setup_metadata_request_handler,
setup_metadata_signer):
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
cic_type = MetadataPointer.PERSON
metadata_client = UssdMetadataHandler(cic_type, identifier)
cic_type = ':cic.person'
metadata_client = MetadataRequestsHandler(cic_type, identifier)
assert metadata_client.cic_type == cic_type
assert metadata_client.engine == 'pgp'
assert metadata_client.identifier == identifier
@@ -39,5 +38,7 @@ def test_ussd_metadata_handler(activated_account,
assert result.status_code == 200
person_metadata.pop('digest')
request_mocker.register_uri('GET', metadata_client.url, status_code=200, reason='OK', json=person_metadata)
result = metadata_client.query().json()
result = metadata_client.query()
assert result == person_metadata
cached_metadata = metadata_client.get_cached_metadata()
assert json.loads(cached_metadata) == person_metadata

View File

@@ -1,7 +1,7 @@
# standard imports
import os
# external imports
from cic_types.condiments import MetadataPointer
from chainlib.hash import strip_0x
from cic_types.processor import generate_metadata_pointer
# local imports
@@ -11,8 +11,8 @@ from cic_ussd.metadata import CustomMetadata
def test_custom_metadata(activated_account, load_config, setup_metadata_request_handler, setup_metadata_signer):
cic_type = MetadataPointer.CUSTOM
identifier = bytes.fromhex(activated_account.blockchain_address)
cic_type = ':cic.custom'
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
custom_metadata_client = CustomMetadata(identifier)
assert custom_metadata_client.cic_type == cic_type
assert custom_metadata_client.engine == 'pgp'

View File

@@ -1,7 +1,7 @@
# standard imports
import os
# external imports
from cic_types.condiments import MetadataPointer
from chainlib.hash import strip_0x
from cic_types.processor import generate_metadata_pointer
# local imports
@@ -11,8 +11,8 @@ from cic_ussd.metadata import PersonMetadata
def test_person_metadata(activated_account, load_config, setup_metadata_request_handler, setup_metadata_signer):
cic_type = MetadataPointer.PERSON
identifier = bytes.fromhex(activated_account.blockchain_address)
cic_type = ':cic.person'
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
person_metadata_client = PersonMetadata(identifier)
assert person_metadata_client.cic_type == cic_type
assert person_metadata_client.engine == 'pgp'

View File

@@ -1,7 +1,7 @@
# standard imports
import os
# external imports
from cic_types.condiments import MetadataPointer
from chainlib.hash import strip_0x
from cic_types.processor import generate_metadata_pointer
# local imports
@@ -12,8 +12,8 @@ from cic_ussd.metadata import PhonePointerMetadata
def test_phone_pointer_metadata(activated_account, load_config, setup_metadata_request_handler, setup_metadata_signer):
cic_type = MetadataPointer.PHONE
identifier = bytes.fromhex(activated_account.blockchain_address)
cic_type = ':cic.phone'
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
phone_pointer_metadata = PhonePointerMetadata(identifier)
assert phone_pointer_metadata.cic_type == cic_type
assert phone_pointer_metadata.engine == 'pgp'

View File

@@ -1,7 +1,7 @@
# standard imports
import os
# external imports
from cic_types.condiments import MetadataPointer
from chainlib.hash import strip_0x
from cic_types.processor import generate_metadata_pointer
# local imports
@@ -11,8 +11,8 @@ from cic_ussd.metadata import PreferencesMetadata
def test_preferences_metadata(activated_account, load_config, setup_metadata_request_handler, setup_metadata_signer):
cic_type = MetadataPointer.PREFERENCES
identifier = bytes.fromhex(activated_account.blockchain_address)
cic_type = ':cic.preferences'
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
preferences_metadata_client = PreferencesMetadata(identifier)
assert preferences_metadata_client.cic_type == cic_type
assert preferences_metadata_client.engine == 'pgp'

View File

@@ -0,0 +1,17 @@
# standard imports
import shutil
# third-party imports
# local imports
from cic_ussd.metadata.signer import Signer
def test_client(load_config, setup_metadata_signer, person_metadata):
signer = Signer()
gpg = signer.gpg
assert signer.key_data is not None
gpg.import_keys(key_data=signer.key_data)
gpg_keys = gpg.list_keys()
assert signer.get_operational_key() == gpg_keys[0]
shutil.rmtree(Signer.gpg_path)

View File

@@ -6,19 +6,33 @@ import tempfile
# external imports
import pytest
from chainlib.hash import strip_0x
from cic_types.condiments import MetadataPointer
from cic_types.processor import generate_metadata_pointer
# local imports
from cic_ussd.metadata import PersonMetadata, PhonePointerMetadata, PreferencesMetadata
from cic_ussd.metadata import Metadata, PersonMetadata, PhonePointerMetadata, PreferencesMetadata
from cic_ussd.metadata.signer import Signer
logg = logging.getLogger(__name__)
@pytest.fixture(scope='function')
def setup_metadata_signer(load_config):
temp_dir = tempfile.mkdtemp(dir='/tmp')
logg.debug(f'Created temp dir: {temp_dir}')
Signer.gpg_path = temp_dir
Signer.gpg_passphrase = load_config.get('PGP_PASSPHRASE')
Signer.key_file_path = os.path.join(load_config.get('PGP_KEYS_PATH'), load_config.get('PGP_PRIVATE_KEYS'))
@pytest.fixture(scope='function')
def setup_metadata_request_handler(load_config):
Metadata.base_url = load_config.get('CIC_META_URL')
@pytest.fixture(scope='function')
def account_phone_pointer(activated_account):
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
return generate_metadata_pointer(identifier, MetadataPointer.PERSON)
return generate_metadata_pointer(identifier, ':cic.phone')
@pytest.fixture(scope='function')

View File

@@ -10,5 +10,6 @@ erc20-transfer-authorization>=0.3.5a2,<0.4.0
erc20-faucet>=0.3.2a2,<0.4.0
sarafu-faucet>=0.0.7a2,<0.1.0
confini>=0.4.2rc3,<1.0.0
crypto-dev-signer>=0.4.15rc2,<=0.4.15
eth-token-index>=0.2.4a1,<=0.3.0
okota>=0.2.4a5,<0.3.0

View File

@@ -1,10 +1,10 @@
sarafu-faucet~=0.0.7a2
cic-eth[tools]~=0.12.5a1
cic-types~=0.2.0a4
funga>=0.5.1a1,<0.6.0
cic-eth[tools]~=0.12.4a8
cic-types~=0.1.0a15
crypto-dev-signer~=0.4.15a7
faker==4.17.1
chainsyncer~=0.0.6a3
chainlib-eth~=0.0.10a2
chainlib-eth~=0.0.9a14
eth-address-index~=0.2.3a4
eth-contract-registry~=0.6.3a3
eth-accounts-index~=0.1.2a3