Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to v6 and add sponsor method #3

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
## 2.0.0 (2022-08-14)

### Features

- Upgrade endpoints to v6
- New functions:
- `getApp(app: string)`
- `unusedSponsorships(app: string)`,
- `userVerificationStatus(app: string, appUserId: string, params?: { includeHash?: boolean; signed?: "eth" | "nacl"; timestamp?: "seconds" | "milliseconds"; })`
- `userSponsorshipStatus(appUserId: string)`
87 changes: 40 additions & 47 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,84 +16,84 @@ You can install or upgrade python-brightid using:

Or you can install from source with:

$ git clone https://github.com/PooyaFekri/python-brightid.git
$ git clone https://github.com/BrightID/python-brightid.git
$ cd python-brightid
$ python3 setup.py install

## Integrating apps

Apps can use BrightID to make sure their users have no multiple accounts. To verify uniquness of a user, app should:

1. Create a unique `contextId` for the user
1. Create a unique `appUserId` for the user

```
>>> import uuid
>>> context = 'top-up-gifter'
>>> contextId = uuid.uuid4().hex
>>> contextId
>>> app = 'top-up-gifter'
>>> appUserId = uuid.uuid4().hex
>>> app
>>> 'a9ee5dc6ac114c95af50ef90225e0e53'
```
2. Create a deep link with that `contextId`

2. Create a deep link with that `appUserId`

```
>>> url = 'http://node.brightid.org'
>>> deep_link = brightid.tools.create_deep_link(context, contextId, url)
>>> deep_link = brightid.tools.create_deep_link(app, appUserId, url)
>>> node = brightid.Node('http://node.brightid.org/brightid/v5')
>>> brightid.tools.create_qr(deep_link)
>>> 'iVBORw0KGgoAAAANSUhEUgAAAggAAA...dKGIAAAAAElFTkSuQmCC'
```
3. Ask the user to click the deep link or scan the QR code representation to link their BrightID to that `contextId`

4. Query BrightID nodes to check if the BrightID that linked the `contextId` is verified.
3. Ask the user to click the deep link or scan the QR code representation to link their BrightID to that `appUserId`

4. Query BrightID nodes to check if the BrightID that linked the `appUserId` is verified.

```
>>> app = 'top-up-gifter'
>>> try:
... v = node.verifications.get(app, contextId)
... v = node.verifications.get(app, appUserId)
... except Exception as e:
... print(str(e))
...
>>>
```

If exception is raised and `str(e)` is:

- `contextId not found`, it means that user did not link the `contextId` to the BrightID yet. The app should query again after a while in such case.
- `appUserId not found`, it means that user did not link the `appUserId` to the BrightID yet. The app should query again after a while in such case.

- `user can not be verified for this app`, it means user is not verified on BrightID yet. The app should ask user to return back after [getting verified](https://brightid.gitbook.io/brightid/getting-verified) on BrightID.

- `user is not sponsored`, it means that user is not [sponsored](https://dev.brightid.org/docs/guides/docs/basic-integration.md#sponsoring-users) and app should sponsor the user by signing and sending a `Sponsor` operation, wait about 10 seconds for the operation to get applied, and then query again.

```
>>> import brightid
>>> import uuid
>>> import time
>>> node = brightid.Node('http://node.brightid.org/brightid/v5')
>>> op = {
... 'name': 'Sponsor',
... 'app': app,
... 'contextId': contextId,
... 'timestamp': int(time.time()*1000),
... 'v': 5
... }

>>> node = brightid.Node()
>>> app = "Gitcoin"
>>> app_user_id = uuid.uuid4().hex
>>> sponsor_private_key = 'rXSGf0ka3WsvsX...ZKwujpNH51Q=='
>>> op['sig'] = brightid.tools.sign(op, sponsor_private_key)
>>> op['sig']
'7wOmXH1ivk104a9kO5mWkslWstYnWnRtnS/jtXEz3P1PyxhTh06i3H0yZpIrST/iiYpd15iscCuyGJpghnOXDw=='
>>> node.operations.post(op)
'rFT1ASgPQuxUx0XfMq9EjBbuzCOaoOw_sdklaDamPXw'
>>> node.sponsorships.sponsor(sponsor_private_key, app, app_user_id)
>>> time.sleep(10)
>>> try:
... v = node.verifications.get(app, contextId)
... v = node.sponsorships.sponsorshipsStatus(app_user_id)
... except Exception as e:
... print(str(e))
...
>>> v
{'unique': True, 'app': 'top-up-gifter', 'context': 'top-up-gifter', 'contextIds': ['a9ee5dc6ac114c95af50ef90225e0e53', '6e85ac7a7a1945d352de5c422db69f72']}
{'unique': True, 'app': 'Gitcoin', 'appUserIds': ['a9ee5dc6ac114c95af50ef90225e0e53', '6e85ac7a7a1945d352de5c422db69f72']}
```

5. check its database to ensure none of linked `contextId`s by this user got the service supposed to be provided once for each user before. The response has a list of all `contextIds` the BrightID user linked under this context which can be used by app for this purpose.
5. check its database to ensure none of linked `appUserId`s by this user got the service supposed to be provided once for each user before. The response has a list of all `appUserIds` the BrightID user linked under this app which can be used by app for this purpose.

## Using API

Check [BrightID API documentation](https://dev.brightid.org/docs/node-api) to find more details.

#### Connecting to Node

```
>>> import brightid
>>> node = brightid.Node('http://node.brightid.org/brightid/v5')
Expand All @@ -102,7 +102,9 @@ Check [BrightID API documentation](https://dev.brightid.org/docs/node-api) to fi
>>> node.ip()
'68.183.76.106'
```

#### Getting users data with brightid

```
>>> id = 'kwQyKj5RS5DzPq9bvaNTKv0E6JxYIorylnF0ACUFbH0'
>>> node.users.get(id)
Expand All @@ -116,39 +118,30 @@ Check [BrightID API documentation](https://dev.brightid.org/docs/node-api) to fi
>>> node.users.verifications(id)
[{'name': 'BrightID', 'timestamp': 1604002284314}, {'name': 'Yekta', 'rank': 1, 'timestamp': 1608280959867, 'raw_rank': 4.596579161873709}, ...]
```

#### Getting apps data

```
>>> node.apps.get()
[{'id': '1hive', 'name': '1Hive Honey Faucet', 'context': '1hive', 'verification': 'BrightID', 'logo': 'data:image/png;base64,iVBoAYFAw...rkJggg==', 'url': 'https://1hive.org/', 'assignedSponsorships': 7775, 'unusedSponsorships': 701}, ...]
[{'id': '1hive', 'name': '1Hive Honey Faucet', 'app': '1hive', 'verification': 'BrightID', 'logo': 'data:image/png;base64,iVBoAYFAw...rkJggg==', 'url': 'https://1hive.org/', 'assignedSponsorships': 7775, 'unusedSponsorships': 701}, ...]
>>> app = 'top-up-gifter'
>>> node.apps.get(app)
{'id': 'top-up-gifter', 'name': 'Top-up Gifter', 'context': 'top-up-gifter', 'verification': 'BrightID', 'logo': 'data:image/png;base64,iVBORF...YII=', 'url': 'https://t.me/top_up_gifter_bot', 'assignedSponsorships': 100, 'unusedSponsorships': 95}
{'id': 'top-up-gifter', 'name': 'Top-up Gifter', 'app': 'top-up-gifter', 'verification': 'BrightID', 'logo': 'data:image/png;base64,iVBORF...YII=', 'url': 'https://t.me/top_up_gifter_bot', 'assignedSponsorships': 100, 'unusedSponsorships': 95}
```

#### Getting verifications data

```
>>> node.verifications.get(app)
{'count': 68, 'contextIds': ['6847e75f4351f70836c2e6330fb3d8ac', '6e85ac7a7a1945d352de5c422db69f72', ...]}
{'count': 68, 'appUserIds': ['6847e75f4351f70836c2e6330fb3d8ac', '6e85ac7a7a1945d352de5c422db69f72', ...]}
>>> node.verifications.get(app, count_only=True)
68
>>> node.verifications.get(app, contextId, singed='eth', timestamp='seconds')
{'unique': True, 'app': 'top-up-gifter', 'context': 'top-up-gifter', 'contextIds': ['dc100f7d468b232e65aa5545d718caa3', '386039e2db5916e1375b6227bf900b58'], 'timestamp': 1608282563}
```
#### Blocking verification for test
```
>>> action='sponsorship' # sponsorship/link/verification are valid actions
>>> testing_key='a8...OtD'
>>> node.testblocks.put(app, action, contextId, testing_key)
>>> try:
... node.verifications.get(app, contextId, singed='eth', timestamp='seconds')
... except Exception as e:
... print(str(e))
...
user is not sponsored
>>> node.testblocks.delete(app, action, contextId, testing_key)
>>> node.verifications.get(app, contextId, singed='eth', timestamp='seconds')
{'unique': True, 'app': 'top-up-gifter', 'context': 'top-up-gifter', 'contextIds': ['dc100f7d468b232e65aa5545d718caa3', '386039e2db5916e1375b6227bf900b58'], 'timestamp': 1608282714}
>>> node.verifications.get(app, appUserId, singed='eth', timestamp='seconds')
{'unique': True, 'app': 'top-up-gifter', 'app': 'top-up-gifter', 'appUserIds': ['dc100f7d468b232e65aa5545d718caa3', '386039e2db5916e1375b6227bf900b58'], 'timestamp': 1608282563}
```

#### Posting operations

```
>>> import time
>>> user = brightid.tools.create_bright_id()
Expand Down
6 changes: 6 additions & 0 deletions brightid/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,9 @@ def get(self, app=''):
res = response.json()
self.node.check_error(res)
return res.get('data').get('apps') or res.get('data')

def unusedSponsorships(self, app=''):
response = requests.get(f'{self.node.url}/apps/{app}')
res = response.json()
self.node.check_error(res)
return res.get('data').get('apps').get('unusedSponsorships')
1 change: 1 addition & 0 deletions brightid/groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ def get(self, group):
res = response.json()
self.node.check_error(res)
return res.get('data')

10 changes: 2 additions & 8 deletions brightid/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,15 @@


class Node:
def __init__(self, url='http://node.brightid.org/brightid/v5'):
self.url = url
def __init__(self):
self.url = "http://node.brightid.org/brightid/v6"
self.users = users.Users(self)
self.groups = groups.Groups(self)
self.operations = operations.Operations(self)
self.verifications = verifications.Verifications(self)
self.testblocks = testblocks.Testblocks(self)
self.apps = apps.Apps(self)

def ip(self):
response = requests.get(f'{self.url}/ip')
res = response.json()
self.check_error(res)
return res.get('data').get('ip')

def state(self):
response = requests.get(f'{self.url}/state')
res = response.json()
Expand Down
1 change: 1 addition & 0 deletions brightid/operations.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import requests
import time


class Operations:
Expand Down
28 changes: 28 additions & 0 deletions brightid/sponsorships.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import requests
import time


class Sponsorships:
def __init__(self, node):
self.node = node

def sponsorshipStatus(self, app_user_id):
response = requests.get(
f'{self.node.url}/sponsorships/{app_user_id}')
res = response.json()
self.node.check_error(res)
return res.get('data')

def sponsor(self, key, app, app_user_id):
unusedSponsorships = self.node.apps.unusedSponsorships(app)
if (unusedSponsorships < 1):
raise RuntimeError(app + ' does not have any unused sponsorships')
op = {
'name': 'Sponsor',
'app': app,
'appUserId': app_user_id,
'timestamp': int(time.time()*1000),
'v': 6
}
op['sig'] = self.node.tools.sign(op, key)
self.node.operations.post(op)
26 changes: 0 additions & 26 deletions brightid/testblocks.py

This file was deleted.

9 changes: 3 additions & 6 deletions brightid/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@
import pyqrcode


def create_deep_link(context, context_id, url='http://node.brightid.org', schema='https'):
url = url.replace('/', '%2f')
if schema == 'brightid':
deep_link = f'brightid://link-verification/{url}/{context}/{context_id}'
else:
deep_link = f'https://app.brightid.org/link-verification/{url}/{context}/{context_id}/'
def create_deep_link(app, app_user_id):
url = "http://node.brightid.org".replace('/', '%2f')
deep_link = f'brightid://link-verification/{url}/{app}/{app_user_id}'
return deep_link


Expand Down
3 changes: 2 additions & 1 deletion brightid/users.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import requests


class Users:
def __init__(self, node):
self.node = node
Expand All @@ -25,7 +26,7 @@ def connections(self, user, direction):
self.node.check_error(res)
return res.get('data').get('connections')

def profile(self, user, requestor):
def profile(self, user, requestor=""):
response = requests.get(
f'{self.node.url}/users/{user}/profile/{requestor}')
res = response.json()
Expand Down
4 changes: 2 additions & 2 deletions brightid/verifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ class Verifications:
def __init__(self, node):
self.node = node

def get(self, app, context_id='', **kwargs):
def get(self, app, app_user_id='', **kwargs):
params = (
('timestamp', kwargs.get('timestamp')),
('signed', kwargs.get('signed')),
('count_only', kwargs.get('count_only'))
)
response = requests.get(f'{self.node.url}/verifications/{app}/{context_id}', params=params)
response = requests.get(f'{self.node.url}/verifications/{app}/{app_user_id}', params=params)
res = response.json()
self.node.check_error(res)
if res.get('data').get('count') and kwargs.get('count_only'):
Expand Down
12 changes: 6 additions & 6 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
setup(
name = 'python_brightid',
packages = ['brightid'],
version = '1.2.0',
version = '2.0.0',
license='MIT',
description = 'SDK for integrating with BrightId!',
author = 'Pooya Fekri',
author_email = '[email protected]',
url = 'https://github.com/PooyaFekri/python-brightid',
download_url = 'https://github.com/PooyaFekri/python-brightid/archive/main.zip',
author = 'Pooya Fekri, Victor Ginelli (@youngkidwarrior)',
author_email = '[email protected], [email protected]',
url = 'https://github.com/BrightID/python-brightid',
download_url = 'https://github.com/BrightID/python-brightid/archive/main.zip',
keywords = ['Brightid'],
install_requires=[
'requests',
Expand All @@ -19,7 +19,7 @@
classifiers=[ # Optional
# How mature is this project? Common values are
# 3 - Alpha
# 4 - Beta
# 4 - Beta``
# 5 - Production/Stable
'Development Status :: 4 - Beta',

Expand Down