koi-net 1.1.0b6__py3-none-any.whl → 1.1.0b8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of koi-net might be problematic. Click here for more details.

koi_net/actor.py ADDED
@@ -0,0 +1,60 @@
1
+ from logging import getLogger
2
+ from rid_lib.types import KoiNetNode
3
+ from rid_lib import RIDType
4
+ from koi_net.context import HandlerContext
5
+ from koi_net.protocol.api_models import ErrorResponse
6
+ from .protocol.event import Event, EventType
7
+
8
+
9
+ logger = getLogger(__name__)
10
+
11
+
12
+ class Actor:
13
+ ctx: HandlerContext
14
+
15
+ def __init__(self):
16
+ pass
17
+
18
+ def set_ctx(self, ctx: HandlerContext):
19
+ self.ctx = ctx
20
+
21
+ def handshake_with(self, target: KoiNetNode):
22
+ logger.debug(f"Initiating handshake with {target}")
23
+ self.ctx.event_queue.push_event_to(
24
+ Event.from_rid(
25
+ event_type=EventType.FORGET,
26
+ rid=self.ctx.identity.rid),
27
+ node=target
28
+ )
29
+
30
+ self.ctx.event_queue.push_event_to(
31
+ event=Event.from_bundle(
32
+ event_type=EventType.NEW,
33
+ bundle=self.ctx.effector.deref(self.ctx.identity.rid)),
34
+ node=target
35
+ )
36
+
37
+ self.ctx.event_queue.flush_webhook_queue(target)
38
+
39
+ def identify_coordinators(self):
40
+ return self.ctx.resolver.get_state_providers(KoiNetNode)
41
+
42
+ def catch_up_with(self, target: KoiNetNode, rid_types: list[RIDType] = []):
43
+ logger.debug(f"catching up with {target} on {rid_types or 'all types'}")
44
+
45
+ payload = self.ctx.request_handler.fetch_manifests(
46
+ node=target,
47
+ rid_types=rid_types
48
+ )
49
+ if type(payload) == ErrorResponse:
50
+ logger.debug("failed to reach node")
51
+ return
52
+
53
+ for manifest in payload.manifests:
54
+ if manifest.rid == self.ctx.identity.rid:
55
+ continue
56
+
57
+ self.ctx.handle(
58
+ manifest=manifest,
59
+ source=target
60
+ )
koi_net/context.py CHANGED
@@ -1,4 +1,6 @@
1
1
  from rid_lib.ext import Cache
2
+
3
+ from koi_net.network.resolver import NetworkResolver
2
4
  from .config import NodeConfig
3
5
  from .effector import Effector
4
6
  from .network.graph import NetworkGraph
@@ -28,6 +30,7 @@ class HandlerContext:
28
30
  event_queue: NetworkEventQueue
29
31
  graph: NetworkGraph
30
32
  request_handler: RequestHandler
33
+ resolver: NetworkResolver
31
34
  effector: Effector
32
35
  _processor: ProcessorInterface | None
33
36
 
@@ -39,6 +42,7 @@ class HandlerContext:
39
42
  event_queue: NetworkEventQueue,
40
43
  graph: NetworkGraph,
41
44
  request_handler: RequestHandler,
45
+ resolver: NetworkResolver,
42
46
  effector: Effector
43
47
  ):
44
48
  self.identity = identity
@@ -47,6 +51,7 @@ class HandlerContext:
47
51
  self.event_queue = event_queue
48
52
  self.graph = graph
49
53
  self.request_handler = request_handler
54
+ self.resolver = resolver
50
55
  self.effector = effector
51
56
  self._processor = None
52
57
 
koi_net/core.py CHANGED
@@ -7,7 +7,7 @@ from .network.graph import NetworkGraph
7
7
  from .network.request_handler import RequestHandler
8
8
  from .network.response_handler import ResponseHandler
9
9
  from .network.error_handler import ErrorHandler
10
- from .network.behavior import Actor
10
+ from .actor import Actor
11
11
  from .processor.interface import ProcessorInterface
12
12
  from .processor import default_handlers
13
13
  from .processor.handler import KnowledgeHandler
@@ -110,11 +110,7 @@ class NodeInterface(Generic[T]):
110
110
  effector=self.effector
111
111
  )
112
112
 
113
- self.actor = (ActorOverride or Actor)(
114
- identity=self.identity,
115
- effector=self.effector,
116
- event_queue=self.event_queue
117
- )
113
+ self.actor = (ActorOverride or Actor)()
118
114
 
119
115
  # pull all handlers defined in default_handlers module
120
116
  if handlers is None:
@@ -137,6 +133,7 @@ class NodeInterface(Generic[T]):
137
133
  event_queue=self.event_queue,
138
134
  graph=self.graph,
139
135
  request_handler=self.request_handler,
136
+ resolver=self.resolver,
140
137
  effector=self.effector
141
138
  )
142
139
 
@@ -167,6 +164,8 @@ class NodeInterface(Generic[T]):
167
164
  self.effector.set_resolver(self.resolver)
168
165
  self.effector.set_action_context(self.action_context)
169
166
 
167
+ self.actor.set_ctx(self.handler_context)
168
+
170
169
  self.lifecycle = (NodeLifecycleOverride or NodeLifecycle)(
171
170
  config=self.config,
172
171
  identity=self.identity,
@@ -174,7 +173,6 @@ class NodeInterface(Generic[T]):
174
173
  processor=self.processor,
175
174
  effector=self.effector,
176
175
  actor=self.actor,
177
- handler_context=self.handler_context,
178
176
  use_kobj_processor_thread=use_kobj_processor_thread
179
177
  )
180
178
 
koi_net/lifecycle.py CHANGED
@@ -1,9 +1,9 @@
1
1
  import logging
2
2
  from contextlib import contextmanager, asynccontextmanager
3
3
 
4
- from koi_net.context import HandlerContext
4
+ from rid_lib.types import KoiNetNode
5
5
 
6
- from .network.behavior import Actor
6
+ from .actor import Actor
7
7
  from .effector import Effector
8
8
  from .config import NodeConfig
9
9
  from .processor.interface import ProcessorInterface
@@ -28,7 +28,6 @@ class NodeLifecycle:
28
28
  processor: ProcessorInterface,
29
29
  effector: Effector,
30
30
  actor: Actor,
31
- handler_context: HandlerContext,
32
31
  use_kobj_processor_thread: bool
33
32
  ):
34
33
  self.config = config
@@ -37,7 +36,6 @@ class NodeLifecycle:
37
36
  self.processor = processor
38
37
  self.effector = effector
39
38
  self.actor = actor
40
- self.handler_context = handler_context
41
39
  self.use_kobj_processor_thread = use_kobj_processor_thread
42
40
 
43
41
  @contextmanager
@@ -89,8 +87,11 @@ class NodeLifecycle:
89
87
  logger.debug(f"I don't have any neighbors, reaching out to first contact {self.config.koi_net.first_contact.rid!r}")
90
88
 
91
89
  self.actor.handshake_with(self.config.koi_net.first_contact.rid)
92
-
93
-
90
+
91
+ for coordinator in self.actor.identify_coordinators():
92
+ self.actor.catch_up_with(coordinator, rid_types=[KoiNetNode])
93
+
94
+
94
95
  def stop(self):
95
96
  """Stops a node, call this method last.
96
97
 
@@ -3,7 +3,7 @@ from koi_net.protocol.errors import ErrorType
3
3
  from koi_net.protocol.event import EventType
4
4
  from rid_lib.types import KoiNetNode
5
5
  from ..processor.interface import ProcessorInterface
6
- from ..network.behavior import Actor
6
+ from ..actor import Actor
7
7
 
8
8
  logger = getLogger(__name__)
9
9
 
koi_net/network/graph.py CHANGED
@@ -57,15 +57,15 @@ class NetworkGraph:
57
57
  """Returns edges this node belongs to.
58
58
 
59
59
  All edges returned by default, specify `direction` to restrict to incoming or outgoing edges only."""
60
-
60
+
61
61
  edges = []
62
62
  if direction != "in" and self.dg.out_edges:
63
63
  out_edges = self.dg.out_edges(self.identity.rid)
64
- edges.extend([e for e in out_edges])
64
+ edges.extend(out_edges)
65
65
 
66
66
  if direction != "out" and self.dg.in_edges:
67
67
  in_edges = self.dg.in_edges(self.identity.rid)
68
- edges.extend([e for e in in_edges])
68
+ edges.extend(in_edges)
69
69
 
70
70
  edge_rids = []
71
71
  for edge in edges:
@@ -87,7 +87,7 @@ class NetworkGraph:
87
87
 
88
88
  All neighboring nodes returned by default, specify `direction` to restrict to neighbors connected by incoming or outgoing edges only."""
89
89
 
90
- neighbors = []
90
+ neighbors = set()
91
91
  for edge_rid in self.get_edges(direction):
92
92
  edge_bundle = self.cache.read(edge_rid)
93
93
 
@@ -96,7 +96,7 @@ class NetworkGraph:
96
96
  continue
97
97
 
98
98
  edge_profile = edge_bundle.validate_contents(EdgeProfile)
99
-
99
+
100
100
  if status and edge_profile.status != status:
101
101
  continue
102
102
 
@@ -104,9 +104,9 @@ class NetworkGraph:
104
104
  continue
105
105
 
106
106
  if edge_profile.target == self.identity.rid:
107
- neighbors.append(edge_profile.source)
107
+ neighbors.add(edge_profile.source)
108
108
  elif edge_profile.source == self.identity.rid:
109
- neighbors.append(edge_profile.target)
109
+ neighbors.add(edge_profile.target)
110
110
 
111
111
  return list(neighbors)
112
112
 
@@ -119,7 +119,7 @@ class RequestHandler:
119
119
  )
120
120
 
121
121
  try:
122
- result = httpx.post(url, data=signed_envelope.model_dump_json())
122
+ result = httpx.post(url, data=signed_envelope.model_dump_json(exclude_none=True))
123
123
  except httpx.ConnectError as err:
124
124
  logger.debug("Failed to connect")
125
125
  self.error_handler.handle_connection_error(node)
@@ -49,7 +49,7 @@ class NetworkResolver:
49
49
  def get_state_providers(self, rid_type: RIDType) -> list[KoiNetNode]:
50
50
  """Returns list of node RIDs which provide state for the specified RID type."""
51
51
 
52
- logger.debug(f"Looking for state providers of '{rid_type}'")
52
+ logger.debug(f"Looking for state providers of {rid_type}")
53
53
  provider_nodes = []
54
54
  for node_rid in self.cache.list_rids(rid_types=[KoiNetNode]):
55
55
  if node_rid == self.identity.rid:
koi_net/protocol/edge.py CHANGED
@@ -2,6 +2,7 @@ from enum import StrEnum
2
2
  from pydantic import BaseModel
3
3
  from rid_lib import RIDType
4
4
  from rid_lib.ext.bundle import Bundle
5
+ from rid_lib.ext.utils import sha256_hash
5
6
  from rid_lib.types import KoiNetEdge, KoiNetNode
6
7
 
7
8
 
@@ -27,7 +28,9 @@ def generate_edge_bundle(
27
28
  rid_types: list[RIDType],
28
29
  edge_type: EdgeType
29
30
  ) -> Bundle:
30
- edge_rid = KoiNetEdge.generate(source, target)
31
+ edge_rid = KoiNetEdge(sha256_hash(
32
+ str(source) + str(target)
33
+ ))
31
34
  edge_profile = EdgeProfile(
32
35
  source=source,
33
36
  target=target,
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
  from typing import Generic, TypeVar
3
- from pydantic import BaseModel
3
+ from pydantic import BaseModel, ConfigDict
4
4
  from rid_lib.types import KoiNetNode
5
5
 
6
6
  from .secure import PrivateKey, PublicKey
@@ -13,6 +13,8 @@ logger = logging.getLogger(__name__)
13
13
  T = TypeVar("T", bound=RequestModels | ResponseModels)
14
14
 
15
15
  class SignedEnvelope(BaseModel, Generic[T]):
16
+ model_config = ConfigDict(exclude_none=True)
17
+
16
18
  payload: T
17
19
  source_node: KoiNetNode
18
20
  target_node: KoiNetNode
@@ -26,24 +28,26 @@ class SignedEnvelope(BaseModel, Generic[T]):
26
28
  target_node=self.target_node
27
29
  )
28
30
 
29
- logger.debug(f"Verifying envelope: {unsigned_envelope.model_dump_json()}")
31
+ logger.debug(f"Verifying envelope: {unsigned_envelope.model_dump_json(exclude_none=True)}")
30
32
 
31
33
  pub_key.verify(
32
34
  self.signature,
33
- unsigned_envelope.model_dump_json().encode()
35
+ unsigned_envelope.model_dump_json(exclude_none=True).encode()
34
36
  )
35
37
 
36
38
  class UnsignedEnvelope(BaseModel, Generic[T]):
39
+ model_config = ConfigDict(exclude_none=True)
40
+
37
41
  payload: T
38
42
  source_node: KoiNetNode
39
43
  target_node: KoiNetNode
40
44
 
41
45
  def sign_with(self, priv_key: PrivateKey) -> SignedEnvelope[T]:
42
- logger.debug(f"Signing envelope: {self.model_dump_json()}")
46
+ logger.debug(f"Signing envelope: {self.model_dump_json(exclude_none=True)}")
43
47
  logger.debug(f"Type: [{type(self.payload)}]")
44
48
 
45
49
  signature = priv_key.sign(
46
- self.model_dump_json().encode()
50
+ self.model_dump_json(exclude_none=True).encode()
47
51
  )
48
52
 
49
53
  return SignedEnvelope(
koi_net/protocol/event.py CHANGED
@@ -15,9 +15,6 @@ class Event(BaseModel):
15
15
  manifest: Manifest | None = None
16
16
  contents: dict | None = None
17
17
 
18
- class Config:
19
- exclude_none = True
20
-
21
18
  def __repr__(self):
22
19
  return f"<Event '{self.rid}' event type: '{self.event_type}'>"
23
20
 
koi_net/server.py CHANGED
@@ -73,7 +73,8 @@ class NodeServer:
73
73
  self.router.add_api_route(
74
74
  path=path,
75
75
  endpoint=self.secure.envelope_handler(func),
76
- methods=["POST"]
76
+ methods=["POST"],
77
+ response_model_exclude_none=True
77
78
  )
78
79
 
79
80
  _add_endpoint(BROADCAST_EVENTS_PATH, self.broadcast_events)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: koi-net
3
- Version: 1.1.0b6
3
+ Version: 1.1.0b8
4
4
  Summary: Implementation of KOI-net protocol in Python
5
5
  Project-URL: Homepage, https://github.com/BlockScience/koi-net/
6
6
  Author-email: Luke Miller <luke@block.science>
@@ -1,21 +1,21 @@
1
1
  koi_net/__init__.py,sha256=b0Ze0pZmJAuygpWUFHM6Kvqo3DkU_uzmkptv1EpAArw,31
2
+ koi_net/actor.py,sha256=Gad1Xg8n_-zm6sj0nDkm_B1MwErQJKeUsbGmIDTRqJk,1820
2
3
  koi_net/config.py,sha256=47XbQ59GRYFi4rlsoWKlnzMQATcnK70i3qmKTZAGOQk,4087
3
- koi_net/context.py,sha256=FwkzjmsyNqUeOeGCuZXtONqs5_MSl1R8-e3IxHuyzTI,1531
4
- koi_net/core.py,sha256=aO9caoS8dLafRGheJWzhbp_ym7o7bi_wxds683bly48,7150
4
+ koi_net/context.py,sha256=G067ecIJJ5k8aesdyxjZC_vh3zVg9PR_H2U-09YIxXA,1683
5
+ koi_net/core.py,sha256=6nEDAOkMTv-pynU_hLOG4tKVWhopMPIHyZI6oJTdLj8,7061
5
6
  koi_net/default_actions.py,sha256=TkQR9oj9CpO37Gb5bZLmFNl-Q8n3OxGiX4dvxQR7SaA,421
6
7
  koi_net/effector.py,sha256=gSyZgRxQ91X04UL261e2pXWUfBHnQTGtjSHpc2JufxA,4097
7
8
  koi_net/identity.py,sha256=FvIWksGTqwM7HCevIwmo_6l-t-2tnYkaaR4CanZatL4,569
8
- koi_net/lifecycle.py,sha256=GL2zltmh-aeBuNVg_rbIgsXMch672w7xkWAXCTjxst4,3550
9
+ koi_net/lifecycle.py,sha256=1WCo-VOox0rK3v_lfzhnFMGiyacZJpsaENlyGq7jHJQ,3563
9
10
  koi_net/poller.py,sha256=bIrlqdac5vLQYAid35xiQJLDMR85GnOSPCXSTQ07-Mc,1173
10
11
  koi_net/secure.py,sha256=cGNF2assqCaYq0i0fhQBm7aREoAdpY-XVypDsE1ALaU,3970
11
- koi_net/server.py,sha256=dZfSKrNHqIVD1qc9WkYRO30L4so-A7iW4IsX63oSmlE,4265
12
+ koi_net/server.py,sha256=PrR_cXQV5YMKa6FXwiJXwMZJ52VQVzLPYYPVl-Miuw8,4315
12
13
  koi_net/network/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- koi_net/network/behavior.py,sha256=NZLvWlrxR0uWriE3ZzCXmocUVccQthy7Xx8E_8KBwsg,1208
14
- koi_net/network/error_handler.py,sha256=_dAl2ovpUZEVhPc8_dcLA7I-FoMNqQNwFy0QLa4uTSY,1617
14
+ koi_net/network/error_handler.py,sha256=09ulFPpSzoI49RFYPxc5TLXslqJQ_78q7TzLJIABeMw,1606
15
15
  koi_net/network/event_queue.py,sha256=DWs26C235iYkP4koKcdbhmIOHGZRJ48d072BoNWyiHo,7325
16
- koi_net/network/graph.py,sha256=60SLiR3aNXIOGe-URzMGgx8abmMEJtz37EJs6jeImEM,4143
17
- koi_net/network/request_handler.py,sha256=vBXw2GO5vGAYCs18flWbln4kO_HmY1G9uuFu9MYqedY,6852
18
- koi_net/network/resolver.py,sha256=coIp4M6k0-8sUfAy4h2NMx_7zCNroWlCHKOj3AXZVhc,5412
16
+ koi_net/network/graph.py,sha256=neVVVHSyqsQR8hW2-lLC8IkDC2xciBXh3aWYXQpVqZs,4134
17
+ koi_net/network/request_handler.py,sha256=_SM5MuYkS636wGJeFkesapQsW5x_kt_1o9KTXB0wksU,6869
18
+ koi_net/network/resolver.py,sha256=YpQq6HKyfcoqr9TnHRnlQI33IM3tE0ljU02pZ3wWLh8,5410
19
19
  koi_net/network/response_handler.py,sha256=__R_EvEpjaMz3PCDvkNgWF_EAHe2nePGk-zK_cT4C4g,2077
20
20
  koi_net/processor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
21
  koi_net/processor/default_handlers.py,sha256=1OTC4p0luTadNm90q6Fr_dbvysFzgRCbltp-YP6cRXo,9562
@@ -26,13 +26,13 @@ koi_net/processor/knowledge_pipeline.py,sha256=i7FpCFl0UIOwCI5zhP1i8M4PX4A48VN28
26
26
  koi_net/protocol/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
27
  koi_net/protocol/api_models.py,sha256=jzRZWW_ZB5YsBAiwCom882-WIbr0rPyelJxExRgHZGc,1755
28
28
  koi_net/protocol/consts.py,sha256=bisbVEojPIHlLhkLafBzfIhH25TjNfvTORF1g6YXzIM,243
29
- koi_net/protocol/edge.py,sha256=dQKtI0_eX2E6tD7kMExv6DeJMkqNo2cY-LxJMJbiK0E,963
30
- koi_net/protocol/envelope.py,sha256=BkBabUZXDOiGJnfBJm5BHTQZflsiQ3jBmG1gNOtmeO4,1698
29
+ koi_net/protocol/edge.py,sha256=PzdEhC43T1KO5iMSEu7I4tiz-7sZxtz41dJfWf-oHA0,1034
30
+ koi_net/protocol/envelope.py,sha256=UVHlO2BDyDiP5eixqx9xD6xUsCfFRi0kZyzC4BC-DOw,1886
31
31
  koi_net/protocol/errors.py,sha256=uKPQ-TGLouZuK0xd2pXuCQoRTyu_JFsydSCLml13Cz8,595
32
- koi_net/protocol/event.py,sha256=eGgihEj1gliLoQRk8pVB2q_was0AGo-PbT3Hqnpn3oU,1379
32
+ koi_net/protocol/event.py,sha256=HxzLN-iCXPyr2YzrswMIkgZYeUdFbBpa5v98dAB06lQ,1328
33
33
  koi_net/protocol/node.py,sha256=7GQzHORFr9cP4BqJgir6EGSWCskL-yqmvJksIiLfcWU,409
34
34
  koi_net/protocol/secure.py,sha256=6sRLWxG5EDF0QLBj29gk3hPmZnPXATrTTFdwx39wQfY,5127
35
- koi_net-1.1.0b6.dist-info/METADATA,sha256=G2VwaYEW0oLstoINlbOJlT-f2yAhSXEOZP3shspdgqo,37118
36
- koi_net-1.1.0b6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
37
- koi_net-1.1.0b6.dist-info/licenses/LICENSE,sha256=03mgCL5qth2aD9C3F3qNVs4sFJSpK9kjtYCyOwdSp7s,1069
38
- koi_net-1.1.0b6.dist-info/RECORD,,
35
+ koi_net-1.1.0b8.dist-info/METADATA,sha256=Kcb1WYE-Eu04rmqyapoF_D_BzfO7q3Q8rSd_dLnXQcY,37118
36
+ koi_net-1.1.0b8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
37
+ koi_net-1.1.0b8.dist-info/licenses/LICENSE,sha256=03mgCL5qth2aD9C3F3qNVs4sFJSpK9kjtYCyOwdSp7s,1069
38
+ koi_net-1.1.0b8.dist-info/RECORD,,
@@ -1,42 +0,0 @@
1
- from logging import getLogger
2
- from rid_lib.types import KoiNetNode
3
- from ..protocol.event import Event, EventType
4
- from ..identity import NodeIdentity
5
- from ..effector import Effector
6
- from ..network.event_queue import NetworkEventQueue
7
-
8
- logger = getLogger(__name__)
9
-
10
-
11
- class Actor:
12
- identity: NodeIdentity
13
- effector: Effector
14
- event_queue: NetworkEventQueue
15
-
16
- def __init__(
17
- self,
18
- identity: NodeIdentity,
19
- effector: Effector,
20
- event_queue: NetworkEventQueue
21
- ):
22
- self.identity = identity
23
- self.effector = effector
24
- self.event_queue = event_queue
25
-
26
- def handshake_with(self, target: KoiNetNode):
27
- logger.debug(f"Initiating handshake with {target}")
28
- self.event_queue.push_event_to(
29
- Event.from_rid(
30
- event_type=EventType.FORGET,
31
- rid=self.identity.rid),
32
- node=target
33
- )
34
-
35
- self.event_queue.push_event_to(
36
- event=Event.from_bundle(
37
- event_type=EventType.NEW,
38
- bundle=self.effector.deref(self.identity.rid)),
39
- node=target
40
- )
41
-
42
- self.event_queue.flush_webhook_queue(target)