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,244 @@
|
|
|
1
|
+
"""fleetmesh-wasi-v1: the WASI preview1 runner on wasmtime-py, and WasmExecutor.
|
|
2
|
+
|
|
3
|
+
wasmtime is optional. Everything the deterministic profile fixes (clock, random,
|
|
4
|
+
NaN canonicalisation, no filesystem, no network) is set up in run_wasi_module.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import hashlib
|
|
9
|
+
import os
|
|
10
|
+
from typing import Any, Mapping, Sequence
|
|
11
|
+
|
|
12
|
+
from fleetmesh_core.crypto import crypto
|
|
13
|
+
from ..common import ComputeError, resolve_blob
|
|
14
|
+
from ..sandbox import ComputeLimits, NODE_MAX_MEMORY_BYTES
|
|
15
|
+
from .base import ComputeExecutionResult, ComputeExecutor, ComputeQuote
|
|
16
|
+
|
|
17
|
+
# -------------------------------------------------------------------------
|
|
18
|
+
# fleetmesh-wasi-v1 runtime (wasmtime-py)
|
|
19
|
+
# -------------------------------------------------------------------------
|
|
20
|
+
#
|
|
21
|
+
# Profile: WASI preview1, stdin = job input, stdout = result, no preopened
|
|
22
|
+
# directories (no filesystem), no environment, no network (preview1 has no
|
|
23
|
+
# sockets), clock_time_get / clock_res_get fixed at WASM_VIRTUAL_EPOCH_NS,
|
|
24
|
+
# random_get a SHA-256 stream seeded from the job nonce, NaNs canonicalised,
|
|
25
|
+
# threads/SIMD-relaxed off. Two nodes running the same module on the same
|
|
26
|
+
# input and nonce must produce byte-identical output and fuel.
|
|
27
|
+
#
|
|
28
|
+
# wasmtime is optional: a node without it refuses WASM jobs with a clear
|
|
29
|
+
# error instead of returning a fake result.
|
|
30
|
+
|
|
31
|
+
WASM_DEFAULT_MEMORY_BYTES: int = 256 * 1024 * 1024
|
|
32
|
+
WASM_MAX_OUTPUT_BYTES: int = 16 * 1024 * 1024
|
|
33
|
+
WASM_VIRTUAL_EPOCH_NS: int = 1_700_000_000 * 1_000_000_000
|
|
34
|
+
|
|
35
|
+
_WASM_ENGINE: Any = None
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def wasmtime_available() -> bool:
|
|
39
|
+
# importlib.util.find_spec rather than a bare import: the module object
|
|
40
|
+
# is not needed here, and pyflakes cannot see through a noqa.
|
|
41
|
+
import importlib.util
|
|
42
|
+
return importlib.util.find_spec('wasmtime') is not None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _wasm_engine() -> Any:
|
|
46
|
+
global _WASM_ENGINE
|
|
47
|
+
if _WASM_ENGINE is None:
|
|
48
|
+
import wasmtime
|
|
49
|
+
cfg = wasmtime.Config()
|
|
50
|
+
cfg.consume_fuel = True
|
|
51
|
+
cfg.cranelift_nan_canonicalization = True
|
|
52
|
+
cfg.wasm_threads = False
|
|
53
|
+
cfg.wasm_relaxed_simd = False
|
|
54
|
+
cfg.parallel_compilation = False
|
|
55
|
+
# Default wasmtime reserves 4 GiB+guard of address space per memory,
|
|
56
|
+
# which cannot be mapped under the sandbox's RLIMIT_AS. Reserve
|
|
57
|
+
# nothing up front and let memories move on growth instead.
|
|
58
|
+
cfg.memory_reservation = 0
|
|
59
|
+
cfg.memory_guard_size = 64 * 1024
|
|
60
|
+
cfg.memory_reservation_for_growth = 1024 * 1024
|
|
61
|
+
cfg.memory_may_move = True
|
|
62
|
+
if hasattr(type(cfg), 'macos_use_mach_ports'):
|
|
63
|
+
try:
|
|
64
|
+
cfg.macos_use_mach_ports = False
|
|
65
|
+
except Exception:
|
|
66
|
+
pass
|
|
67
|
+
_WASM_ENGINE = wasmtime.Engine(cfg)
|
|
68
|
+
return _WASM_ENGINE
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def run_wasi_module(
|
|
72
|
+
module_bytes: bytes,
|
|
73
|
+
input_bytes: bytes,
|
|
74
|
+
fuel_cap: int,
|
|
75
|
+
memory_bytes: int = WASM_DEFAULT_MEMORY_BYTES,
|
|
76
|
+
seed: str = '',
|
|
77
|
+
args: Sequence[str] = (),
|
|
78
|
+
) -> tuple[bytes, int]:
|
|
79
|
+
"""Run a WASI command module under fleetmesh-wasi-v1. Returns (stdout, fuel)."""
|
|
80
|
+
try:
|
|
81
|
+
import wasmtime
|
|
82
|
+
except ImportError as e:
|
|
83
|
+
raise ComputeError('WASM executor unavailable: install wasmtime (pip install wasmtime) [%s]' % e)
|
|
84
|
+
import tempfile
|
|
85
|
+
|
|
86
|
+
engine = _wasm_engine()
|
|
87
|
+
try:
|
|
88
|
+
module = wasmtime.Module(engine, module_bytes)
|
|
89
|
+
except wasmtime.WasmtimeError as e:
|
|
90
|
+
raise ComputeError('invalid WASM module: %s' % str(e).splitlines()[0])
|
|
91
|
+
|
|
92
|
+
store = wasmtime.Store(engine)
|
|
93
|
+
store.set_fuel(int(fuel_cap))
|
|
94
|
+
store.set_limits(memory_size=int(memory_bytes), instances=1, tables=4, memories=1)
|
|
95
|
+
|
|
96
|
+
out = bytearray()
|
|
97
|
+
overflow = [False]
|
|
98
|
+
|
|
99
|
+
def _stdout(chunk: bytes) -> None:
|
|
100
|
+
if len(out) + len(chunk) > WASM_MAX_OUTPUT_BYTES:
|
|
101
|
+
overflow[0] = True
|
|
102
|
+
return None
|
|
103
|
+
out.extend(chunk)
|
|
104
|
+
return None
|
|
105
|
+
|
|
106
|
+
with tempfile.TemporaryDirectory(prefix='fleetmesh-wasi-') as tmp:
|
|
107
|
+
in_path = os.path.join(tmp, 'stdin')
|
|
108
|
+
with open(in_path, 'wb') as f:
|
|
109
|
+
f.write(input_bytes)
|
|
110
|
+
wc = wasmtime.WasiConfig()
|
|
111
|
+
wc.argv = ['job.wasm', *args]
|
|
112
|
+
wc.stdin_file = in_path
|
|
113
|
+
wc.stdout_custom = _stdout
|
|
114
|
+
store.set_wasi(wc)
|
|
115
|
+
|
|
116
|
+
linker = wasmtime.Linker(engine)
|
|
117
|
+
linker.define_wasi()
|
|
118
|
+
linker.allow_shadowing = True
|
|
119
|
+
i32, i64 = wasmtime.ValType.i32(), wasmtime.ValType.i64()
|
|
120
|
+
|
|
121
|
+
def _clock_get(caller: Any, _cid: int, _prec: int, ptr: int) -> int:
|
|
122
|
+
caller['memory'].write(caller, WASM_VIRTUAL_EPOCH_NS.to_bytes(8, 'little'), ptr)
|
|
123
|
+
return 0
|
|
124
|
+
|
|
125
|
+
def _clock_res(caller: Any, _cid: int, ptr: int) -> int:
|
|
126
|
+
caller['memory'].write(caller, (1).to_bytes(8, 'little'), ptr)
|
|
127
|
+
return 0
|
|
128
|
+
|
|
129
|
+
rng_state = {'ctr': 0}
|
|
130
|
+
rng_key = hashlib.sha256(b'fleetmesh-wasi-v1/random:' + seed.encode('utf-8')).digest()
|
|
131
|
+
|
|
132
|
+
def _random(caller: Any, ptr: int, n: int) -> int:
|
|
133
|
+
buf = bytearray()
|
|
134
|
+
while len(buf) < n:
|
|
135
|
+
buf += hashlib.sha256(rng_key + rng_state['ctr'].to_bytes(8, 'big')).digest()
|
|
136
|
+
rng_state['ctr'] += 1
|
|
137
|
+
caller['memory'].write(caller, bytes(buf[:n]), ptr)
|
|
138
|
+
return 0
|
|
139
|
+
|
|
140
|
+
ns = 'wasi_snapshot_preview1'
|
|
141
|
+
linker.define_func(ns, 'clock_time_get', wasmtime.FuncType([i32, i64, i32], [i32]), _clock_get, access_caller=True)
|
|
142
|
+
linker.define_func(ns, 'clock_res_get', wasmtime.FuncType([i32, i32], [i32]), _clock_res, access_caller=True)
|
|
143
|
+
linker.define_func(ns, 'random_get', wasmtime.FuncType([i32, i32], [i32]), _random, access_caller=True)
|
|
144
|
+
|
|
145
|
+
try:
|
|
146
|
+
instance = linker.instantiate(store, module)
|
|
147
|
+
start = instance.exports(store).get('_start')
|
|
148
|
+
# A module may export '_start' as a memory/table/global to confuse
|
|
149
|
+
# a runner; only a real function is a WASI command entry point.
|
|
150
|
+
if not isinstance(start, wasmtime.Func):
|
|
151
|
+
raise ComputeError('WASM module has no _start export (not a WASI command)')
|
|
152
|
+
start(store)
|
|
153
|
+
except wasmtime.ExitTrap as e:
|
|
154
|
+
if e.code != 0:
|
|
155
|
+
raise ComputeError('WASM module exited with status %d' % e.code)
|
|
156
|
+
except wasmtime.Trap as e:
|
|
157
|
+
if e.trap_code == wasmtime.TrapCode.OUT_OF_FUEL:
|
|
158
|
+
raise ComputeError('job execution exceeded fuel ceiling (%d)' % fuel_cap)
|
|
159
|
+
raise ComputeError('WASM trap: %s' % str(e).splitlines()[0])
|
|
160
|
+
except wasmtime.WasmtimeError as e:
|
|
161
|
+
raise ComputeError('WASM error: %s' % str(e).splitlines()[0])
|
|
162
|
+
|
|
163
|
+
if overflow[0]:
|
|
164
|
+
raise ComputeError('WASM output exceeded %d bytes' % WASM_MAX_OUTPUT_BYTES)
|
|
165
|
+
return bytes(out), int(fuel_cap) - int(store.get_fuel())
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class WasmExecutor(ComputeExecutor):
|
|
169
|
+
"""Deterministic WASI runtime driver (fleetmesh-wasi-v1 profile)."""
|
|
170
|
+
|
|
171
|
+
engine_id: str = 'wasmtime'
|
|
172
|
+
wasi_profile_digest: str = hashlib.sha256(
|
|
173
|
+
b'fleetmesh-wasi-v1:network=none:fs=memory:clock=virtual:prng=seeded'
|
|
174
|
+
).hexdigest()
|
|
175
|
+
|
|
176
|
+
def is_live_runnable(self) -> bool:
|
|
177
|
+
"""Check whether wasmtime engine is installed and available on this host."""
|
|
178
|
+
return wasmtime_available()
|
|
179
|
+
|
|
180
|
+
def quote(
|
|
181
|
+
self,
|
|
182
|
+
module_spec: Mapping[str, Any],
|
|
183
|
+
input_spec: Mapping[str, Any],
|
|
184
|
+
limits: ComputeLimits,
|
|
185
|
+
) -> ComputeQuote:
|
|
186
|
+
mod_len = len(str(module_spec.get('sha256', '')))
|
|
187
|
+
inp_len = len(str(input_spec.get('sha256', '')))
|
|
188
|
+
fuel_est = min(25000 + (mod_len + inp_len) * 250, limits.max_fuel)
|
|
189
|
+
return ComputeQuote(
|
|
190
|
+
fuel_estimate=fuel_est,
|
|
191
|
+
rate_unit='msat_per_mfuel',
|
|
192
|
+
rate_amount=45,
|
|
193
|
+
engine_id=self.engine_id,
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
def execute(
|
|
197
|
+
self,
|
|
198
|
+
job_payload: Mapping[str, Any],
|
|
199
|
+
fuel_cap: int,
|
|
200
|
+
) -> ComputeExecutionResult:
|
|
201
|
+
module_spec = job_payload.get('module', {})
|
|
202
|
+
input_spec = job_payload.get('input', {})
|
|
203
|
+
|
|
204
|
+
blob_store = job_payload.get('blob_store')
|
|
205
|
+
mod_bytes = resolve_blob(module_spec, blob_store=blob_store, blossom_servers=job_payload.get('blossom_servers'))
|
|
206
|
+
inp_bytes = resolve_blob(input_spec, blob_store=blob_store, blossom_servers=job_payload.get('blossom_servers'))
|
|
207
|
+
if mod_bytes is None:
|
|
208
|
+
raise ComputeError('WASM module not resolvable (sha256=%s)' % module_spec.get('sha256', '?'))
|
|
209
|
+
want = str(module_spec.get('sha256') or '').lower()
|
|
210
|
+
if want.startswith('baf'):
|
|
211
|
+
# A CID commits to the same digest; decode it rather than skip
|
|
212
|
+
# the check. A CID that does not decode is a refusal, not a pass.
|
|
213
|
+
from node.protocols.nostr import blobstore
|
|
214
|
+
try:
|
|
215
|
+
want = blobstore.cid_to_sha256(want)
|
|
216
|
+
except Exception as e:
|
|
217
|
+
raise ComputeError('WASM module CID does not decode: %s' % e)
|
|
218
|
+
if want and hashlib.sha256(mod_bytes).hexdigest() != want:
|
|
219
|
+
raise ComputeError('WASM module bytes do not match declared sha256')
|
|
220
|
+
|
|
221
|
+
# The requester may ask for less linear memory than the operator
|
|
222
|
+
# ceiling, never more: run_job passes the clamped ComputeLimits
|
|
223
|
+
# ceiling as '_memory_ceiling_bytes'; without it (direct execute)
|
|
224
|
+
# NODE_MAX_MEMORY_BYTES applies. A wasm cap above the rlimit would let
|
|
225
|
+
# the module get killed by the sandbox instead of trapping cleanly.
|
|
226
|
+
ceiling = int(job_payload.get('_memory_ceiling_bytes') or NODE_MAX_MEMORY_BYTES)
|
|
227
|
+
ceiling = max(1, min(ceiling, NODE_MAX_MEMORY_BYTES))
|
|
228
|
+
requested = int(job_payload.get('wasm_max_memory_bytes') or WASM_DEFAULT_MEMORY_BYTES)
|
|
229
|
+
memory_bytes = max(1, min(requested, ceiling))
|
|
230
|
+
|
|
231
|
+
raw_output, fuel_used = run_wasi_module(
|
|
232
|
+
mod_bytes, inp_bytes or b'', fuel_cap,
|
|
233
|
+
memory_bytes=memory_bytes,
|
|
234
|
+
seed=str(job_payload.get('nonce') or job_payload.get('ask') or ''),
|
|
235
|
+
args=[str(a) for a in (job_payload.get('args') or [])],
|
|
236
|
+
)
|
|
237
|
+
result_hash = crypto.sha256(raw_output)
|
|
238
|
+
return ComputeExecutionResult(
|
|
239
|
+
result_hash=result_hash,
|
|
240
|
+
raw_output=raw_output,
|
|
241
|
+
fuel_used=fuel_used,
|
|
242
|
+
engine_id=self.engine_id,
|
|
243
|
+
wasi_profile_digest=self.wasi_profile_digest,
|
|
244
|
+
)
|