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

chore(deps): update dependencies + stability improvements #20

Merged
merged 10 commits into from
Jul 2, 2024
Merged
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
68 changes: 25 additions & 43 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,52 +19,34 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
python-version: ["3.9", "3.10", "3.11", "3.12"]
os: [ubuntu-latest, macos-latest, windows-latest]

runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- if: matrix.os == 'macos-latest' && matrix.python-version == '3.11'
name: Install build tools
run: |
brew install automake

- name: Install Poetry
uses: abatilo/actions-poetry@v2

- name: Install dependencies
run: |
poetry install

- name: Run tests
run: |
poetry run pytest


lint:
name: Lint
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- if: matrix.os == 'macos-latest' && matrix.python-version == '3.11'
name: Install build tools
run: |
brew install automake

- name: Install Poetry
uses: abatilo/actions-poetry@v2

- name: Install dependencies
run: |
poetry install

- name: Run tests
run: |
poetry run pytest

ruff:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Set up Python 3.9
uses: actions/setup-python@v3
with:
python-version: 3.9

- name: Install Poetry
uses: abatilo/actions-poetry@v2

- name: Install dependencies
run: |
poetry install

- name: Run tests
run: |
poetry run black --check .

- uses: actions/checkout@v4
- uses: chartboost/ruff-action@v1
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
__pycache__

.idea/
.vscode/
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
[![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)

# Babble

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

**Run formatter**

poetry run black .
poetry run ruff check --fix && ruff format
13 changes: 7 additions & 6 deletions examples/simple-e2e.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import base64
from datetime import datetime
from datetime import datetime, timezone

from babble import Client, Identity

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

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


# create out clients
# create clients
client1 = create_client("the wise mans fear none name")
client2 = create_client("the name of the wind man fear")

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

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

# start sending of a message
client1.send(
client2.delegate_address, "why hello there " + datetime.utcnow().isoformat()
client2.delegate_address,
"why hello there " + datetime.now(timezone.utc).isoformat(),
)

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

client1_dorado.send(
client2_dorado.delegate_address,
"why hello there on dorado" + datetime.utcnow().isoformat(),
"why hello there on dorado" + datetime.now(timezone.utc).isoformat(),
)

for msg in client2_dorado.receive():
Expand Down
1,118 changes: 461 additions & 657 deletions poetry.lock

Large diffs are not rendered by default.

17 changes: 9 additions & 8 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,18 @@ packages = [{ include = "babble", from = "src" }]


[tool.poetry.dependencies]
python = "^3.7"
ecdsa = "^0.18.0"
requests = "^2.28.2"
python = ">=3.9,<3.13"
ecdsa = "^0.19.0"
requests = "^2.32.3"
bech32 = "^1.2.0"
pycryptodome = "^3.16.0"
pyjwt = "^2.6.0"
eciespy = "^0.3.13"
pycryptodome = "^3.20.0"
pyjwt = "^2.8.0"
eciespy = "^0.4.2"

[tool.poetry.group.dev.dependencies]
pytest = "^7.2.1"
black = "22.12.0"
pytest = "^8.2.2"
tomli = "^2.0.1"
ruff = "^0.4.10"

[build-system]
requires = ["poetry-core"]
Expand Down
5 changes: 2 additions & 3 deletions scripts/do_release.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import tomli
from packaging.version import Version


ROOT = Path(__file__).parent.parent


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


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

def main(self):
"""Run release process."""
Expand Down
4 changes: 2 additions & 2 deletions src/babble/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
from .client import Client, Message
from .crypto import Identity
from .client import Client, Message # noqa
from .crypto import Identity # noqa
52 changes: 32 additions & 20 deletions src/babble/auth.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
from dataclasses import dataclass
from datetime import datetime, timezone
from typing import Tuple
from typing import Optional, Tuple

import jwt
import requests

from .config import AUTH_SERVER
from .config import AUTH_SERVER, DEFAULT_REQUEST_TIMEOUT
from .crypto.identity import Identity
from .encoding import to_base64, from_base64
from .encoding import from_base64, to_base64


@dataclass
Expand All @@ -18,18 +18,29 @@ class TokenMetadata:
expires_at: datetime


def send_post_request(url: str, data: dict) -> Optional[dict]:
"""Send a POST request to the given URL with the given data."""
try:
response = requests.post(url, json=data, timeout=DEFAULT_REQUEST_TIMEOUT)
return response.json()
except requests.exceptions.RequestException as err:
print(f"Error: {err}")
return None


def authenticate(identity: Identity, name: str = None) -> Tuple[str, TokenMetadata]:
r = requests.post(
"""Authenticate the given identity and return the token and metadata."""
resp = send_post_request(
f"{AUTH_SERVER}/auth/login/wallet/challenge",
json={
{
"address": identity.address,
"client_id": name if name else "uagent",
},
)
r.raise_for_status()
resp = r.json()
if not resp or "challenge" not in resp or "nonce" not in resp:
return None, None

payload = resp["challenge"]
payload: str = resp["challenge"]

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

r = requests.post(
f"{AUTH_SERVER}/auth/login/wallet/verify",
json=login_request,
login_resp = send_post_request(
f"{AUTH_SERVER}/auth/login/wallet/verify", login_request
)
r.raise_for_status()
if not login_resp:
return None, None

r = requests.post(
f"{AUTH_SERVER}/tokens",
json=r.json(),
)
r.raise_for_status()
token_resp = send_post_request(f"{AUTH_SERVER}/tokens", login_resp)
if not token_resp or "access_token" not in token_resp:
return None, None

# extract the token
token = str(r.json()["access_token"])
token = str(token_resp["access_token"])

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

assert metadata.address == identity.address
assert metadata.public_key == identity.public_key
if (
not metadata.address == identity.address
or not metadata.public_key == identity.public_key
):
return None, None

return token, metadata
11 changes: 7 additions & 4 deletions src/babble/client.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
from dataclasses import dataclass
from datetime import datetime, timezone, timedelta
from datetime import datetime, timedelta, timezone
from typing import List

import bech32

from .auth import authenticate
from .crypto.exceptions import RoutingError
from .crypto.identity import Identity
from .encoding import to_json, to_base64, from_base64, from_json
from .encoding import from_base64, from_json, to_base64, to_json
from .mailbox import (
lookup_messaging_public_key,
register_messaging_public_key,
dispatch_messages,
list_messages,
lookup_messaging_public_key,
register_messaging_public_key,
)

EXPIRATION_BUFFER_SECONDS = 60 * 5 # 5 minutes
Expand Down Expand Up @@ -61,6 +61,7 @@ def __init__(

# authenticate against the API
self._token = None
self._token_metadata = {"expires_at": self._now()}
self._update_authentication()

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

def __repr__(self):
return f"{self._delegate_address} ({self._identity.public_key})"
Expand Down
4 changes: 4 additions & 0 deletions src/babble/config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import os

MAINNET_CHAIN_ID = "fetchhub-4"
TESTNET_CHAIN_ID = "dorado-1"

DEFAULT_REQUEST_TIMEOUT = 30
AUTH_SERVER = os.environ.get("AUTH_SERVER", "https://accounts.fetch.ai/v1")
MEMORANDUM_SERVER = os.environ.get(
"MEMORANDUM_SERVER",
Expand Down
2 changes: 1 addition & 1 deletion src/babble/crypto/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from .identity import Identity
from .identity import Identity # noqa
5 changes: 2 additions & 3 deletions src/babble/crypto/identity.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@
import bech32
import ecdsa
from ecdsa.util import sigencode_string_canonize
from ecies import encrypt, decrypt
from ecies.utils import PrivateKey
from ecies import PrivateKey, decrypt, encrypt

from .hashfuncs import sha256, ripemd160
from .hashfuncs import ripemd160, sha256


def _to_bech32(prefix: str, data: bytes) -> str:
Expand Down
2 changes: 1 addition & 1 deletion src/babble/encoding.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import base64
import json
from typing import Union, Any
from typing import Any, Union


def to_json(data: Any) -> str:
Expand Down
4 changes: 2 additions & 2 deletions src/babble/mailbox.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from dataclasses import dataclass
from datetime import datetime, timezone
from typing import Optional, Dict, Any, List
from typing import Any, Dict, List, Optional

import requests

Expand Down Expand Up @@ -176,7 +176,7 @@ def drop_messages(token: str, ids: List[str]):
"ids": ids,
}

resp = _execute(
_ = _execute(
"""
mutation Mutation($ids: [ID!]!) {
dropMessages(ids: $ids) {
Expand Down
Loading
Loading