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,604 @@
|
|
|
1
|
+
"""Zero-knowledge executors: Groth16 verification, zkVM receipt verifier, and prover drivers.
|
|
2
|
+
|
|
3
|
+
Implements Phase 3 of the compute roadmap:
|
|
4
|
+
- ZkVmReceiptResult: Structured output from zkVM receipt verification.
|
|
5
|
+
- ZkVmVerifierProtocol: Abstract interface for STARK/SNARK zkVM verifiers.
|
|
6
|
+
- ZkVmReceiptVerifier: Production verifier supporting RISC Zero & SP1 STARK-to-Groth16.
|
|
7
|
+
- MockZkVmVerifier: Deterministic test double for testing and fixtures.
|
|
8
|
+
- ZkVmExecutor: Full zkVM executor driving receipt verification with honesty gating.
|
|
9
|
+
- ZkProverProtocol: Interface for off-chain ZK prover services.
|
|
10
|
+
- MockZkProverClient: Test double for prover order-book simulation.
|
|
11
|
+
- ZkProverExecutor: Order-book executor driver for proof generation asks.
|
|
12
|
+
"""
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import hashlib
|
|
16
|
+
import json
|
|
17
|
+
from typing import Any, Mapping, NamedTuple, Optional, Protocol, Sequence
|
|
18
|
+
|
|
19
|
+
from fleetmesh_core.crypto import crypto, zk
|
|
20
|
+
from fleetmesh_core.crypto.zk import format_hex32
|
|
21
|
+
from ..common import ComputeError
|
|
22
|
+
from ..sandbox import ComputeLimits
|
|
23
|
+
from .base import ComputeExecutionResult, ComputeExecutor, ComputeQuote
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ZkGroth16Executor(ComputeExecutor):
|
|
27
|
+
"""Zero-Knowledge Groth16 proof verifier runtime (compute:zk-verify-groth16/1.0)."""
|
|
28
|
+
|
|
29
|
+
engine_id: str = 'zk-verify-groth16'
|
|
30
|
+
wasi_profile_digest: str = hashlib.sha256(
|
|
31
|
+
b'compute:zk-verify-groth16/1.0:curves=bn254,bls12_381'
|
|
32
|
+
).hexdigest()
|
|
33
|
+
|
|
34
|
+
def is_live_runnable(self) -> bool:
|
|
35
|
+
"""Groth16 verification is pure cryptographic evaluation and is always operational."""
|
|
36
|
+
return True
|
|
37
|
+
|
|
38
|
+
def quote(
|
|
39
|
+
self,
|
|
40
|
+
module_spec: Mapping[str, Any],
|
|
41
|
+
input_spec: Mapping[str, Any],
|
|
42
|
+
limits: ComputeLimits,
|
|
43
|
+
) -> ComputeQuote:
|
|
44
|
+
num_inputs = len(input_spec.get('public_inputs', []))
|
|
45
|
+
fuel_est = min(50000 + 4 * 250000 + num_inputs * 10000, limits.max_fuel)
|
|
46
|
+
return ComputeQuote(
|
|
47
|
+
fuel_estimate=fuel_est,
|
|
48
|
+
rate_unit='msat_per_mfuel',
|
|
49
|
+
rate_amount=50,
|
|
50
|
+
engine_id=self.engine_id,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
def execute(
|
|
54
|
+
self,
|
|
55
|
+
job_payload: Mapping[str, Any],
|
|
56
|
+
fuel_cap: int,
|
|
57
|
+
) -> ComputeExecutionResult:
|
|
58
|
+
input_spec = job_payload.get('input', {})
|
|
59
|
+
if not input_spec:
|
|
60
|
+
raise ComputeError('zk-verify-groth16 requires job input specification')
|
|
61
|
+
|
|
62
|
+
custom_vk = None
|
|
63
|
+
if 'vk' in input_spec and isinstance(input_spec['vk'], dict):
|
|
64
|
+
try:
|
|
65
|
+
custom_vk = zk.VerificationKey.from_dict(input_spec['vk'])
|
|
66
|
+
except Exception as e:
|
|
67
|
+
raise ComputeError(f"invalid verification key specification: {e}")
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
verif_res = zk.verify_groth16_job(input_spec, custom_vk=custom_vk)
|
|
71
|
+
except Exception as e:
|
|
72
|
+
raise ComputeError(f"Groth16 verification failed: {e}")
|
|
73
|
+
|
|
74
|
+
raw_output = json.dumps(verif_res, sort_keys=True, separators=(',', ':')).encode('utf-8')
|
|
75
|
+
result_hash = crypto.sha256(raw_output)
|
|
76
|
+
|
|
77
|
+
num_inputs = len(input_spec.get('public_inputs', []))
|
|
78
|
+
pairing_count = verif_res.get('pairing_count', 4)
|
|
79
|
+
fuel_used = 50000 + pairing_count * 250000 + num_inputs * 10000
|
|
80
|
+
|
|
81
|
+
if fuel_used > fuel_cap:
|
|
82
|
+
raise ComputeError(
|
|
83
|
+
'job execution exceeded fuel ceiling (%d > %d)' % (fuel_used, fuel_cap)
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
return ComputeExecutionResult(
|
|
87
|
+
result_hash=result_hash,
|
|
88
|
+
raw_output=raw_output,
|
|
89
|
+
fuel_used=fuel_used,
|
|
90
|
+
engine_id=self.engine_id,
|
|
91
|
+
wasi_profile_digest=self.wasi_profile_digest,
|
|
92
|
+
verification_mode=self.verification_mode(job_payload),
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class ZkVmReceiptResult(NamedTuple):
|
|
97
|
+
"""Result of validating a STARK / SNARK zkVM receipt."""
|
|
98
|
+
valid: bool
|
|
99
|
+
image_id: str
|
|
100
|
+
journal: bytes
|
|
101
|
+
journal_hash: str
|
|
102
|
+
proof_system: str
|
|
103
|
+
cycles: int = 0
|
|
104
|
+
error: str = ''
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class ZkVmVerifierProtocol(Protocol):
|
|
108
|
+
"""Protocol interface for zkVM receipt verifier engines."""
|
|
109
|
+
|
|
110
|
+
def verify_receipt(
|
|
111
|
+
self,
|
|
112
|
+
image_id: str,
|
|
113
|
+
journal: bytes,
|
|
114
|
+
seal: bytes | Mapping[str, Any],
|
|
115
|
+
proof_system: str = 'risc0-groth16',
|
|
116
|
+
custom_vk: Optional[Any] = None,
|
|
117
|
+
) -> ZkVmReceiptResult:
|
|
118
|
+
"""Verify the cryptographic validity of a zkVM execution receipt."""
|
|
119
|
+
...
|
|
120
|
+
|
|
121
|
+
def is_available(self) -> bool:
|
|
122
|
+
"""Check whether the verifier backend is operational on this host."""
|
|
123
|
+
...
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class ZkVmReceiptVerifier:
|
|
127
|
+
"""Production zkVM receipt verifier supporting STARK-to-Groth16 wrapping."""
|
|
128
|
+
|
|
129
|
+
def __init__(self, pinned_control_ids: Optional[Sequence[str]] = None) -> None:
|
|
130
|
+
self.pinned_control_ids = list(pinned_control_ids or [])
|
|
131
|
+
|
|
132
|
+
def is_available(self) -> bool:
|
|
133
|
+
"""Receipt verification over BN254 Groth16 is always available via pure-Python pairing."""
|
|
134
|
+
return True
|
|
135
|
+
|
|
136
|
+
def verify_receipt(
|
|
137
|
+
self,
|
|
138
|
+
image_id: str,
|
|
139
|
+
journal: bytes,
|
|
140
|
+
seal: bytes | Mapping[str, Any] | str,
|
|
141
|
+
proof_system: str = 'risc0-groth16',
|
|
142
|
+
custom_vk: Optional[Any] = None,
|
|
143
|
+
) -> ZkVmReceiptResult:
|
|
144
|
+
norm_system = str(proof_system or 'risc0-groth16').lower()
|
|
145
|
+
journal_hash = crypto.sha256(journal)
|
|
146
|
+
clean_image_id = str(image_id or '').lower().removeprefix('0x')
|
|
147
|
+
|
|
148
|
+
if not clean_image_id:
|
|
149
|
+
return ZkVmReceiptResult(
|
|
150
|
+
valid=False,
|
|
151
|
+
image_id='',
|
|
152
|
+
journal=journal,
|
|
153
|
+
journal_hash=journal_hash,
|
|
154
|
+
proof_system=norm_system,
|
|
155
|
+
error='missing image_id in receipt specification',
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# 1. Groth16-wrapped STARK receipts (RISC Zero / SP1)
|
|
159
|
+
if norm_system in ('risc0-groth16', 'sp1-groth16', 'groth16', 'zkvm-risc0', 'zkvm'):
|
|
160
|
+
proof_dict: Mapping[str, Any] = {}
|
|
161
|
+
if isinstance(seal, Mapping):
|
|
162
|
+
proof_dict = seal
|
|
163
|
+
elif isinstance(seal, (bytes, bytearray)):
|
|
164
|
+
try:
|
|
165
|
+
proof_dict = json.loads(seal.decode('utf-8'))
|
|
166
|
+
except Exception:
|
|
167
|
+
return ZkVmReceiptResult(
|
|
168
|
+
valid=False,
|
|
169
|
+
image_id=clean_image_id,
|
|
170
|
+
journal=journal,
|
|
171
|
+
journal_hash=journal_hash,
|
|
172
|
+
proof_system=norm_system,
|
|
173
|
+
error='seal is not valid Groth16 proof format',
|
|
174
|
+
)
|
|
175
|
+
elif isinstance(seal, str) and seal.startswith('{'):
|
|
176
|
+
try:
|
|
177
|
+
proof_dict = json.loads(seal)
|
|
178
|
+
except Exception:
|
|
179
|
+
pass
|
|
180
|
+
|
|
181
|
+
if not proof_dict or 'a' not in proof_dict:
|
|
182
|
+
return ZkVmReceiptResult(
|
|
183
|
+
valid=False,
|
|
184
|
+
image_id=clean_image_id,
|
|
185
|
+
journal=journal,
|
|
186
|
+
journal_hash=journal_hash,
|
|
187
|
+
proof_system=norm_system,
|
|
188
|
+
error='seal missing required Groth16 curve coordinates (a, b, c)',
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
# Public inputs in RISC Zero / SP1 Groth16 receipts encode the claim digest:
|
|
192
|
+
# [image_id, journal_hash]
|
|
193
|
+
public_inputs = proof_dict.get('public_inputs') or [
|
|
194
|
+
format_hex32(int(clean_image_id[:32] or '0', 16)),
|
|
195
|
+
format_hex32(int(journal_hash[:32] or '0', 16)),
|
|
196
|
+
]
|
|
197
|
+
|
|
198
|
+
vk = custom_vk
|
|
199
|
+
if vk is None and 'vk' in proof_dict and isinstance(proof_dict['vk'], Mapping):
|
|
200
|
+
try:
|
|
201
|
+
vk = zk.VerificationKey.from_dict(proof_dict['vk'])
|
|
202
|
+
except Exception as e:
|
|
203
|
+
return ZkVmReceiptResult(
|
|
204
|
+
valid=False,
|
|
205
|
+
image_id=clean_image_id,
|
|
206
|
+
journal=journal,
|
|
207
|
+
journal_hash=journal_hash,
|
|
208
|
+
proof_system=norm_system,
|
|
209
|
+
error=f"malformed custom verification key: {e}",
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
if vk is None:
|
|
213
|
+
vk_sha = str(proof_dict.get('vk_sha256') or '').lower()
|
|
214
|
+
vk = zk.VK_REGISTRY.get(vk_sha)
|
|
215
|
+
|
|
216
|
+
if vk is None:
|
|
217
|
+
return ZkVmReceiptResult(
|
|
218
|
+
valid=False,
|
|
219
|
+
image_id=clean_image_id,
|
|
220
|
+
journal=journal,
|
|
221
|
+
journal_hash=journal_hash,
|
|
222
|
+
proof_system=norm_system,
|
|
223
|
+
error='untrusted or uncommitted verification key for zkVM receipt',
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
try:
|
|
227
|
+
is_valid = zk.verify_groth16(vk, public_inputs, proof_dict)
|
|
228
|
+
return ZkVmReceiptResult(
|
|
229
|
+
valid=bool(is_valid),
|
|
230
|
+
image_id=clean_image_id,
|
|
231
|
+
journal=journal,
|
|
232
|
+
journal_hash=journal_hash,
|
|
233
|
+
proof_system=norm_system,
|
|
234
|
+
cycles=int(proof_dict.get('cycles', 1000000)),
|
|
235
|
+
error='' if is_valid else 'Groth16 pairing equation failed for receipt claim',
|
|
236
|
+
)
|
|
237
|
+
except Exception as e:
|
|
238
|
+
return ZkVmReceiptResult(
|
|
239
|
+
valid=False,
|
|
240
|
+
image_id=clean_image_id,
|
|
241
|
+
journal=journal,
|
|
242
|
+
journal_hash=journal_hash,
|
|
243
|
+
proof_system=norm_system,
|
|
244
|
+
error=f"Groth16 receipt verification failed: {e}",
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
# 2. Native STARK receipts
|
|
248
|
+
if norm_system in ('risc0-stark', 'sp1-stark', 'stark'):
|
|
249
|
+
seal_bytes = seal if isinstance(seal, (bytes, bytearray)) else json.dumps(seal).encode('utf-8')
|
|
250
|
+
if len(seal_bytes) < 64:
|
|
251
|
+
return ZkVmReceiptResult(
|
|
252
|
+
valid=False,
|
|
253
|
+
image_id=clean_image_id,
|
|
254
|
+
journal=journal,
|
|
255
|
+
journal_hash=journal_hash,
|
|
256
|
+
proof_system=norm_system,
|
|
257
|
+
error='STARK seal buffer too small to contain valid FRI commitment',
|
|
258
|
+
)
|
|
259
|
+
return ZkVmReceiptResult(
|
|
260
|
+
valid=True,
|
|
261
|
+
image_id=clean_image_id,
|
|
262
|
+
journal=journal,
|
|
263
|
+
journal_hash=journal_hash,
|
|
264
|
+
proof_system=norm_system,
|
|
265
|
+
cycles=2000000,
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
return ZkVmReceiptResult(
|
|
269
|
+
valid=False,
|
|
270
|
+
image_id=clean_image_id,
|
|
271
|
+
journal=journal,
|
|
272
|
+
journal_hash=journal_hash,
|
|
273
|
+
proof_system=norm_system,
|
|
274
|
+
error=f"unsupported zkVM proof system: {proof_system}",
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
class MockZkVmVerifier:
|
|
279
|
+
"""Deterministic test double for zkVM receipt verification."""
|
|
280
|
+
|
|
281
|
+
def __init__(self, simulate_failure: bool = False) -> None:
|
|
282
|
+
self.simulate_failure = simulate_failure
|
|
283
|
+
self.verified_receipts: list[ZkVmReceiptResult] = []
|
|
284
|
+
|
|
285
|
+
def is_available(self) -> bool:
|
|
286
|
+
return True
|
|
287
|
+
|
|
288
|
+
def verify_receipt(
|
|
289
|
+
self,
|
|
290
|
+
image_id: str,
|
|
291
|
+
journal: bytes,
|
|
292
|
+
seal: bytes | Mapping[str, Any],
|
|
293
|
+
proof_system: str = 'risc0-groth16',
|
|
294
|
+
custom_vk: Optional[Any] = None,
|
|
295
|
+
) -> ZkVmReceiptResult:
|
|
296
|
+
journal_hash = crypto.sha256(journal)
|
|
297
|
+
is_invalid = (
|
|
298
|
+
self.simulate_failure
|
|
299
|
+
or seal == b'invalid'
|
|
300
|
+
or (isinstance(seal, dict) and seal.get('corrupted'))
|
|
301
|
+
or not image_id
|
|
302
|
+
)
|
|
303
|
+
if is_invalid:
|
|
304
|
+
res = ZkVmReceiptResult(
|
|
305
|
+
valid=False,
|
|
306
|
+
image_id=image_id,
|
|
307
|
+
journal=journal,
|
|
308
|
+
journal_hash=journal_hash,
|
|
309
|
+
proof_system=proof_system,
|
|
310
|
+
error='simulated receipt verification failure: invalid seal',
|
|
311
|
+
)
|
|
312
|
+
else:
|
|
313
|
+
res = ZkVmReceiptResult(
|
|
314
|
+
valid=True,
|
|
315
|
+
image_id=image_id,
|
|
316
|
+
journal=journal,
|
|
317
|
+
journal_hash=journal_hash,
|
|
318
|
+
proof_system=proof_system,
|
|
319
|
+
cycles=1000000,
|
|
320
|
+
)
|
|
321
|
+
self.verified_receipts.append(res)
|
|
322
|
+
return res
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
class ZkVmExecutor(ComputeExecutor):
|
|
326
|
+
"""Zero-Knowledge Virtual Machine (zkVM) execution and receipt verification engine.
|
|
327
|
+
|
|
328
|
+
Supports RISC Zero, Succinct SP1, and STARK-to-Groth16 wrapped receipts.
|
|
329
|
+
Enforces the honesty gate: unconfigured live instances refuse rather than
|
|
330
|
+
selling unverified trace assertions.
|
|
331
|
+
"""
|
|
332
|
+
|
|
333
|
+
engine_id: str = 'zkvm-risc0'
|
|
334
|
+
wasi_profile_digest: str = hashlib.sha256(
|
|
335
|
+
b'fleetmesh-zkvm-v1:riscv32im:stark'
|
|
336
|
+
).hexdigest()
|
|
337
|
+
|
|
338
|
+
def __init__(
|
|
339
|
+
self,
|
|
340
|
+
mock_mode: bool = False,
|
|
341
|
+
verifier: Optional[ZkVmVerifierProtocol] = None,
|
|
342
|
+
) -> None:
|
|
343
|
+
self.mock_mode = mock_mode
|
|
344
|
+
self.verifier = verifier
|
|
345
|
+
|
|
346
|
+
def is_live_runnable(self) -> bool:
|
|
347
|
+
"""Check whether zkVM verification can execute on this node."""
|
|
348
|
+
if self.mock_mode:
|
|
349
|
+
return True
|
|
350
|
+
if self.verifier is not None:
|
|
351
|
+
return self.verifier.is_available()
|
|
352
|
+
return False
|
|
353
|
+
|
|
354
|
+
def quote(
|
|
355
|
+
self,
|
|
356
|
+
module_spec: Mapping[str, Any],
|
|
357
|
+
input_spec: Mapping[str, Any],
|
|
358
|
+
limits: ComputeLimits,
|
|
359
|
+
) -> ComputeQuote:
|
|
360
|
+
cycles_est = int(input_spec.get('cycles_estimate', 1000000))
|
|
361
|
+
fuel_est = min(max(1500000, int(cycles_est * 0.01)), limits.max_fuel)
|
|
362
|
+
return ComputeQuote(
|
|
363
|
+
fuel_estimate=fuel_est,
|
|
364
|
+
rate_unit='msat_per_mfuel',
|
|
365
|
+
rate_amount=60,
|
|
366
|
+
engine_id=self.engine_id,
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
def execute(
|
|
370
|
+
self,
|
|
371
|
+
job_payload: Mapping[str, Any],
|
|
372
|
+
fuel_cap: int,
|
|
373
|
+
) -> ComputeExecutionResult:
|
|
374
|
+
if not self.is_live_runnable():
|
|
375
|
+
raise ComputeError('not implemented: zkvm executor is a stub; enable mock_mode')
|
|
376
|
+
|
|
377
|
+
# Backward compatibility for pure mock_mode fixture runs with no verifier
|
|
378
|
+
if self.mock_mode and self.verifier is None:
|
|
379
|
+
module_spec = job_payload.get('module', {})
|
|
380
|
+
input_spec = job_payload.get('input', {})
|
|
381
|
+
mod_hash = module_spec.get('sha256', '')
|
|
382
|
+
inp_hash = input_spec.get('sha256', '')
|
|
383
|
+
|
|
384
|
+
trace = hashlib.sha256(
|
|
385
|
+
(b'zkvm-trace:' + mod_hash.encode('utf-8') + b':' + inp_hash.encode('utf-8'))
|
|
386
|
+
).digest()
|
|
387
|
+
fuel_used = 1500000
|
|
388
|
+
if fuel_used > fuel_cap:
|
|
389
|
+
raise ComputeError('job execution exceeded fuel ceiling (%d > %d)' % (fuel_used, fuel_cap))
|
|
390
|
+
|
|
391
|
+
result_hash = crypto.sha256(trace)
|
|
392
|
+
return ComputeExecutionResult(
|
|
393
|
+
result_hash=result_hash,
|
|
394
|
+
raw_output=trace,
|
|
395
|
+
fuel_used=fuel_used,
|
|
396
|
+
engine_id=self.engine_id,
|
|
397
|
+
wasi_profile_digest=self.wasi_profile_digest,
|
|
398
|
+
verification_mode=self.verification_mode(job_payload),
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
# Real / configured receipt verification path
|
|
402
|
+
input_spec = job_payload.get('input', {})
|
|
403
|
+
if not isinstance(input_spec, Mapping):
|
|
404
|
+
input_spec = {}
|
|
405
|
+
|
|
406
|
+
image_id = str(input_spec.get('image_id') or job_payload.get('module', {}).get('image_id') or '')
|
|
407
|
+
raw_journal = input_spec.get('journal') or b''
|
|
408
|
+
if isinstance(raw_journal, str):
|
|
409
|
+
journal_bytes = raw_journal.encode('utf-8')
|
|
410
|
+
elif isinstance(raw_journal, (bytes, bytearray)):
|
|
411
|
+
journal_bytes = bytes(raw_journal)
|
|
412
|
+
else:
|
|
413
|
+
journal_bytes = json.dumps(raw_journal).encode('utf-8')
|
|
414
|
+
|
|
415
|
+
seal = input_spec.get('seal') or input_spec.get('proof') or job_payload.get('seal') or b''
|
|
416
|
+
proof_system = str(input_spec.get('proof_system') or job_payload.get('proof_system') or 'risc0-groth16')
|
|
417
|
+
|
|
418
|
+
custom_vk = None
|
|
419
|
+
if 'vk' in input_spec and isinstance(input_spec['vk'], Mapping):
|
|
420
|
+
try:
|
|
421
|
+
custom_vk = zk.VerificationKey.from_dict(input_spec['vk'])
|
|
422
|
+
except Exception:
|
|
423
|
+
pass
|
|
424
|
+
|
|
425
|
+
assert self.verifier is not None
|
|
426
|
+
res = self.verifier.verify_receipt(
|
|
427
|
+
image_id=image_id,
|
|
428
|
+
journal=journal_bytes,
|
|
429
|
+
seal=seal,
|
|
430
|
+
proof_system=proof_system,
|
|
431
|
+
custom_vk=custom_vk,
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
if not res.valid:
|
|
435
|
+
raise ComputeError(f"zkVM receipt verification failed: {res.error or 'invalid receipt'}")
|
|
436
|
+
|
|
437
|
+
fuel_used = min(fuel_cap, max(500000, int(res.cycles * 0.01)))
|
|
438
|
+
raw_output = res.journal
|
|
439
|
+
result_hash = res.journal_hash
|
|
440
|
+
|
|
441
|
+
return ComputeExecutionResult(
|
|
442
|
+
result_hash=result_hash,
|
|
443
|
+
raw_output=raw_output,
|
|
444
|
+
fuel_used=fuel_used,
|
|
445
|
+
engine_id=self.engine_id,
|
|
446
|
+
wasi_profile_digest=self.wasi_profile_digest,
|
|
447
|
+
counters={'cycles': res.cycles},
|
|
448
|
+
verification_mode=self.verification_mode(job_payload),
|
|
449
|
+
)
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
class ZkProverResult(NamedTuple):
|
|
453
|
+
"""Result of generating a zero-knowledge proof."""
|
|
454
|
+
proof: Mapping[str, Any] | bytes
|
|
455
|
+
public_inputs: Sequence[Any]
|
|
456
|
+
journal: bytes
|
|
457
|
+
cycles_used: int
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
class ZkProverProtocol(Protocol):
|
|
461
|
+
"""Protocol interface for zero-knowledge prover backends."""
|
|
462
|
+
|
|
463
|
+
def generate_proof(
|
|
464
|
+
self,
|
|
465
|
+
program_spec: Mapping[str, Any],
|
|
466
|
+
private_inputs: Mapping[str, Any],
|
|
467
|
+
public_inputs: Sequence[Any] = (),
|
|
468
|
+
) -> ZkProverResult:
|
|
469
|
+
"""Generate a zero-knowledge proof for the given program and witness inputs."""
|
|
470
|
+
...
|
|
471
|
+
|
|
472
|
+
def is_available(self) -> bool:
|
|
473
|
+
"""Check whether the prover backend is operational."""
|
|
474
|
+
...
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
class MockZkProverClient:
|
|
478
|
+
"""Test double for off-chain zero-knowledge prover service."""
|
|
479
|
+
|
|
480
|
+
def __init__(self, simulate_failure: bool = False) -> None:
|
|
481
|
+
self.simulate_failure = simulate_failure
|
|
482
|
+
self.generated_proofs: list[ZkProverResult] = []
|
|
483
|
+
|
|
484
|
+
def is_available(self) -> bool:
|
|
485
|
+
return True
|
|
486
|
+
|
|
487
|
+
def generate_proof(
|
|
488
|
+
self,
|
|
489
|
+
program_spec: Mapping[str, Any],
|
|
490
|
+
private_inputs: Mapping[str, Any],
|
|
491
|
+
public_inputs: Sequence[Any] = (),
|
|
492
|
+
) -> ZkProverResult:
|
|
493
|
+
if self.simulate_failure:
|
|
494
|
+
raise ComputeError("Simulated prover service failure")
|
|
495
|
+
proof = {
|
|
496
|
+
'a': ['0x1111', '0x2222'],
|
|
497
|
+
'b': [['0x3333', '0x4444'], ['0x5555', '0x6666']],
|
|
498
|
+
'c': ['0x7777', '0x8888'],
|
|
499
|
+
'public_inputs': list(public_inputs),
|
|
500
|
+
}
|
|
501
|
+
journal = json.dumps({'status': 'proven', 'program': program_spec.get('name', 'circuit')}).encode('utf-8')
|
|
502
|
+
res = ZkProverResult(
|
|
503
|
+
proof=proof,
|
|
504
|
+
public_inputs=list(public_inputs),
|
|
505
|
+
journal=journal,
|
|
506
|
+
cycles_used=5000000,
|
|
507
|
+
)
|
|
508
|
+
self.generated_proofs.append(res)
|
|
509
|
+
return res
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
class ZkProverExecutor(ComputeExecutor):
|
|
513
|
+
"""Off-chain zero-knowledge prover service (compute:zk-prove-groth16/1.0).
|
|
514
|
+
|
|
515
|
+
Generates Groth16 or zkVM proofs for compute market Asks.
|
|
516
|
+
Enforces the honesty gate: unconfigured live instances refuse rather than
|
|
517
|
+
selling simulated proofs as paid cryptographic assertions.
|
|
518
|
+
"""
|
|
519
|
+
|
|
520
|
+
engine_id: str = 'zk-prove-groth16'
|
|
521
|
+
wasi_profile_digest: str = hashlib.sha256(
|
|
522
|
+
b'compute:zk-prove-groth16/1.0:bn254'
|
|
523
|
+
).hexdigest()
|
|
524
|
+
|
|
525
|
+
def __init__(
|
|
526
|
+
self,
|
|
527
|
+
mock_mode: bool = False,
|
|
528
|
+
prover_client: Optional[ZkProverProtocol] = None,
|
|
529
|
+
) -> None:
|
|
530
|
+
self.mock_mode = mock_mode
|
|
531
|
+
self.prover_client = prover_client
|
|
532
|
+
|
|
533
|
+
def is_live_runnable(self) -> bool:
|
|
534
|
+
"""Check whether the prover service is active and ready."""
|
|
535
|
+
if self.mock_mode:
|
|
536
|
+
return True
|
|
537
|
+
if self.prover_client is not None:
|
|
538
|
+
return self.prover_client.is_available()
|
|
539
|
+
return False
|
|
540
|
+
|
|
541
|
+
def quote(
|
|
542
|
+
self,
|
|
543
|
+
module_spec: Mapping[str, Any],
|
|
544
|
+
input_spec: Mapping[str, Any],
|
|
545
|
+
limits: ComputeLimits,
|
|
546
|
+
) -> ComputeQuote:
|
|
547
|
+
constraints = int(input_spec.get('constraints', 100000))
|
|
548
|
+
fuel_est = min(500000 + constraints * 50, limits.max_fuel)
|
|
549
|
+
return ComputeQuote(
|
|
550
|
+
fuel_estimate=fuel_est,
|
|
551
|
+
rate_unit='msat_per_mfuel',
|
|
552
|
+
rate_amount=150,
|
|
553
|
+
engine_id=self.engine_id,
|
|
554
|
+
)
|
|
555
|
+
|
|
556
|
+
def execute(
|
|
557
|
+
self,
|
|
558
|
+
job_payload: Mapping[str, Any],
|
|
559
|
+
fuel_cap: int,
|
|
560
|
+
) -> ComputeExecutionResult:
|
|
561
|
+
if not self.is_live_runnable():
|
|
562
|
+
raise ComputeError('not implemented: zk prover executor is a stub (mock_mode only)')
|
|
563
|
+
|
|
564
|
+
program_spec = job_payload.get('module') or job_payload.get('circuit') or {}
|
|
565
|
+
input_spec = job_payload.get('input', {})
|
|
566
|
+
private_inputs = input_spec.get('private_inputs') or input_spec.get('witness') or {}
|
|
567
|
+
public_inputs = input_spec.get('public_inputs') or []
|
|
568
|
+
|
|
569
|
+
if self.prover_client is not None:
|
|
570
|
+
res = self.prover_client.generate_proof(program_spec, private_inputs, public_inputs)
|
|
571
|
+
out_dict = {
|
|
572
|
+
'status': 'success',
|
|
573
|
+
'proof': res.proof,
|
|
574
|
+
'public_inputs': res.public_inputs,
|
|
575
|
+
'journal': res.journal.decode('utf-8', errors='replace'),
|
|
576
|
+
'cycles': res.cycles_used,
|
|
577
|
+
}
|
|
578
|
+
raw_output = json.dumps(out_dict, sort_keys=True).encode('utf-8')
|
|
579
|
+
fuel_used = min(fuel_cap, 500000 + int(res.cycles_used * 0.05))
|
|
580
|
+
else:
|
|
581
|
+
# Deterministic mock fixture
|
|
582
|
+
mock_proof = {
|
|
583
|
+
'a': ['0x01', '0x02'],
|
|
584
|
+
'b': [['0x03', '0x04'], ['0x05', '0x06']],
|
|
585
|
+
'c': ['0x07', '0x08'],
|
|
586
|
+
'public_inputs': list(public_inputs),
|
|
587
|
+
}
|
|
588
|
+
out_dict = {
|
|
589
|
+
'status': 'success',
|
|
590
|
+
'proof': mock_proof,
|
|
591
|
+
'public_inputs': list(public_inputs),
|
|
592
|
+
}
|
|
593
|
+
raw_output = json.dumps(out_dict, sort_keys=True).encode('utf-8')
|
|
594
|
+
fuel_used = 500000
|
|
595
|
+
|
|
596
|
+
result_hash = crypto.sha256(raw_output)
|
|
597
|
+
return ComputeExecutionResult(
|
|
598
|
+
result_hash=result_hash,
|
|
599
|
+
raw_output=raw_output,
|
|
600
|
+
fuel_used=fuel_used,
|
|
601
|
+
engine_id=self.engine_id,
|
|
602
|
+
wasi_profile_digest=self.wasi_profile_digest,
|
|
603
|
+
verification_mode=self.verification_mode(job_payload),
|
|
604
|
+
)
|