Skip to content

Commit 7a4e03f

Browse files
authored
Merge pull request #12 from BlockScience/refactor
v1.1.0-beta.1
2 parents 33a52f9 + 7ff6bfd commit 7a4e03f

32 files changed

+1465
-678
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
rid-lib
22
__pycache__
33
*.json
4+
*.pem
5+
*.yaml
46
venv
57
.env
68
prototypes

examples/basic_coordinator_node.py

Lines changed: 6 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,7 @@
88
from rid_lib.types import KoiNetNode, KoiNetEdge
99
from koi_net import NodeInterface
1010
from koi_net.config import NodeConfig, KoiNetConfig
11-
from koi_net.processor.handler import HandlerType
12-
from koi_net.processor.knowledge_object import KnowledgeObject, KnowledgeSource
13-
from koi_net.protocol.edge import EdgeType
14-
from koi_net.protocol.event import Event, EventType
15-
from koi_net.protocol.helpers import generate_edge_bundle
1611
from koi_net.protocol.node import NodeProfile, NodeType, NodeProvides
17-
from koi_net.processor import ProcessorInterface
1812
from koi_net.protocol.api_models import (
1913
PollEvents,
2014
FetchRids,
@@ -41,6 +35,8 @@
4135
handlers=[RichHandler()]
4236
)
4337

38+
# TODO: UPDATE OR REMOVE, THIS EXAMPLE IS OUT OF DATE
39+
4440
logging.getLogger("koi_net").setLevel(logging.DEBUG)
4541

4642

@@ -69,32 +65,6 @@ class CoordinatorNodeConfig(NodeConfig):
6965
logger = logging.getLogger(__name__)
7066

7167

72-
@node.processor.register_handler(HandlerType.Network, rid_types=[KoiNetNode])
73-
def handshake_handler(proc: ProcessorInterface, kobj: KnowledgeObject):
74-
logger.info("Handling node handshake")
75-
76-
# only respond if node declares itself as NEW
77-
if kobj.event_type != EventType.NEW:
78-
return
79-
80-
logger.info("Sharing this node's bundle with peer")
81-
proc.network.push_event_to(
82-
event=Event.from_bundle(EventType.NEW, proc.identity.bundle),
83-
node=kobj.rid,
84-
flush=True
85-
)
86-
87-
logger.info("Proposing new edge")
88-
# defer handling of proposed edge
89-
proc.handle(bundle=generate_edge_bundle(
90-
source=kobj.rid,
91-
target=proc.identity.rid,
92-
edge_type=EdgeType.WEBHOOK,
93-
rid_types=[KoiNetNode, KoiNetEdge]
94-
))
95-
96-
97-
9868
@asynccontextmanager
9969
async def lifespan(app: FastAPI):
10070
node.start()
@@ -117,20 +87,20 @@ def broadcast_events(req: EventsPayload):
11787
@app.post(POLL_EVENTS_PATH)
11888
def poll_events(req: PollEvents) -> EventsPayload:
11989
logger.info(f"Request to {POLL_EVENTS_PATH}")
120-
events = node.network.flush_poll_queue(req.rid)
90+
events = node.event_queue.flush_poll_queue(req.rid)
12191
return EventsPayload(events=events)
12292

12393
@app.post(FETCH_RIDS_PATH)
12494
def fetch_rids(req: FetchRids) -> RidsPayload:
125-
return node.network.response_handler.fetch_rids(req)
95+
return node.response_handler.fetch_rids(req)
12696

12797
@app.post(FETCH_MANIFESTS_PATH)
12898
def fetch_manifests(req: FetchManifests) -> ManifestsPayload:
129-
return node.network.response_handler.fetch_manifests(req)
99+
return node.response_handler.fetch_manifests(req)
130100

131101
@app.post(FETCH_BUNDLES_PATH)
132102
def fetch_bundles(req: FetchBundles) -> BundlesPayload:
133-
return node.network.response_handler.fetch_bundles(req)
103+
return node.response_handler.fetch_bundles(req)
134104

135105
if __name__ == "__main__":
136106
openapi_spec = app.openapi()

examples/basic_partial_node.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33
from pydantic import Field
44
from rich.logging import RichHandler
55
from koi_net import NodeInterface
6-
from koi_net.processor.knowledge_object import KnowledgeSource
76
from koi_net.protocol.node import NodeProfile, NodeType
8-
from koi_net.config import NodeConfig, KoiNetConfig
7+
from koi_net.config import NodeConfig, KoiNetConfig, NodeContact
98

109
logging.basicConfig(
1110
level=logging.INFO,
@@ -18,29 +17,32 @@
1817
logger = logging.getLogger(__name__)
1918

2019

21-
class CoordinatorNodeConfig(NodeConfig):
20+
class PartialNodeConfig(NodeConfig):
2221
koi_net: KoiNetConfig | None = Field(default_factory = lambda:
2322
KoiNetConfig(
2423
node_name="partial",
2524
node_profile=NodeProfile(
2625
node_type=NodeType.PARTIAL
2726
),
2827
cache_directory_path=".basic_partial_rid_cache",
29-
event_queues_path="basic_partial_event_queues.json",
30-
first_contact="http://127.0.0.1:8000/koi-net"
28+
event_queues_path="basic_partial_event_queues.json"
3129
)
3230
)
3331

3432

3533
node = NodeInterface(
36-
config=CoordinatorNodeConfig.load_from_yaml("basic_partial_config.yaml")
34+
config=PartialNodeConfig.load_from_yaml("basic_partial_config.yaml")
3735
)
3836

37+
3938
node.start()
4039

4140
while True:
42-
for event in node.network.poll_neighbors():
43-
node.processor.handle(event=event, source=KnowledgeSource.External)
41+
neighbors = node.resolver.poll_neighbors()
42+
for node_rid in neighbors:
43+
events = neighbors[node_rid]
44+
for event in events:
45+
node.processor.handle(event=event, source=node_rid)
4446
node.processor.flush_kobj_queue()
4547

4648
time.sleep(5)

pyproject.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "koi-net"
7-
version = "1.0.0"
7+
version = "1.1.0-beta.1"
88
description = "Implementation of KOI-net protocol in Python"
99
authors = [
1010
{name = "Luke Miller", email = "[email protected]"}
@@ -13,12 +13,13 @@ readme = "README.md"
1313
requires-python = ">=3.10"
1414
license = {file = "LICENSE"}
1515
dependencies = [
16-
"rid-lib>=3.2.3",
16+
"rid-lib>=3.2.7",
1717
"networkx>=3.4.2",
1818
"httpx>=0.28.1",
1919
"pydantic>=2.10.6",
2020
"ruamel.yaml>=0.18.10",
21-
"python-dotenv>=1.1.0"
21+
"python-dotenv>=1.1.0",
22+
"cryptography>=45.0.3"
2223
]
2324

2425
[project.optional-dependencies]

src/koi_net/config.py

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,42 @@
11
import os
2-
from typing import TypeVar
32
from ruamel.yaml import YAML
4-
from koi_net.protocol.node import NodeProfile
5-
from rid_lib.types import KoiNetNode
63
from pydantic import BaseModel, Field, PrivateAttr
74
from dotenv import load_dotenv
5+
from rid_lib.ext.utils import sha256_hash
6+
from rid_lib.types import KoiNetNode
7+
from .protocol.secure import PrivateKey
8+
from .protocol.node import NodeProfile, NodeType
89

910

1011
class ServerConfig(BaseModel):
11-
host: str | None = "127.0.0.1"
12-
port: int | None = 8000
12+
host: str = "127.0.0.1"
13+
port: int = 8000
1314
path: str | None = "/koi-net"
1415

1516
@property
1617
def url(self) -> str:
1718
return f"http://{self.host}:{self.port}{self.path or ''}"
1819

20+
class NodeContact(BaseModel):
21+
rid: KoiNetNode | None = None
22+
url: str | None = None
23+
1924
class KoiNetConfig(BaseModel):
2025
node_name: str
2126
node_rid: KoiNetNode | None = None
2227
node_profile: NodeProfile
2328

24-
cache_directory_path: str | None = ".rid_cache"
25-
event_queues_path: str | None = "event_queues.json"
26-
27-
first_contact: str | None = None
29+
cache_directory_path: str = ".rid_cache"
30+
event_queues_path: str = "event_queues.json"
31+
private_key_pem_path: str = "priv_key.pem"
32+
33+
first_contact: NodeContact = Field(default_factory=NodeContact)
34+
35+
_priv_key: PrivateKey | None = PrivateAttr(default=None)
2836

2937
class EnvConfig(BaseModel):
38+
priv_key_password: str | None = "PRIV_KEY_PASSWORD"
39+
3040
def __init__(self, **kwargs):
3141
super().__init__(**kwargs)
3242
load_dotenv()
@@ -41,8 +51,10 @@ def __getattribute__(self, name):
4151
return value
4252

4353
class NodeConfig(BaseModel):
44-
server: ServerConfig | None = Field(default_factory=ServerConfig)
54+
server: ServerConfig = Field(default_factory=ServerConfig)
4555
koi_net: KoiNetConfig
56+
env: EnvConfig = Field(default_factory=EnvConfig)
57+
4658
_file_path: str = PrivateAttr(default="config.yaml")
4759
_file_content: str | None = PrivateAttr(default=None)
4860

@@ -72,13 +84,27 @@ def load_from_yaml(
7284

7385
config._file_path = file_path
7486

75-
if generate_missing:
76-
config.koi_net.node_rid = (
77-
config.koi_net.node_rid or KoiNetNode.generate(config.koi_net.node_name)
78-
)
79-
config.koi_net.node_profile.base_url = (
80-
config.koi_net.node_profile.base_url or config.server.url
81-
)
87+
if generate_missing:
88+
if not config.koi_net.node_rid:
89+
priv_key = PrivateKey.generate()
90+
pub_key = priv_key.public_key()
91+
92+
config.koi_net.node_rid = KoiNetNode(
93+
config.koi_net.node_name,
94+
sha256_hash(pub_key.to_der())
95+
)
96+
97+
with open(config.koi_net.private_key_pem_path, "w") as f:
98+
f.write(
99+
priv_key.to_pem(config.env.priv_key_password)
100+
)
101+
102+
config.koi_net.node_profile.public_key = pub_key.to_der()
103+
104+
if config.koi_net.node_profile.node_type == NodeType.FULL:
105+
config.koi_net.node_profile.base_url = (
106+
config.koi_net.node_profile.base_url or config.server.url
107+
)
82108

83109
config.save_to_yaml()
84110

@@ -98,4 +124,3 @@ def save_to_yaml(self):
98124
f.write(self._file_content)
99125
raise e
100126

101-
ConfigType = TypeVar("ConfigType", bound=NodeConfig)

src/koi_net/context.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
2+
from koi_net.effector import Effector
3+
from rid_lib.ext import Cache
4+
from .network.graph import NetworkGraph
5+
from .network.event_queue import NetworkEventQueue
6+
from .network.request_handler import RequestHandler
7+
from .identity import NodeIdentity
8+
from .processor.interface import ProcessorInterface
9+
10+
11+
class ActionContext:
12+
identity: NodeIdentity
13+
effector: Effector
14+
15+
def __init__(
16+
self,
17+
identity: NodeIdentity,
18+
effector: Effector
19+
):
20+
self.identity = identity
21+
self.effector = effector
22+
23+
24+
class HandlerContext:
25+
identity: NodeIdentity
26+
cache: Cache
27+
event_queue: NetworkEventQueue
28+
graph: NetworkGraph
29+
request_handler: RequestHandler
30+
effector: Effector
31+
_processor: ProcessorInterface | None
32+
33+
def __init__(
34+
self,
35+
identity: NodeIdentity,
36+
cache: Cache,
37+
event_queue: NetworkEventQueue,
38+
graph: NetworkGraph,
39+
request_handler: RequestHandler,
40+
effector: Effector
41+
):
42+
self.identity = identity
43+
self.cache = cache
44+
self.event_queue = event_queue
45+
self.graph = graph
46+
self.request_handler = request_handler
47+
self.effector = effector
48+
self._processor = None
49+
50+
def set_processor(self, processor: ProcessorInterface):
51+
self._processor = processor
52+
53+
@property
54+
def handle(self):
55+
return self._processor.handle

0 commit comments

Comments
 (0)