Skip to content

Commit 939f964

Browse files
authored
chore(deps): update dependencies + stability improvements (#20)
1 parent aabf3a9 commit 939f964

18 files changed

+682
-792
lines changed

.github/workflows/ci.yml

+25-43
Original file line numberDiff line numberDiff line change
@@ -19,52 +19,34 @@ jobs:
1919
strategy:
2020
fail-fast: false
2121
matrix:
22-
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
22+
python-version: ["3.9", "3.10", "3.11", "3.12"]
2323
os: [ubuntu-latest, macos-latest, windows-latest]
2424

2525
runs-on: ${{ matrix.os }}
2626
steps:
27-
- uses: actions/checkout@v3
28-
- name: Set up Python ${{ matrix.python-version }}
29-
uses: actions/setup-python@v3
30-
with:
31-
python-version: ${{ matrix.python-version }}
32-
- if: matrix.os == 'macos-latest' && matrix.python-version == '3.11'
33-
name: Install build tools
34-
run: |
35-
brew install automake
36-
37-
- name: Install Poetry
38-
uses: abatilo/actions-poetry@v2
39-
40-
- name: Install dependencies
41-
run: |
42-
poetry install
43-
44-
- name: Run tests
45-
run: |
46-
poetry run pytest
47-
48-
49-
lint:
50-
name: Lint
27+
- uses: actions/checkout@v4
28+
- name: Set up Python ${{ matrix.python-version }}
29+
uses: actions/setup-python@v4
30+
with:
31+
python-version: ${{ matrix.python-version }}
32+
- if: matrix.os == 'macos-latest' && matrix.python-version == '3.11'
33+
name: Install build tools
34+
run: |
35+
brew install automake
36+
37+
- name: Install Poetry
38+
uses: abatilo/actions-poetry@v2
39+
40+
- name: Install dependencies
41+
run: |
42+
poetry install
43+
44+
- name: Run tests
45+
run: |
46+
poetry run pytest
47+
48+
ruff:
5149
runs-on: ubuntu-latest
52-
5350
steps:
54-
- uses: actions/checkout@v3
55-
- name: Set up Python 3.9
56-
uses: actions/setup-python@v3
57-
with:
58-
python-version: 3.9
59-
60-
- name: Install Poetry
61-
uses: abatilo/actions-poetry@v2
62-
63-
- name: Install dependencies
64-
run: |
65-
poetry install
66-
67-
- name: Run tests
68-
run: |
69-
poetry run black --check .
70-
51+
- uses: actions/checkout@v4
52+
- uses: chartboost/ruff-action@v1

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
__pycache__
33

44
.idea/
5+
.vscode/

README.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
2+
13
# Babble
24

35
A simple python library for interacting with the Fetch.ai messaging service (called Memorandum)
@@ -35,4 +37,4 @@ for msg in client2.receive():
3537

3638
**Run formatter**
3739

38-
poetry run black .
40+
poetry run ruff check --fix && ruff format

examples/simple-e2e.py

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import base64
2-
from datetime import datetime
2+
from datetime import datetime, timezone
33

44
from babble import Client, Identity
55

@@ -13,7 +13,7 @@ def create_client(seed: str, chain_id: str = MAINNET_CHAIN_ID) -> Client:
1313
delegate_pubkey = delegate_identity.public_key
1414
delegate_pubkey_b64 = base64.b64encode(bytes.fromhex(delegate_pubkey)).decode()
1515

16-
# Important: Messaging ublic key to be registered should be different even though delegate address can be same.
16+
# Important: Messaging public key to be registered should be different even though delegate address can be same.
1717
identity = Identity.from_seed(f"{seed} {chain_id}")
1818
signed_bytes, signature = delegate_identity.sign_arbitrary(
1919
identity.public_key.encode()
@@ -29,11 +29,11 @@ def create_client(seed: str, chain_id: str = MAINNET_CHAIN_ID) -> Client:
2929
)
3030

3131

32-
# create out clients
32+
# create clients
3333
client1 = create_client("the wise mans fear none name")
3434
client2 = create_client("the name of the wind man fear")
3535

36-
# create out clients with same seed phrase, should not be an issue
36+
# create clients with same seed phrase, should not be an issue
3737
client1_dorado = create_client("the wise mans fear none name", TESTNET_CHAIN_ID)
3838
client2_dorado = create_client("the name of the wind man fear", TESTNET_CHAIN_ID)
3939

@@ -47,7 +47,8 @@ def create_client(seed: str, chain_id: str = MAINNET_CHAIN_ID) -> Client:
4747

4848
# start sending of a message
4949
client1.send(
50-
client2.delegate_address, "why hello there " + datetime.utcnow().isoformat()
50+
client2.delegate_address,
51+
"why hello there " + datetime.now(timezone.utc).isoformat(),
5152
)
5253

5354
# simulate the reading of the message
@@ -61,7 +62,7 @@ def create_client(seed: str, chain_id: str = MAINNET_CHAIN_ID) -> Client:
6162

6263
client1_dorado.send(
6364
client2_dorado.delegate_address,
64-
"why hello there on dorado" + datetime.utcnow().isoformat(),
65+
"why hello there on dorado" + datetime.now(timezone.utc).isoformat(),
6566
)
6667

6768
for msg in client2_dorado.receive():

poetry.lock

+461-657
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

+9-8
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,18 @@ packages = [{ include = "babble", from = "src" }]
99

1010

1111
[tool.poetry.dependencies]
12-
python = "^3.7"
13-
ecdsa = "^0.18.0"
14-
requests = "^2.28.2"
12+
python = ">=3.9,<3.13"
13+
ecdsa = "^0.19.0"
14+
requests = "^2.32.3"
1515
bech32 = "^1.2.0"
16-
pycryptodome = "^3.16.0"
17-
pyjwt = "^2.6.0"
18-
eciespy = "^0.3.13"
16+
pycryptodome = "^3.20.0"
17+
pyjwt = "^2.8.0"
18+
eciespy = "^0.4.2"
1919

2020
[tool.poetry.group.dev.dependencies]
21-
pytest = "^7.2.1"
22-
black = "22.12.0"
21+
pytest = "^8.2.2"
22+
tomli = "^2.0.1"
23+
ruff = "^0.4.10"
2324

2425
[build-system]
2526
requires = ["poetry-core"]

scripts/do_release.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import tomli
99
from packaging.version import Version
1010

11-
1211
ROOT = Path(__file__).parent.parent
1312

1413

@@ -27,7 +26,7 @@ def pypi_password(self) -> str:
2726

2827

2928
def get_the_latest_release_version() -> Version:
30-
"""Get release version from gihtub tags."""
29+
"""Get release version from github tags."""
3130
text = subprocess.check_output("git ls-remote --tags origin", shell=True, text=True)
3231
tags = [i.split("\t")[1].strip() for i in text.splitlines()]
3332
tags = [i for i in tags if i.startswith("refs/tags/v") and not i.endswith("^{}")]
@@ -93,7 +92,7 @@ def upload_packages(self):
9392
stderr=sys.stderr,
9493
)
9594
if result.returncode != 0:
96-
raise RuntimeError("Upload pacakges failed!")
95+
raise RuntimeError("Upload packages failed!")
9796

9897
def main(self):
9998
"""Run release process."""

src/babble/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
from .client import Client, Message
2-
from .crypto import Identity
1+
from .client import Client, Message # noqa
2+
from .crypto import Identity # noqa

src/babble/auth.py

+32-20
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
from dataclasses import dataclass
22
from datetime import datetime, timezone
3-
from typing import Tuple
3+
from typing import Optional, Tuple
44

55
import jwt
66
import requests
77

8-
from .config import AUTH_SERVER
8+
from .config import AUTH_SERVER, DEFAULT_REQUEST_TIMEOUT
99
from .crypto.identity import Identity
10-
from .encoding import to_base64, from_base64
10+
from .encoding import from_base64, to_base64
1111

1212

1313
@dataclass
@@ -18,18 +18,29 @@ class TokenMetadata:
1818
expires_at: datetime
1919

2020

21+
def send_post_request(url: str, data: dict) -> Optional[dict]:
22+
"""Send a POST request to the given URL with the given data."""
23+
try:
24+
response = requests.post(url, json=data, timeout=DEFAULT_REQUEST_TIMEOUT)
25+
return response.json()
26+
except requests.exceptions.RequestException as err:
27+
print(f"Error: {err}")
28+
return None
29+
30+
2131
def authenticate(identity: Identity, name: str = None) -> Tuple[str, TokenMetadata]:
22-
r = requests.post(
32+
"""Authenticate the given identity and return the token and metadata."""
33+
resp = send_post_request(
2334
f"{AUTH_SERVER}/auth/login/wallet/challenge",
24-
json={
35+
{
2536
"address": identity.address,
2637
"client_id": name if name else "uagent",
2738
},
2839
)
29-
r.raise_for_status()
30-
resp = r.json()
40+
if not resp or "challenge" not in resp or "nonce" not in resp:
41+
return None, None
3142

32-
payload = resp["challenge"]
43+
payload: str = resp["challenge"]
3344

3445
# create the signature
3546
_, signature = identity.sign_arbitrary(payload.encode())
@@ -47,20 +58,18 @@ def authenticate(identity: Identity, name: str = None) -> Tuple[str, TokenMetada
4758
"scope": "",
4859
}
4960

50-
r = requests.post(
51-
f"{AUTH_SERVER}/auth/login/wallet/verify",
52-
json=login_request,
61+
login_resp = send_post_request(
62+
f"{AUTH_SERVER}/auth/login/wallet/verify", login_request
5363
)
54-
r.raise_for_status()
64+
if not login_resp:
65+
return None, None
5566

56-
r = requests.post(
57-
f"{AUTH_SERVER}/tokens",
58-
json=r.json(),
59-
)
60-
r.raise_for_status()
67+
token_resp = send_post_request(f"{AUTH_SERVER}/tokens", login_resp)
68+
if not token_resp or "access_token" not in token_resp:
69+
return None, None
6170

6271
# extract the token
63-
token = str(r.json()["access_token"])
72+
token = str(token_resp["access_token"])
6473

6574
# parse the token
6675
token_data = jwt.decode(
@@ -78,7 +87,10 @@ def authenticate(identity: Identity, name: str = None) -> Tuple[str, TokenMetada
7887
expires_at=datetime.fromtimestamp(token_data["exp"], timezone.utc),
7988
)
8089

81-
assert metadata.address == identity.address
82-
assert metadata.public_key == identity.public_key
90+
if (
91+
not metadata.address == identity.address
92+
or not metadata.public_key == identity.public_key
93+
):
94+
return None, None
8395

8496
return token, metadata

src/babble/client.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
from dataclasses import dataclass
2-
from datetime import datetime, timezone, timedelta
2+
from datetime import datetime, timedelta, timezone
33
from typing import List
44

55
import bech32
66

77
from .auth import authenticate
88
from .crypto.exceptions import RoutingError
99
from .crypto.identity import Identity
10-
from .encoding import to_json, to_base64, from_base64, from_json
10+
from .encoding import from_base64, from_json, to_base64, to_json
1111
from .mailbox import (
12-
lookup_messaging_public_key,
13-
register_messaging_public_key,
1412
dispatch_messages,
1513
list_messages,
14+
lookup_messaging_public_key,
15+
register_messaging_public_key,
1616
)
1717

1818
EXPIRATION_BUFFER_SECONDS = 60 * 5 # 5 minutes
@@ -61,6 +61,7 @@ def __init__(
6161

6262
# authenticate against the API
6363
self._token = None
64+
self._token_metadata = {"expires_at": self._now()}
6465
self._update_authentication()
6566

6667
# ensure the registration is in place
@@ -73,6 +74,8 @@ def _update_authentication(self):
7374
< self._now() - timedelta(seconds=EXPIRATION_BUFFER_SECONDS)
7475
):
7576
self._token, self._token_metadata = authenticate(self._identity, self._name)
77+
if not self._token or not self._token_metadata:
78+
raise ValueError("Failed to authenticate")
7679

7780
def __repr__(self):
7881
return f"{self._delegate_address} ({self._identity.public_key})"

src/babble/config.py

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import os
22

3+
MAINNET_CHAIN_ID = "fetchhub-4"
4+
TESTNET_CHAIN_ID = "dorado-1"
5+
6+
DEFAULT_REQUEST_TIMEOUT = 30
37
AUTH_SERVER = os.environ.get("AUTH_SERVER", "https://accounts.fetch.ai/v1")
48
MEMORANDUM_SERVER = os.environ.get(
59
"MEMORANDUM_SERVER",

src/babble/crypto/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
from .identity import Identity
1+
from .identity import Identity # noqa

src/babble/crypto/identity.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@
77
import bech32
88
import ecdsa
99
from ecdsa.util import sigencode_string_canonize
10-
from ecies import encrypt, decrypt
11-
from ecies.utils import PrivateKey
10+
from ecies import PrivateKey, decrypt, encrypt
1211

13-
from .hashfuncs import sha256, ripemd160
12+
from .hashfuncs import ripemd160, sha256
1413

1514

1615
def _to_bech32(prefix: str, data: bytes) -> str:

src/babble/encoding.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import base64
22
import json
3-
from typing import Union, Any
3+
from typing import Any, Union
44

55

66
def to_json(data: Any) -> str:

src/babble/mailbox.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from dataclasses import dataclass
22
from datetime import datetime, timezone
3-
from typing import Optional, Dict, Any, List
3+
from typing import Any, Dict, List, Optional
44

55
import requests
66

@@ -176,7 +176,7 @@ def drop_messages(token: str, ids: List[str]):
176176
"ids": ids,
177177
}
178178

179-
resp = _execute(
179+
_ = _execute(
180180
"""
181181
mutation Mutation($ids: [ID!]!) {
182182
dropMessages(ids: $ids) {

0 commit comments

Comments
 (0)