Skip to content

Commit 09a1b0f

Browse files
committed
Import code
1 parent 1016de0 commit 09a1b0f

File tree

20 files changed

+2070
-1
lines changed

20 files changed

+2070
-1
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
name: 'Set version'
2+
3+
runs:
4+
using: "composite"
5+
steps:
6+
- shell: bash
7+
if: "startsWith(github.ref, 'refs/tags/')"
8+
run: |
9+
sed -i'' -e "s/version = [\"]0.1.0[\"]/version = \"$GITHUB_REF_NAME\"/g" pyproject.toml

.github/workflows/publish.yml

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
name: Build and publish
2+
3+
on:
4+
push:
5+
tags:
6+
- '*'
7+
workflow_dispatch:
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
linux:
14+
runs-on: ubuntu-latest
15+
strategy:
16+
matrix:
17+
target: [x86_64, x86, aarch64, armv7, s390x, ppc64le]
18+
steps:
19+
- uses: actions/checkout@v3
20+
- uses: ./.github/actions/set-version
21+
- uses: actions/setup-python@v4
22+
with:
23+
python-version: '3.10'
24+
- name: Build wheels
25+
uses: PyO3/maturin-action@v1
26+
with:
27+
target: ${{ matrix.target }}
28+
args: --release --out dist --find-interpreter
29+
sccache: 'true'
30+
manylinux: auto
31+
- name: Upload wheels
32+
uses: actions/upload-artifact@v3
33+
with:
34+
name: wheels
35+
path: dist
36+
37+
windows:
38+
runs-on: windows-latest
39+
strategy:
40+
matrix:
41+
target: [x64, x86]
42+
steps:
43+
- uses: actions/checkout@v3
44+
- uses: ./.github/actions/set-version
45+
- uses: actions/setup-python@v4
46+
with:
47+
python-version: '3.10'
48+
architecture: ${{ matrix.target }}
49+
- name: Build wheels
50+
uses: PyO3/maturin-action@v1
51+
with:
52+
target: ${{ matrix.target }}
53+
args: --release --out dist --find-interpreter
54+
sccache: 'true'
55+
- name: Upload wheels
56+
uses: actions/upload-artifact@v3
57+
with:
58+
name: wheels
59+
path: dist
60+
61+
macos:
62+
runs-on: macos-latest
63+
strategy:
64+
matrix:
65+
target: [x86_64, aarch64]
66+
steps:
67+
- uses: actions/checkout@v3
68+
- uses: ./.github/actions/set-version
69+
- uses: actions/setup-python@v4
70+
with:
71+
python-version: '3.10'
72+
- name: Build wheels
73+
uses: PyO3/maturin-action@v1
74+
with:
75+
target: ${{ matrix.target }}
76+
args: --release --out dist --find-interpreter
77+
sccache: 'true'
78+
- name: Upload wheels
79+
uses: actions/upload-artifact@v3
80+
with:
81+
name: wheels
82+
path: dist
83+
84+
sdist:
85+
runs-on: ubuntu-latest
86+
steps:
87+
- uses: actions/checkout@v3
88+
- uses: ./.github/actions/set-version
89+
- name: Build sdist
90+
uses: PyO3/maturin-action@v1
91+
with:
92+
command: sdist
93+
args: --out dist
94+
- name: Upload sdist
95+
uses: actions/upload-artifact@v3
96+
with:
97+
name: wheels
98+
path: dist
99+
100+
release:
101+
name: Release
102+
runs-on: ubuntu-latest
103+
if: "startsWith(github.ref, 'refs/tags/')"
104+
needs: [linux, windows, macos, sdist]
105+
steps:
106+
- uses: actions/download-artifact@v3
107+
with:
108+
name: wheels
109+
- name: Publish to PyPI
110+
uses: PyO3/maturin-action@v1
111+
env:
112+
MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
113+
with:
114+
command: upload
115+
args: --non-interactive --skip-existing *

.github/workflows/test.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Unit tests
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- main
7+
workflow_dispatch:
8+
9+
jobs:
10+
unit-tests:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v4
14+
- uses: dtolnay/rust-toolchain@stable
15+
with:
16+
toolchain: stable
17+
components: rustfmt
18+
target: x86_64-unknown-linux-gnu
19+
- name: Setup Python 3.8
20+
uses: actions/setup-python@v4
21+
with:
22+
python-version: 3.8
23+
- name: Setup dependencies
24+
run: |
25+
pip install --upgrade pip
26+
pip install pytest typing_extensions
27+
pip install https://github.com/Tribler/py-ipv8/archive/master.zip
28+
- name: Check rust formatting (rustfmt)
29+
run: cargo fmt --all -- --check
30+
- name: Build and run Python tests
31+
run: |
32+
cargo build
33+
cp target/debug/librust_endpoint.so ipv8_rust_tunnels/rust_endpoint.so
34+
export PYTHONPATH=$(pwd):$PYTHONPATH
35+
echo "PYTHONPATH=.:$PYTHONPATH" >> $GITHUB_ENV
36+
pytest ipv8_rust_tunnels

Cargo.toml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
[package]
2+
name = "ipv8-rust-tunnels"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[profile.release]
7+
opt-level = 3
8+
strip = true
9+
debug = false
10+
codegen-units = 1
11+
lto = true
12+
13+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
14+
[lib]
15+
name = "rust_endpoint"
16+
crate-type = ["cdylib"]
17+
18+
[dependencies]
19+
pyo3 = { version = "0.20.0", features = ["extension-module"] }
20+
tokio = { version = "1.34.0", features = ["full"] }
21+
env_logger = "0.10.1"
22+
log = "0.4.20"
23+
arc-swap = "1.6.0"
24+
chacha20poly1305 = "0.10.1"
25+
socks5-proto = "0.4.0"
26+
socks5-server = "0.10.0"
27+
bytes = "1.5.0"
28+
rand = "0.8.5"
29+
map-macro = "0.2.6"

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
# ipv8-rust-tunnels
1+
# IPv8-rust-tunnels
2+
[![](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)

ipv8_rust_tunnels/__init__.py

Whitespace-only changes.

ipv8_rust_tunnels/endpoint.py

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
from __future__ import annotations
2+
3+
from collections import UserDict
4+
from typing import TYPE_CHECKING
5+
6+
if TYPE_CHECKING:
7+
from ipv8.messaging.anonymization.community import TunnelCommunity, TunnelSettings
8+
from ipv8.messaging.anonymization.payload import CellPayload
9+
from ipv8.types import Address
10+
11+
import asyncio
12+
13+
import ipv8_rust_tunnels.rust_endpoint as rust
14+
15+
from ipv8.messaging.anonymization.crypto import CryptoEndpoint
16+
from ipv8.messaging.interfaces.udp.endpoint import Endpoint, EndpointClosedException, UDPv4Address
17+
from ipv8.taskmanager import TaskManager
18+
from ipv8.util import succeed
19+
20+
21+
class ShadowDict(UserDict):
22+
def __init__(self, adder, updater, remover):
23+
self.adder = adder
24+
self.updater = updater
25+
self.remover = remover
26+
super().__init__()
27+
28+
def __setitem__(self, key, item):
29+
super().__setitem__(key, item)
30+
self.adder(key, item)
31+
32+
def __getitem__(self, key):
33+
item = super().__getitem__(key)
34+
if getattr(item, 'dirty', False):
35+
self.updater(key, item)
36+
item.dirty = False
37+
return item
38+
39+
def __delitem__(self, key):
40+
super().__delitem__(key)
41+
self.remover(key)
42+
43+
44+
class RustEndpoint(CryptoEndpoint, Endpoint, TaskManager):
45+
46+
def __init__(self, port=0, ip="0.0.0.0"):
47+
CryptoEndpoint.__init__(self)
48+
Endpoint.__init__(self)
49+
TaskManager.__init__(self)
50+
self.rust_ep = ep = rust.Endpoint(ip, port)
51+
self.loop = asyncio.get_running_loop()
52+
self.bytes_up = self.bytes_down = 0
53+
self.prefix = self.settings = None
54+
55+
self.circuits = ShadowDict(ep.add_circuit, ep.update_circuit, ep.remove_circuit)
56+
self.relays = ShadowDict(ep.add_relay, lambda *_: None, ep.remove_relay)
57+
self.exit_sockets = ShadowDict(ep.add_exit, lambda *_: None, ep.remove_exit)
58+
59+
self.register_task('update_stats', self.update_stats, interval=1)
60+
61+
def update_stats(self):
62+
for circuit in self.circuits.values():
63+
self.rust_ep.update_circuit_stats(circuit.circuit_id, circuit)
64+
65+
for relay in self.relays.values():
66+
self.rust_ep.update_relay_stats(relay.circuit_id, relay)
67+
68+
for exit_socket in self.exit_sockets.values():
69+
self.rust_ep.update_exit_stats(exit_socket.circuit_id, exit_socket)
70+
71+
def setup_tunnels(self, tunnel_community: TunnelCommunity, settings: TunnelSettings) -> None:
72+
"""
73+
Set up the TunnelCommunity.
74+
"""
75+
self.prefix = tunnel_community.get_prefix()
76+
self.settings = settings
77+
78+
self.rust_ep.set_prefix(self.prefix)
79+
self.rust_ep.set_max_relay_early(settings.max_relay_early)
80+
self.rust_ep.set_peer_flags(settings.peer_flags)
81+
82+
def set_max_relay_early(self, max_relay_early: int) -> None:
83+
"""
84+
Set the maximum number of relay_early cells that are allowed to pass a relay.
85+
"""
86+
self.rust_ep.set_max_relay_early(max_relay_early)
87+
88+
def set_peer_flags(self, max_relay_early: int) -> None:
89+
"""
90+
Set peer flags.
91+
"""
92+
self.rust_ep.set_peer_flags(max_relay_early)
93+
94+
def datagram_received(self, ip: str, port: int, datagram: bytes) -> None:
95+
"""
96+
Process incoming data that's coming directly from the socket.
97+
"""
98+
self.bytes_down += len(datagram)
99+
self.loop.call_soon_threadsafe(self.notify_listeners, (UDPv4Address(ip, port), datagram))
100+
101+
def send(self, socket_address: Address, packet: bytes) -> None:
102+
"""
103+
Send a packet to a given address.
104+
"""
105+
self.assert_open()
106+
try:
107+
self.rust_ep.send((str(socket_address[0]), socket_address[1]), packet)
108+
self.bytes_up += len(packet)
109+
except (TypeError, ValueError, AttributeError, rust.RustError) as exc:
110+
self._logger.warning("Dropping packet due to message formatting error: %s", exc)
111+
112+
def send_cell(self, target_addr: Address, cell: CellPayload) -> None:
113+
"""
114+
Send the given payload DIRECTLY to the given peer with the appropriate encryption rules.
115+
"""
116+
packet = cell.to_bin(self.prefix)
117+
self.rust_ep.send_cell(target_addr, packet)
118+
self.bytes_up += len(packet)
119+
120+
async def open(self) -> bool: # noqa: A003
121+
"""
122+
Open the Endpoint.
123+
124+
:return: True is the Endpoint was successfully opened, False otherwise.
125+
"""
126+
self.rust_ep.open(self.datagram_received)
127+
return succeed(self.rust_ep.is_open())
128+
129+
def close(self) -> None:
130+
"""
131+
Closes the Endpoint.
132+
"""
133+
if not self.is_open():
134+
return
135+
136+
self.rust_ep.close()
137+
138+
def assert_open(self) -> None:
139+
"""
140+
Check if we are opened by the programmer and if the underlying transport is fully open.
141+
"""
142+
if not self.is_open():
143+
raise EndpointClosedException(self)
144+
145+
def get_address(self) -> Address:
146+
"""
147+
Get the address for this Endpoint.
148+
"""
149+
self.assert_open()
150+
return self.rust_ep.get_address()
151+
152+
def is_open(self) -> bool:
153+
"""
154+
Check if the underlying socket is open.
155+
"""
156+
return self.rust_ep.is_open()
157+
158+
def reset_byte_counters(self) -> None:
159+
"""
160+
Set bytes_up and bytes_down to 0.
161+
"""
162+
self.bytes_up = 0
163+
self.bytes_down = 0

0 commit comments

Comments
 (0)