fleetmesh-node 0.2.0__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.
- fleetmesh_node-0.2.0.dist-info/METADATA +57 -0
- fleetmesh_node-0.2.0.dist-info/RECORD +338 -0
- fleetmesh_node-0.2.0.dist-info/WHEEL +5 -0
- fleetmesh_node-0.2.0.dist-info/entry_points.txt +2 -0
- fleetmesh_node-0.2.0.dist-info/licenses/LICENSE +121 -0
- fleetmesh_node-0.2.0.dist-info/top_level.txt +2 -0
- node/RELEASE +1 -0
- node/__init__.py +35 -0
- node/app/__init__.py +49 -0
- node/app/commands.py +265 -0
- node/app/config.py +131 -0
- node/app/control.py +670 -0
- node/app/core.py +867 -0
- node/app/plugins.py +76 -0
- node/app/router.py +83 -0
- node/app/socket.py +431 -0
- node/app/systemd.py +36 -0
- node/app/views.py +204 -0
- node/channels/__init__.py +378 -0
- node/channels/bus.py +331 -0
- node/channels/journal.py +46 -0
- node/commands/__init__.py +140 -0
- node/commands/adapter.py +210 -0
- node/commands/base.py +278 -0
- node/commands/cluster_cmds.py +183 -0
- node/commands/compute_cmds.py +401 -0
- node/commands/core.py +474 -0
- node/commands/gossip_cmds.py +106 -0
- node/commands/nsite_cmds.py +141 -0
- node/commands/peering.py +402 -0
- node/commands/plugin_cmds.py +135 -0
- node/commands/sidecar_cmds.py +120 -0
- node/commands/sync.py +388 -0
- node/commands/transport_cmds.py +451 -0
- node/commands/video_cmds.py +99 -0
- node/compartment/__init__.py +124 -0
- node/compartment/base.py +868 -0
- node/compartment/command.py +458 -0
- node/compartment/context.py +163 -0
- node/compartment/every.py +108 -0
- node/compartment/handler.py +131 -0
- node/compartment/lifecycle.py +460 -0
- node/compartment/lint.py +388 -0
- node/compartment/need.py +158 -0
- node/compartment/problem.py +521 -0
- node/compartment/refusals.py +120 -0
- node/compartment/registry.py +790 -0
- node/compartment/schema.py +290 -0
- node/compartment/sidecar.py +499 -0
- node/compartment/stores.py +662 -0
- node/compartment/wire.py +225 -0
- node/compartment/worker.py +61 -0
- node/config/__init__.py +43 -0
- node/config/base.py +116 -0
- node/config/change.py +441 -0
- node/config/changestore.py +201 -0
- node/config/compartments.py +1144 -0
- node/config/node.py +318 -0
- node/config/parameter.py +563 -0
- node/config/protocols.py +274 -0
- node/config/records.py +171 -0
- node/examples/__init__.py +1 -0
- node/examples/demo.py +226 -0
- node/examples/follower.py +46 -0
- node/interfaces.py +235 -0
- node/keystore/__init__.py +27 -0
- node/keystore/core.py +103 -0
- node/keystore/enclave.py +688 -0
- node/meshnode.py +111 -0
- node/metrics/__init__.py +396 -0
- node/metrics/adapters.py +102 -0
- node/metrics/face.py +110 -0
- node/planes/__init__.py +21 -0
- node/planes/identity/__init__.py +20 -0
- node/planes/identity/announce/__init__.py +9 -0
- node/planes/identity/announce/compartment.py +447 -0
- node/planes/identity/announce/types.py +26 -0
- node/planes/identity/discovery/__init__.py +9 -0
- node/planes/identity/discovery/compartment.py +522 -0
- node/planes/identity/discovery/types.py +13 -0
- node/planes/identity/linked/__init__.py +1 -0
- node/planes/identity/linked/compartment.py +270 -0
- node/planes/identity/linked/types.py +25 -0
- node/planes/identity/presence/__init__.py +7 -0
- node/planes/identity/presence/compartment.py +409 -0
- node/planes/identity/presence/types.py +14 -0
- node/planes/operations/__init__.py +13 -0
- node/planes/operations/scheduler/__init__.py +9 -0
- node/planes/operations/scheduler/compartment.py +353 -0
- node/planes/operations/scheduler/types.py +21 -0
- node/planes/operations/updates/__init__.py +17 -0
- node/planes/operations/updates/core.py +310 -0
- node/planes/operations/updates/store.py +79 -0
- node/planes/operations/updates/types.py +20 -0
- node/planes/peering/__init__.py +28 -0
- node/planes/peering/budget/__init__.py +43 -0
- node/planes/peering/budget/chain.py +43 -0
- node/planes/peering/budget/core.py +537 -0
- node/planes/peering/budget/evaluation.py +80 -0
- node/planes/peering/budget/meter.py +100 -0
- node/planes/peering/budget/receipts.py +439 -0
- node/planes/peering/budget/shape.py +101 -0
- node/planes/peering/budget/tick.py +110 -0
- node/planes/peering/budget/types.py +67 -0
- node/planes/peering/budget/windows.py +346 -0
- node/planes/peering/capacity/__init__.py +7 -0
- node/planes/peering/capacity/compartment.py +342 -0
- node/planes/peering/capacity/types.py +24 -0
- node/planes/peering/peering/__init__.py +25 -0
- node/planes/peering/peering/core.py +464 -0
- node/planes/peering/peering/envelope.py +142 -0
- node/planes/peering/peering/handshake.py +198 -0
- node/planes/peering/peering/shape.py +80 -0
- node/planes/peering/peering/types.py +39 -0
- node/planes/peering/reporter/__init__.py +42 -0
- node/planes/peering/reporter/constants.py +29 -0
- node/planes/peering/reporter/core.py +299 -0
- node/planes/peering/reporter/finding.py +74 -0
- node/planes/peering/reporter/findings.py +171 -0
- node/planes/peering/reporter/publish.py +285 -0
- node/planes/peering/reporter/reading.py +193 -0
- node/planes/peering/reporter/shape.py +86 -0
- node/planes/peering/reporter/types.py +50 -0
- node/planes/peering/strain/__init__.py +9 -0
- node/planes/peering/strain/compartment.py +204 -0
- node/planes/peering/strain/types.py +21 -0
- node/planes/peering/trust/__init__.py +9 -0
- node/planes/peering/trust/compartment.py +239 -0
- node/planes/peering/trust/types.py +30 -0
- node/planes/services/__init__.py +33 -0
- node/planes/services/compute/__init__.py +160 -0
- node/planes/services/compute/common.py +134 -0
- node/planes/services/compute/compartment.py +681 -0
- node/planes/services/compute/executors/__init__.py +121 -0
- node/planes/services/compute/executors/agent.py +840 -0
- node/planes/services/compute/executors/bacalhau.py +605 -0
- node/planes/services/compute/executors/base.py +127 -0
- node/planes/services/compute/executors/harness.py +439 -0
- node/planes/services/compute/executors/mcp.py +454 -0
- node/planes/services/compute/executors/media.py +898 -0
- node/planes/services/compute/executors/nondet.py +283 -0
- node/planes/services/compute/executors/providers.py +499 -0
- node/planes/services/compute/executors/registry.py +140 -0
- node/planes/services/compute/executors/wasm.py +244 -0
- node/planes/services/compute/executors/zk.py +604 -0
- node/planes/services/compute/jobs.py +227 -0
- node/planes/services/compute/load.py +180 -0
- node/planes/services/compute/market.py +433 -0
- node/planes/services/compute/sandbox.py +282 -0
- node/planes/services/compute/types.py +84 -0
- node/planes/services/compute_wire/__init__.py +58 -0
- node/planes/services/compute_wire/compartment.py +256 -0
- node/planes/services/compute_wire/messages.py +320 -0
- node/planes/services/compute_wire/sealing.py +147 -0
- node/planes/services/compute_wire/session.py +572 -0
- node/planes/services/compute_wire/types.py +49 -0
- node/planes/services/compute_wire/verify.py +331 -0
- node/planes/services/dvm/__init__.py +24 -0
- node/planes/services/dvm/gateway.py +526 -0
- node/planes/services/dvm/protocol.py +35 -0
- node/planes/services/dvm/translate.py +15 -0
- node/planes/services/dvm/types.py +25 -0
- node/planes/services/gateway/__init__.py +40 -0
- node/planes/services/gateway/settle.py +279 -0
- node/planes/services/gateway/translate.py +644 -0
- node/planes/services/gossip/__init__.py +22 -0
- node/planes/services/gossip/core.py +929 -0
- node/planes/services/gossip/envelope.py +154 -0
- node/planes/services/gossip/mesh.py +127 -0
- node/planes/services/gossip/types.py +50 -0
- node/planes/services/nsite/__init__.py +7 -0
- node/planes/services/nsite/client.py +62 -0
- node/planes/services/nsite/compartment.py +752 -0
- node/planes/services/nsite/types.py +44 -0
- node/planes/services/paidfetch/__init__.py +7 -0
- node/planes/services/paidfetch/compartment.py +862 -0
- node/planes/services/paidfetch/types.py +99 -0
- node/planes/services/settle/__init__.py +27 -0
- node/planes/services/settle/compartment.py +384 -0
- node/planes/services/settle/engine.py +456 -0
- node/planes/services/settle/gateways.py +541 -0
- node/planes/services/settle/payment.py +512 -0
- node/planes/services/settle/store.py +74 -0
- node/planes/services/settle/types.py +58 -0
- node/planes/services/sidecars/__init__.py +8 -0
- node/planes/services/sidecars/compartment.py +461 -0
- node/planes/services/sidecars/manifest.py +304 -0
- node/planes/services/sidecars/manifests/10-grasp.json +37 -0
- node/planes/services/sidecars/manifests/20-ncontainer.json +31 -0
- node/planes/services/sidecars/manifests/30-magpie.json +28 -0
- node/planes/services/sidecars/types.py +43 -0
- node/planes/services/video/__init__.py +106 -0
- node/planes/services/video/barter.py +407 -0
- node/planes/services/video/cli.py +158 -0
- node/planes/services/video/compartment.py +932 -0
- node/planes/services/video/ingest.py +524 -0
- node/planes/services/video/ledger.py +705 -0
- node/planes/services/video/packager.py +847 -0
- node/planes/services/video/player.py +456 -0
- node/planes/services/video/timed_segments.py +124 -0
- node/planes/services/video/transit.py +478 -0
- node/planes/store/__init__.py +24 -0
- node/planes/store/blob_replication/__init__.py +7 -0
- node/planes/store/blob_replication/compartment.py +496 -0
- node/planes/store/blob_replication/types.py +35 -0
- node/planes/store/blob_wire/__init__.py +48 -0
- node/planes/store/blob_wire/client.py +57 -0
- node/planes/store/blob_wire/core.py +359 -0
- node/planes/store/blob_wire/intake.py +74 -0
- node/planes/store/blob_wire/messages.py +85 -0
- node/planes/store/projected/__init__.py +7 -0
- node/planes/store/projected/compartment.py +258 -0
- node/planes/store/projected/types.py +29 -0
- node/planes/store/retention/__init__.py +7 -0
- node/planes/store/retention/compartment.py +214 -0
- node/planes/store/retention/types.py +12 -0
- node/planes/store/sync/__init__.py +35 -0
- node/planes/store/sync/core.py +311 -0
- node/planes/store/sync/envelope.py +106 -0
- node/planes/store/sync/handover.py +206 -0
- node/planes/store/sync/initiator.py +128 -0
- node/planes/store/sync/responder.py +114 -0
- node/planes/store/sync/rules.py +179 -0
- node/planes/store/sync/shape.py +80 -0
- node/planes/store/sync/types.py +51 -0
- node/planes/transport/__init__.py +23 -0
- node/planes/transport/bridge/__init__.py +13 -0
- node/planes/transport/bridge/compartment.py +785 -0
- node/planes/transport/bridge/ratelimit.py +44 -0
- node/planes/transport/bridge/types.py +43 -0
- node/planes/transport/lan/__init__.py +9 -0
- node/planes/transport/lan/compartment.py +446 -0
- node/planes/transport/lan/types.py +25 -0
- node/planes/transport/link/__init__.py +7 -0
- node/planes/transport/link/compartment.py +257 -0
- node/planes/transport/link/types.py +27 -0
- node/planes/transport/mediation/__init__.py +45 -0
- node/planes/transport/mediation/compartment.py +238 -0
- node/planes/transport/mediation/leaf.py +107 -0
- node/planes/transport/mediation/messages.py +302 -0
- node/planes/transport/mediation/service.py +233 -0
- node/planes/transport/mediation/store.py +37 -0
- node/planes/transport/mediation/types.py +20 -0
- node/planes/transport/p2p/__init__.py +7 -0
- node/planes/transport/p2p/compartment.py +349 -0
- node/planes/transport/p2p/types.py +22 -0
- node/protocols/__init__.py +277 -0
- node/protocols/activitypub/__init__.py +56 -0
- node/protocols/activitypub/actor.py +230 -0
- node/protocols/activitypub/federation.py +490 -0
- node/protocols/activitypub/media.py +145 -0
- node/protocols/activitypub/models.py +286 -0
- node/protocols/activitypub/package.py +271 -0
- node/protocols/activitypub/store.py +564 -0
- node/protocols/arcade/__init__.py +26 -0
- node/protocols/atproto/__init__.py +13 -0
- node/protocols/atproto/firehose.py +126 -0
- node/protocols/atproto/metering.py +165 -0
- node/protocols/atproto/package.py +155 -0
- node/protocols/atproto/repo.py +250 -0
- node/protocols/atproto/xrpc.py +285 -0
- node/protocols/base.py +109 -0
- node/protocols/circles/__init__.py +29 -0
- node/protocols/dm/__init__.py +50 -0
- node/protocols/grasp/__init__.py +22 -0
- node/protocols/isotime.py +15 -0
- node/protocols/nostr/__init__.py +51 -0
- node/protocols/nostr/blobstore.py +383 -0
- node/protocols/nostr/blossom.py +627 -0
- node/protocols/nostr/client.py +377 -0
- node/protocols/nostr/descriptor.py +211 -0
- node/protocols/nostr/eventstore.py +304 -0
- node/protocols/nostr/package.py +182 -0
- node/protocols/nostr/peers.py +109 -0
- node/protocols/nostr/relayserver.py +656 -0
- node/protocols/p2panda/__init__.py +22 -0
- node/protocols/p2panda/identity.py +19 -0
- node/protocols/p2panda/package.py +236 -0
- node/protocols/p2panda/rung.py +152 -0
- node/protocols/p2panda/store.py +273 -0
- node/protocols/rhio/__init__.py +33 -0
- node/protocols/rhio/package.py +275 -0
- node/protocols/rhio/rung.py +136 -0
- node/protocols/rhio/store.py +1423 -0
- node/protocols/ssb/muxrpc.py +256 -0
- node/protocols/ssb/package.py +267 -0
- node/protocols/textile/__init__.py +111 -0
- node/protocols/textile/transducers.py +259 -0
- node/release.py +119 -0
- node/storage/__init__.py +88 -0
- node/storage/accounts.py +305 -0
- node/storage/adapter.py +161 -0
- node/storage/base.py +385 -0
- node/storage/dialect.py +209 -0
- node/storage/grants.py +317 -0
- node/storage/locator.py +150 -0
- node/storage/postgres.py +294 -0
- node/storage/projected.py +953 -0
- node/storage/sharding.py +448 -0
- node/storage/standing.py +568 -0
- node/testnet/__init__.py +9 -0
- node/testnet/cluster.py +731 -0
- node/testnet/fips_daemon.py +230 -0
- node/transport/__init__.py +94 -0
- node/transport/const.py +110 -0
- node/transport/didcomm_inbound.py +251 -0
- node/transport/dnstxt.py +169 -0
- node/transport/fips_rung/__init__.py +67 -0
- node/transport/fips_rung/core.py +666 -0
- node/transport/fips_rung/listener.py +96 -0
- node/transport/fips_rung/types.py +12 -0
- node/transport/host.py +334 -0
- node/transport/iroh_rung/__init__.py +29 -0
- node/transport/iroh_rung/core.py +477 -0
- node/transport/iroh_rung/quic.py +942 -0
- node/transport/iroh_rung/types.py +22 -0
- node/transport/ladder.py +145 -0
- node/transport/lineproto.py +49 -0
- node/transport/mdns.py +303 -0
- node/transport/netfetch.py +283 -0
- node/transport/relay_rung/__init__.py +23 -0
- node/transport/relay_rung/cooldown.py +87 -0
- node/transport/relay_rung/core.py +138 -0
- node/transport/relay_rung/inbound.py +161 -0
- node/transport/relay_rung/ledger.py +104 -0
- node/transport/relay_rung/listen.py +121 -0
- node/transport/relay_rung/outbound.py +181 -0
- node/transport/relay_rung/shape.py +71 -0
- node/transport/relay_rung/types.py +58 -0
- node/transport/relay_rung/wire.py +63 -0
- node/transport/router.py +761 -0
- node/transport/rung.py +276 -0
- node/transport/ssb_rung.py +166 -0
- node/transport/ws.py +502 -0
- sidecar/fleetmesh_announce.py +338 -0
- sidecar/fleetmesh_p2p.py +373 -0
- sidecar/fleetmesh_rhio.py +352 -0
- sidecar/fleetmesh_ssb.py +603 -0
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
"""compute/1.0 and /1.1 wire message types, ComputeWireError and the make_* builders.
|
|
2
|
+
|
|
3
|
+
Separate because the message vocabulary is needed by sealing, verification and the session alike and has no dependencies of its own beyond fleetmesh_core.crypto.
|
|
4
|
+
"""
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import uuid
|
|
8
|
+
from typing import Any, Mapping, Optional, Sequence
|
|
9
|
+
|
|
10
|
+
from fleetmesh_core.wire.wire_types import (
|
|
11
|
+
ComputeAcceptBody, ComputeCancelBody, ComputeProgressBody, ComputeQuoteBody, ComputeRefuteBody,
|
|
12
|
+
ComputeMode, ComputeSealedBody, ComputeSettledBody, ComputeStreamBody, Message, ProgressState,
|
|
13
|
+
ProofFailure, SealedVerify,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
PIURI: str = 'https://fleetmesh.org/compute/1.0'
|
|
17
|
+
PIURI_V11: str = 'https://fleetmesh.org/compute/1.1'
|
|
18
|
+
QUOTE_TYPE: str = 'https://fleetmesh.org/compute/1.0/quote'
|
|
19
|
+
ACCEPT_TYPE: str = 'https://fleetmesh.org/compute/1.0/accept'
|
|
20
|
+
SEALED_TYPE: str = 'https://fleetmesh.org/compute/1.0/sealed'
|
|
21
|
+
SETTLED_TYPE: str = 'https://fleetmesh.org/compute/1.0/settled'
|
|
22
|
+
REFUTE_TYPE: str = 'https://fleetmesh.org/compute/1.0/refute'
|
|
23
|
+
STREAM_TYPE: str = 'https://fleetmesh.org/compute/1.1/stream'
|
|
24
|
+
PROGRESS_TYPE: str = 'https://fleetmesh.org/compute/1.1/progress'
|
|
25
|
+
CANCEL_TYPE: str = 'https://fleetmesh.org/compute/1.1/cancel'
|
|
26
|
+
|
|
27
|
+
MESSAGES: tuple[str, ...] = (
|
|
28
|
+
'quote', 'accept', 'sealed', 'settled', 'refute', 'stream', 'progress', 'cancel'
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ComputeWireError(Exception):
|
|
33
|
+
"""Raised when a wire message or lifecycle transition is invalid."""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# -------------------------------------------------------------------------
|
|
37
|
+
# DIDComm compute/1.0 Message Builders
|
|
38
|
+
# -------------------------------------------------------------------------
|
|
39
|
+
def make_quote(
|
|
40
|
+
from_did: str,
|
|
41
|
+
to_did: str | Sequence[str],
|
|
42
|
+
ask_id: str,
|
|
43
|
+
price_msat: int,
|
|
44
|
+
invoice: str,
|
|
45
|
+
payment_hash: str,
|
|
46
|
+
thid: Optional[str] = None,
|
|
47
|
+
estimated_duration_s: Optional[int] = None,
|
|
48
|
+
created_time: Optional[int] = None,
|
|
49
|
+
msg_id: Optional[str] = None,
|
|
50
|
+
) -> Message[ComputeQuoteBody]:
|
|
51
|
+
"""Construct a DIDComm compute/1.0/quote message."""
|
|
52
|
+
to_list = [to_did] if isinstance(to_did, str) else list(to_did)
|
|
53
|
+
body: ComputeQuoteBody = {
|
|
54
|
+
'ask': ask_id,
|
|
55
|
+
'price_msat': int(price_msat),
|
|
56
|
+
'invoice': invoice,
|
|
57
|
+
'payment_hash': payment_hash,
|
|
58
|
+
}
|
|
59
|
+
if estimated_duration_s is not None:
|
|
60
|
+
body['estimated_duration_s'] = int(estimated_duration_s)
|
|
61
|
+
|
|
62
|
+
msg: Message[ComputeQuoteBody] = {
|
|
63
|
+
'id': msg_id or str(uuid.uuid4()),
|
|
64
|
+
'type': QUOTE_TYPE,
|
|
65
|
+
'from': from_did,
|
|
66
|
+
'to': to_list,
|
|
67
|
+
'body': body,
|
|
68
|
+
}
|
|
69
|
+
if thid:
|
|
70
|
+
msg['thid'] = thid
|
|
71
|
+
if created_time is not None:
|
|
72
|
+
msg['created_time'] = int(created_time)
|
|
73
|
+
return msg
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def make_accept(
|
|
77
|
+
from_did: str,
|
|
78
|
+
to_did: str | Sequence[str],
|
|
79
|
+
thid: str,
|
|
80
|
+
payment_hash: str,
|
|
81
|
+
job_payload: Mapping[str, Any],
|
|
82
|
+
input_key: Optional[str] = None,
|
|
83
|
+
created_time: Optional[int] = None,
|
|
84
|
+
msg_id: Optional[str] = None,
|
|
85
|
+
) -> Message[ComputeAcceptBody]:
|
|
86
|
+
"""Construct a DIDComm compute/1.0/accept message."""
|
|
87
|
+
to_list = [to_did] if isinstance(to_did, str) else list(to_did)
|
|
88
|
+
body: ComputeAcceptBody = {
|
|
89
|
+
'payment_hash': payment_hash,
|
|
90
|
+
'held': True,
|
|
91
|
+
'job': dict(job_payload),
|
|
92
|
+
}
|
|
93
|
+
if input_key is not None:
|
|
94
|
+
body['input_key'] = input_key
|
|
95
|
+
|
|
96
|
+
msg: Message[ComputeAcceptBody] = {
|
|
97
|
+
'id': msg_id or str(uuid.uuid4()),
|
|
98
|
+
'type': ACCEPT_TYPE,
|
|
99
|
+
'from': from_did,
|
|
100
|
+
'to': to_list,
|
|
101
|
+
'thid': thid,
|
|
102
|
+
'body': body,
|
|
103
|
+
}
|
|
104
|
+
if created_time is not None:
|
|
105
|
+
msg['created_time'] = int(created_time)
|
|
106
|
+
return msg
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def make_sealed(
|
|
110
|
+
from_did: str,
|
|
111
|
+
to_did: str | Sequence[str],
|
|
112
|
+
thid: str,
|
|
113
|
+
ciphertext: str,
|
|
114
|
+
payment_hash: str,
|
|
115
|
+
result: str,
|
|
116
|
+
fuel_used: int,
|
|
117
|
+
verify: Optional[SealedVerify] = None,
|
|
118
|
+
created_time: Optional[int] = None,
|
|
119
|
+
msg_id: Optional[str] = None,
|
|
120
|
+
) -> Message[ComputeSealedBody]:
|
|
121
|
+
"""Construct a DIDComm compute/1.0/sealed message."""
|
|
122
|
+
to_list = [to_did] if isinstance(to_did, str) else list(to_did)
|
|
123
|
+
body: ComputeSealedBody = {
|
|
124
|
+
'ciphertext': ciphertext,
|
|
125
|
+
'payment_hash': payment_hash,
|
|
126
|
+
'result': result,
|
|
127
|
+
'fuel_used': int(fuel_used),
|
|
128
|
+
}
|
|
129
|
+
if verify is not None:
|
|
130
|
+
body['verify'] = verify
|
|
131
|
+
|
|
132
|
+
msg: Message[ComputeSealedBody] = {
|
|
133
|
+
'id': msg_id or str(uuid.uuid4()),
|
|
134
|
+
'type': SEALED_TYPE,
|
|
135
|
+
'from': from_did,
|
|
136
|
+
'to': to_list,
|
|
137
|
+
'thid': thid,
|
|
138
|
+
'body': body,
|
|
139
|
+
}
|
|
140
|
+
if created_time is not None:
|
|
141
|
+
msg['created_time'] = int(created_time)
|
|
142
|
+
return msg
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def make_settled(
|
|
146
|
+
from_did: str,
|
|
147
|
+
to_did: str | Sequence[str],
|
|
148
|
+
thid: str,
|
|
149
|
+
payment_preimage: str,
|
|
150
|
+
receipt_event: Optional[str] = None,
|
|
151
|
+
created_time: Optional[int] = None,
|
|
152
|
+
msg_id: Optional[str] = None,
|
|
153
|
+
) -> Message[ComputeSettledBody]:
|
|
154
|
+
"""Construct a DIDComm compute/1.0/settled message."""
|
|
155
|
+
to_list = [to_did] if isinstance(to_did, str) else list(to_did)
|
|
156
|
+
body: ComputeSettledBody = {
|
|
157
|
+
'payment_preimage': payment_preimage,
|
|
158
|
+
}
|
|
159
|
+
if receipt_event is not None:
|
|
160
|
+
body['receipt_event'] = receipt_event
|
|
161
|
+
|
|
162
|
+
msg: Message[ComputeSettledBody] = {
|
|
163
|
+
'id': msg_id or str(uuid.uuid4()),
|
|
164
|
+
'type': SETTLED_TYPE,
|
|
165
|
+
'from': from_did,
|
|
166
|
+
'to': to_list,
|
|
167
|
+
'thid': thid,
|
|
168
|
+
'body': body,
|
|
169
|
+
}
|
|
170
|
+
if created_time is not None:
|
|
171
|
+
msg['created_time'] = int(created_time)
|
|
172
|
+
return msg
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def make_refute(
|
|
176
|
+
from_did: str,
|
|
177
|
+
to_did: str | Sequence[str],
|
|
178
|
+
thid: str,
|
|
179
|
+
job_receipt: str,
|
|
180
|
+
mode: ComputeMode,
|
|
181
|
+
claimed_result: Optional[str] = None,
|
|
182
|
+
actual_result: Optional[str] = None,
|
|
183
|
+
wasi_profile_digest: Optional[str] = None,
|
|
184
|
+
engine: Optional[str] = None,
|
|
185
|
+
engine_version: Optional[str] = None,
|
|
186
|
+
proof_failure: Optional[ProofFailure] = None,
|
|
187
|
+
created_time: Optional[int] = None,
|
|
188
|
+
msg_id: Optional[str] = None,
|
|
189
|
+
) -> Message[ComputeRefuteBody]:
|
|
190
|
+
"""Construct a DIDComm compute/1.0/refute message."""
|
|
191
|
+
to_list = [to_did] if isinstance(to_did, str) else list(to_did)
|
|
192
|
+
body: ComputeRefuteBody = {
|
|
193
|
+
'job_receipt': job_receipt,
|
|
194
|
+
'mode': mode,
|
|
195
|
+
}
|
|
196
|
+
if claimed_result is not None:
|
|
197
|
+
body['claimed_result'] = claimed_result
|
|
198
|
+
if actual_result is not None:
|
|
199
|
+
body['actual_result'] = actual_result
|
|
200
|
+
if wasi_profile_digest is not None:
|
|
201
|
+
body['wasi_profile_digest'] = wasi_profile_digest
|
|
202
|
+
if engine is not None:
|
|
203
|
+
body['engine'] = engine
|
|
204
|
+
if engine_version is not None:
|
|
205
|
+
body['engine_version'] = engine_version
|
|
206
|
+
if proof_failure is not None:
|
|
207
|
+
body['proof_failure'] = proof_failure
|
|
208
|
+
|
|
209
|
+
msg: Message[ComputeRefuteBody] = {
|
|
210
|
+
'id': msg_id or str(uuid.uuid4()),
|
|
211
|
+
'type': REFUTE_TYPE,
|
|
212
|
+
'from': from_did,
|
|
213
|
+
'to': to_list,
|
|
214
|
+
'thid': thid,
|
|
215
|
+
'body': body,
|
|
216
|
+
}
|
|
217
|
+
if created_time is not None:
|
|
218
|
+
msg['created_time'] = int(created_time)
|
|
219
|
+
return msg
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def make_stream_chunk(
|
|
223
|
+
from_did: str,
|
|
224
|
+
to_did: str | Sequence[str],
|
|
225
|
+
thid: str,
|
|
226
|
+
seq: int,
|
|
227
|
+
delta: str,
|
|
228
|
+
is_final: bool = False,
|
|
229
|
+
counters: Optional[Mapping[str, Any]] = None,
|
|
230
|
+
created_time: Optional[int] = None,
|
|
231
|
+
msg_id: Optional[str] = None,
|
|
232
|
+
) -> Message[ComputeStreamBody]:
|
|
233
|
+
"""Construct a DIDComm compute/1.1/stream chunk message."""
|
|
234
|
+
to_list = [to_did] if isinstance(to_did, str) else list(to_did)
|
|
235
|
+
body: ComputeStreamBody = {
|
|
236
|
+
'seq': int(seq),
|
|
237
|
+
'delta': str(delta),
|
|
238
|
+
}
|
|
239
|
+
if is_final:
|
|
240
|
+
body['is_final'] = True
|
|
241
|
+
if counters is not None:
|
|
242
|
+
body['counters'] = dict(counters)
|
|
243
|
+
|
|
244
|
+
msg: Message[ComputeStreamBody] = {
|
|
245
|
+
'id': msg_id or str(uuid.uuid4()),
|
|
246
|
+
'type': STREAM_TYPE,
|
|
247
|
+
'from': from_did,
|
|
248
|
+
'to': to_list,
|
|
249
|
+
'thid': thid,
|
|
250
|
+
'body': body,
|
|
251
|
+
}
|
|
252
|
+
if created_time is not None:
|
|
253
|
+
msg['created_time'] = int(created_time)
|
|
254
|
+
return msg
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def make_progress(
|
|
258
|
+
from_did: str,
|
|
259
|
+
to_did: str | Sequence[str],
|
|
260
|
+
thid: str,
|
|
261
|
+
state: ProgressState,
|
|
262
|
+
progress: Optional[float] = None,
|
|
263
|
+
elapsed_seconds: Optional[int] = None,
|
|
264
|
+
logs_tail: Optional[str] = None,
|
|
265
|
+
created_time: Optional[int] = None,
|
|
266
|
+
msg_id: Optional[str] = None,
|
|
267
|
+
) -> Message[ComputeProgressBody]:
|
|
268
|
+
"""Construct a DIDComm compute/1.1/progress message."""
|
|
269
|
+
to_list = [to_did] if isinstance(to_did, str) else list(to_did)
|
|
270
|
+
body: ComputeProgressBody = {
|
|
271
|
+
'state': state,
|
|
272
|
+
}
|
|
273
|
+
if progress is not None:
|
|
274
|
+
body['progress'] = float(progress)
|
|
275
|
+
if elapsed_seconds is not None:
|
|
276
|
+
body['elapsed_seconds'] = int(elapsed_seconds)
|
|
277
|
+
if logs_tail is not None:
|
|
278
|
+
body['logs_tail'] = str(logs_tail)
|
|
279
|
+
|
|
280
|
+
msg: Message[ComputeProgressBody] = {
|
|
281
|
+
'id': msg_id or str(uuid.uuid4()),
|
|
282
|
+
'type': PROGRESS_TYPE,
|
|
283
|
+
'from': from_did,
|
|
284
|
+
'to': to_list,
|
|
285
|
+
'thid': thid,
|
|
286
|
+
'body': body,
|
|
287
|
+
}
|
|
288
|
+
if created_time is not None:
|
|
289
|
+
msg['created_time'] = int(created_time)
|
|
290
|
+
return msg
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def make_cancel(
|
|
294
|
+
from_did: str,
|
|
295
|
+
to_did: str | Sequence[str],
|
|
296
|
+
thid: str,
|
|
297
|
+
reason: str,
|
|
298
|
+
penalty_msat: int = 0,
|
|
299
|
+
created_time: Optional[int] = None,
|
|
300
|
+
msg_id: Optional[str] = None,
|
|
301
|
+
) -> Message[ComputeCancelBody]:
|
|
302
|
+
"""Construct a DIDComm compute/1.1/cancel message."""
|
|
303
|
+
to_list = [to_did] if isinstance(to_did, str) else list(to_did)
|
|
304
|
+
body: ComputeCancelBody = {
|
|
305
|
+
'reason': str(reason),
|
|
306
|
+
}
|
|
307
|
+
if penalty_msat > 0:
|
|
308
|
+
body['penalty_msat'] = int(penalty_msat)
|
|
309
|
+
|
|
310
|
+
msg: Message[ComputeCancelBody] = {
|
|
311
|
+
'id': msg_id or str(uuid.uuid4()),
|
|
312
|
+
'type': CANCEL_TYPE,
|
|
313
|
+
'from': from_did,
|
|
314
|
+
'to': to_list,
|
|
315
|
+
'thid': thid,
|
|
316
|
+
'body': body,
|
|
317
|
+
}
|
|
318
|
+
if created_time is not None:
|
|
319
|
+
msg['created_time'] = int(created_time)
|
|
320
|
+
return msg
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"""seal_result / open_sealed: symmetric sealing of a job result under a preimage.
|
|
2
|
+
|
|
3
|
+
Separate because sealing is a self-contained primitive used by both the session and the dispute evaluator.
|
|
4
|
+
"""
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import hashlib
|
|
8
|
+
import os
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
from fleetmesh_core.crypto import nip44
|
|
12
|
+
from .messages import ComputeWireError
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
from .types import SealedResult
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# -------------------------------------------------------------------------
|
|
19
|
+
# Cryptographic Sealing & Accountable Settlement (Phase 4)
|
|
20
|
+
# -------------------------------------------------------------------------
|
|
21
|
+
def seal_result(raw_output: bytes | str, preimage_hex: Optional[str] = None) -> SealedResult:
|
|
22
|
+
"""Encrypt raw output under 32-byte preimage k using NIP-44 v2 ChaCha20/HMAC.
|
|
23
|
+
|
|
24
|
+
Returns dict containing:
|
|
25
|
+
ciphertext: base64-encoded encrypted payload
|
|
26
|
+
payment_hash: SHA-256(k) hex
|
|
27
|
+
payment_preimage: k as 64 hex characters
|
|
28
|
+
result: SHA-256 hash of raw_output bytes
|
|
29
|
+
"""
|
|
30
|
+
if preimage_hex is None:
|
|
31
|
+
preimage_hex = os.urandom(32).hex()
|
|
32
|
+
k_bytes = bytes.fromhex(preimage_hex)
|
|
33
|
+
if len(k_bytes) != 32:
|
|
34
|
+
raise ComputeWireError('preimage must be 32 bytes')
|
|
35
|
+
|
|
36
|
+
payment_hash = hashlib.sha256(k_bytes).hexdigest()
|
|
37
|
+
raw_bytes = raw_output.encode('utf-8') if isinstance(raw_output, str) else raw_output
|
|
38
|
+
result_hash = hashlib.sha256(raw_bytes).hexdigest()
|
|
39
|
+
|
|
40
|
+
ciphertext = nip44.encrypt(raw_bytes, k_bytes)
|
|
41
|
+
return {
|
|
42
|
+
'ciphertext': ciphertext,
|
|
43
|
+
'payment_hash': payment_hash,
|
|
44
|
+
'payment_preimage': preimage_hex,
|
|
45
|
+
'result': result_hash,
|
|
46
|
+
'result_hash': result_hash,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def open_sealed(
|
|
51
|
+
ciphertext: str,
|
|
52
|
+
preimage_hex: str,
|
|
53
|
+
expected_payment_hash: Optional[str] = None,
|
|
54
|
+
expected_result: Optional[str] = None,
|
|
55
|
+
) -> tuple[bool, Optional[bytes], Optional[str]]:
|
|
56
|
+
"""Decrypt sealed output with preimage k and verify SHA-256 hashes.
|
|
57
|
+
|
|
58
|
+
Returns (success, decrypted_bytes, failure_code).
|
|
59
|
+
Failure codes match fleetmesh_core/market.py admissible evidence:
|
|
60
|
+
- 'sealed-key-does-not-open'
|
|
61
|
+
- 'result-hash-mismatch-after-decrypt'
|
|
62
|
+
- 'payment-hash-mismatch'
|
|
63
|
+
"""
|
|
64
|
+
try:
|
|
65
|
+
k_bytes = bytes.fromhex(preimage_hex)
|
|
66
|
+
except Exception:
|
|
67
|
+
return False, None, 'sealed-key-does-not-open'
|
|
68
|
+
|
|
69
|
+
if len(k_bytes) != 32:
|
|
70
|
+
return False, None, 'sealed-key-does-not-open'
|
|
71
|
+
|
|
72
|
+
if expected_payment_hash:
|
|
73
|
+
actual_payment_hash = hashlib.sha256(k_bytes).hexdigest()
|
|
74
|
+
if actual_payment_hash != expected_payment_hash:
|
|
75
|
+
return False, None, 'payment-hash-mismatch'
|
|
76
|
+
|
|
77
|
+
try:
|
|
78
|
+
decrypted = nip44.decrypt_bytes(ciphertext, k_bytes)
|
|
79
|
+
except Exception:
|
|
80
|
+
return False, None, 'sealed-key-does-not-open'
|
|
81
|
+
|
|
82
|
+
if expected_result:
|
|
83
|
+
actual_result_hash = hashlib.sha256(decrypted).hexdigest()
|
|
84
|
+
if actual_result_hash != expected_result:
|
|
85
|
+
return False, decrypted, 'result-hash-mismatch-after-decrypt'
|
|
86
|
+
|
|
87
|
+
return True, decrypted, None
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def encrypt_stream_frame(
|
|
91
|
+
session_key: bytes,
|
|
92
|
+
seq: int,
|
|
93
|
+
delta: str = '',
|
|
94
|
+
plaintext: Optional[str] = None,
|
|
95
|
+
) -> str:
|
|
96
|
+
"""Encrypt a streaming token delta with ChaCha20Poly1305.
|
|
97
|
+
|
|
98
|
+
Framing per non-deterministic-compute.md § 4.1:
|
|
99
|
+
Frame_i = ChaCha20Poly1305(k_session, seq_i || delta_text)
|
|
100
|
+
Nonce (12 bytes): 4 zero bytes + 8 bytes big-endian seq.
|
|
101
|
+
AAD: 8 bytes big-endian seq.
|
|
102
|
+
Returns: base64-encoded ciphertext (including 16-byte Poly1305 tag).
|
|
103
|
+
"""
|
|
104
|
+
import base64
|
|
105
|
+
from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
|
|
106
|
+
|
|
107
|
+
text = delta if plaintext is None else plaintext
|
|
108
|
+
if len(session_key) != 32:
|
|
109
|
+
raise ComputeWireError('session key must be 32 bytes')
|
|
110
|
+
if seq < 0:
|
|
111
|
+
raise ComputeWireError('stream seq cannot be negative')
|
|
112
|
+
|
|
113
|
+
nonce = b'\x00' * 4 + int(seq).to_bytes(8, 'big')
|
|
114
|
+
aad = int(seq).to_bytes(8, 'big')
|
|
115
|
+
cipher = ChaCha20Poly1305(session_key)
|
|
116
|
+
ct = cipher.encrypt(nonce, text.encode('utf-8'), aad)
|
|
117
|
+
return base64.b64encode(ct).decode('ascii')
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def decrypt_stream_frame(
|
|
121
|
+
session_key: bytes,
|
|
122
|
+
seq: int,
|
|
123
|
+
frame_b64: str = '',
|
|
124
|
+
ciphertext_b64: Optional[str] = None,
|
|
125
|
+
) -> str:
|
|
126
|
+
"""Decrypt a ChaCha20Poly1305 streaming token frame.
|
|
127
|
+
|
|
128
|
+
Returns decrypted delta string, or raises ComputeWireError if authentication fails.
|
|
129
|
+
"""
|
|
130
|
+
import base64
|
|
131
|
+
from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
|
|
132
|
+
|
|
133
|
+
b64_in = frame_b64 if ciphertext_b64 is None else ciphertext_b64
|
|
134
|
+
if len(session_key) != 32:
|
|
135
|
+
raise ComputeWireError('session key must be 32 bytes')
|
|
136
|
+
if seq < 0:
|
|
137
|
+
raise ComputeWireError('stream seq cannot be negative')
|
|
138
|
+
|
|
139
|
+
nonce = b'\x00' * 4 + int(seq).to_bytes(8, 'big')
|
|
140
|
+
aad = int(seq).to_bytes(8, 'big')
|
|
141
|
+
cipher = ChaCha20Poly1305(session_key)
|
|
142
|
+
try:
|
|
143
|
+
raw_ct = base64.b64decode(b64_in.encode('ascii'))
|
|
144
|
+
pt = cipher.decrypt(nonce, raw_ct, aad)
|
|
145
|
+
return pt.decode('utf-8')
|
|
146
|
+
except Exception as e:
|
|
147
|
+
raise ComputeWireError(f'stream frame decryption failed at seq {seq}: {e}')
|