Skip to content

Commit

Permalink
Import code
Browse files Browse the repository at this point in the history
  • Loading branch information
egbertbouman committed Jan 17, 2024
1 parent 1016de0 commit 09a1b0f
Show file tree
Hide file tree
Showing 20 changed files with 2,070 additions and 1 deletion.
9 changes: 9 additions & 0 deletions .github/actions/set-version/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
name: 'Set version'

runs:
using: "composite"
steps:
- shell: bash
if: "startsWith(github.ref, 'refs/tags/')"
run: |
sed -i'' -e "s/version = [\"]0.1.0[\"]/version = \"$GITHUB_REF_NAME\"/g" pyproject.toml
115 changes: 115 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
name: Build and publish

on:
push:
tags:
- '*'
workflow_dispatch:

permissions:
contents: read

jobs:
linux:
runs-on: ubuntu-latest
strategy:
matrix:
target: [x86_64, x86, aarch64, armv7, s390x, ppc64le]
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/set-version
- uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
args: --release --out dist --find-interpreter
sccache: 'true'
manylinux: auto
- name: Upload wheels
uses: actions/upload-artifact@v3
with:
name: wheels
path: dist

windows:
runs-on: windows-latest
strategy:
matrix:
target: [x64, x86]
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/set-version
- uses: actions/setup-python@v4
with:
python-version: '3.10'
architecture: ${{ matrix.target }}
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
args: --release --out dist --find-interpreter
sccache: 'true'
- name: Upload wheels
uses: actions/upload-artifact@v3
with:
name: wheels
path: dist

macos:
runs-on: macos-latest
strategy:
matrix:
target: [x86_64, aarch64]
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/set-version
- uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
args: --release --out dist --find-interpreter
sccache: 'true'
- name: Upload wheels
uses: actions/upload-artifact@v3
with:
name: wheels
path: dist

sdist:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/set-version
- name: Build sdist
uses: PyO3/maturin-action@v1
with:
command: sdist
args: --out dist
- name: Upload sdist
uses: actions/upload-artifact@v3
with:
name: wheels
path: dist

release:
name: Release
runs-on: ubuntu-latest
if: "startsWith(github.ref, 'refs/tags/')"
needs: [linux, windows, macos, sdist]
steps:
- uses: actions/download-artifact@v3
with:
name: wheels
- name: Publish to PyPI
uses: PyO3/maturin-action@v1
env:
MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
with:
command: upload
args: --non-interactive --skip-existing *
36 changes: 36 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Unit tests

on:
pull_request:
branches:
- main
workflow_dispatch:

jobs:
unit-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
components: rustfmt
target: x86_64-unknown-linux-gnu
- name: Setup Python 3.8
uses: actions/setup-python@v4
with:
python-version: 3.8
- name: Setup dependencies
run: |
pip install --upgrade pip
pip install pytest typing_extensions
pip install https://github.com/Tribler/py-ipv8/archive/master.zip
- name: Check rust formatting (rustfmt)
run: cargo fmt --all -- --check
- name: Build and run Python tests
run: |
cargo build
cp target/debug/librust_endpoint.so ipv8_rust_tunnels/rust_endpoint.so
export PYTHONPATH=$(pwd):$PYTHONPATH
echo "PYTHONPATH=.:$PYTHONPATH" >> $GITHUB_ENV
pytest ipv8_rust_tunnels
29 changes: 29 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
[package]
name = "ipv8-rust-tunnels"
version = "0.1.0"
edition = "2021"

[profile.release]
opt-level = 3
strip = true
debug = false
codegen-units = 1
lto = true

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
name = "rust_endpoint"
crate-type = ["cdylib"]

[dependencies]
pyo3 = { version = "0.20.0", features = ["extension-module"] }
tokio = { version = "1.34.0", features = ["full"] }
env_logger = "0.10.1"
log = "0.4.20"
arc-swap = "1.6.0"
chacha20poly1305 = "0.10.1"
socks5-proto = "0.4.0"
socks5-server = "0.10.0"
bytes = "1.5.0"
rand = "0.8.5"
map-macro = "0.2.6"
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
# ipv8-rust-tunnels
# IPv8-rust-tunnels
[![](https://img.shields.io/pypi/v/ipv8-rust-tunnels.svg?label=PyPI)](https://pypi.org/project/ipv8-rust-tunnels/)   [![](https://img.shields.io/pypi/pyversions/ipv8-rust-tunnels.svg?label=Python)](https://pypi.org/project/ipv8-rust-tunnels/)   ![Unit tests](https://github.com/egbertbouman/ipv8-rust-tunnels/actions/workflows/test.yml/badge.svg)
Empty file added ipv8_rust_tunnels/__init__.py
Empty file.
163 changes: 163 additions & 0 deletions ipv8_rust_tunnels/endpoint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
from __future__ import annotations

from collections import UserDict
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from ipv8.messaging.anonymization.community import TunnelCommunity, TunnelSettings
from ipv8.messaging.anonymization.payload import CellPayload
from ipv8.types import Address

import asyncio

import ipv8_rust_tunnels.rust_endpoint as rust

from ipv8.messaging.anonymization.crypto import CryptoEndpoint
from ipv8.messaging.interfaces.udp.endpoint import Endpoint, EndpointClosedException, UDPv4Address
from ipv8.taskmanager import TaskManager
from ipv8.util import succeed


class ShadowDict(UserDict):
def __init__(self, adder, updater, remover):
self.adder = adder
self.updater = updater
self.remover = remover
super().__init__()

def __setitem__(self, key, item):
super().__setitem__(key, item)
self.adder(key, item)

def __getitem__(self, key):
item = super().__getitem__(key)
if getattr(item, 'dirty', False):
self.updater(key, item)
item.dirty = False
return item

def __delitem__(self, key):
super().__delitem__(key)
self.remover(key)


class RustEndpoint(CryptoEndpoint, Endpoint, TaskManager):

def __init__(self, port=0, ip="0.0.0.0"):
CryptoEndpoint.__init__(self)
Endpoint.__init__(self)
TaskManager.__init__(self)
self.rust_ep = ep = rust.Endpoint(ip, port)
self.loop = asyncio.get_running_loop()
self.bytes_up = self.bytes_down = 0
self.prefix = self.settings = None

self.circuits = ShadowDict(ep.add_circuit, ep.update_circuit, ep.remove_circuit)
self.relays = ShadowDict(ep.add_relay, lambda *_: None, ep.remove_relay)
self.exit_sockets = ShadowDict(ep.add_exit, lambda *_: None, ep.remove_exit)

self.register_task('update_stats', self.update_stats, interval=1)

def update_stats(self):
for circuit in self.circuits.values():
self.rust_ep.update_circuit_stats(circuit.circuit_id, circuit)

for relay in self.relays.values():
self.rust_ep.update_relay_stats(relay.circuit_id, relay)

for exit_socket in self.exit_sockets.values():
self.rust_ep.update_exit_stats(exit_socket.circuit_id, exit_socket)

def setup_tunnels(self, tunnel_community: TunnelCommunity, settings: TunnelSettings) -> None:
"""
Set up the TunnelCommunity.
"""
self.prefix = tunnel_community.get_prefix()
self.settings = settings

self.rust_ep.set_prefix(self.prefix)
self.rust_ep.set_max_relay_early(settings.max_relay_early)
self.rust_ep.set_peer_flags(settings.peer_flags)

def set_max_relay_early(self, max_relay_early: int) -> None:
"""
Set the maximum number of relay_early cells that are allowed to pass a relay.
"""
self.rust_ep.set_max_relay_early(max_relay_early)

def set_peer_flags(self, max_relay_early: int) -> None:
"""
Set peer flags.
"""
self.rust_ep.set_peer_flags(max_relay_early)

def datagram_received(self, ip: str, port: int, datagram: bytes) -> None:
"""
Process incoming data that's coming directly from the socket.
"""
self.bytes_down += len(datagram)
self.loop.call_soon_threadsafe(self.notify_listeners, (UDPv4Address(ip, port), datagram))

def send(self, socket_address: Address, packet: bytes) -> None:
"""
Send a packet to a given address.
"""
self.assert_open()
try:
self.rust_ep.send((str(socket_address[0]), socket_address[1]), packet)
self.bytes_up += len(packet)
except (TypeError, ValueError, AttributeError, rust.RustError) as exc:
self._logger.warning("Dropping packet due to message formatting error: %s", exc)

def send_cell(self, target_addr: Address, cell: CellPayload) -> None:
"""
Send the given payload DIRECTLY to the given peer with the appropriate encryption rules.
"""
packet = cell.to_bin(self.prefix)
self.rust_ep.send_cell(target_addr, packet)
self.bytes_up += len(packet)

async def open(self) -> bool: # noqa: A003
"""
Open the Endpoint.
:return: True is the Endpoint was successfully opened, False otherwise.
"""
self.rust_ep.open(self.datagram_received)
return succeed(self.rust_ep.is_open())

def close(self) -> None:
"""
Closes the Endpoint.
"""
if not self.is_open():
return

self.rust_ep.close()

def assert_open(self) -> None:
"""
Check if we are opened by the programmer and if the underlying transport is fully open.
"""
if not self.is_open():
raise EndpointClosedException(self)

def get_address(self) -> Address:
"""
Get the address for this Endpoint.
"""
self.assert_open()
return self.rust_ep.get_address()

def is_open(self) -> bool:
"""
Check if the underlying socket is open.
"""
return self.rust_ep.is_open()

def reset_byte_counters(self) -> None:
"""
Set bytes_up and bytes_down to 0.
"""
self.bytes_up = 0
self.bytes_down = 0
Loading

0 comments on commit 09a1b0f

Please sign in to comment.