market-settlement: the dispute and settlement rail of a verifiable infrastructure market (AereComputeMarketV3 on testnet 28001, five scenarios measured end to end, a pure judge over recorded timelines, AIP-23 receipts); README index

This commit is contained in:
Aere Network 2026-10-02 20:05:37 +03:00
parent cf8c56792f
commit 52634a1300
10 changed files with 2810 additions and 1 deletions

View File

@ -16,6 +16,7 @@ notarization command of the verification layer, and the agents' x402 wallet (EIP
| [`control-plane/`](control-plane/) | from findings to a finished migration: a prioritized plan from the inventory and the scanner, its execution through the gateway, KMS and PKI with consent per action and a measured proof afterwards, recipes for the servers the products do not touch and a rescan that judges them, a compliance report against NIST IR 8547, the EU roadmap and optionally CNSA 2.0, and a console that checks it all again | | [`control-plane/`](control-plane/) | from findings to a finished migration: a prioritized plan from the inventory and the scanner, its execution through the gateway, KMS and PKI with consent per action and a measured proof afterwards, recipes for the servers the products do not touch and a rescan that judges them, a compliance report against NIST IR 8547, the EU roadmap and optionally CNSA 2.0, and a console that checks it all again |
| [`agents/`](agents/) | limits an AI agent cannot break unseen: a post-quantum identity (ML-DSA-65), a policy (spending per time window, allowed tools and recipients, which actions need human approval), a signed ledger of every action judged against the policy, approvals and revocation signed by people, and a verifier that re-runs the policy over the whole ledger without trusting the agent; two branches of one ledger are a proof of equivocation anyone can check; and a wallet that pays over x402 only what the agent's ledger records and its policy allows, either holding the payment key for the owner or co-signing from a 2-of-2 contract wallet that neither the agent nor the owner can spend alone; both run on the public testnet with their evidence; and the chain as the witness of a ledger: a notarized head, read through the AIP-23 verifier under a post-quantum certified anchor, bounds entry times from below (a backdated entry is caught) | | [`agents/`](agents/) | limits an AI agent cannot break unseen: a post-quantum identity (ML-DSA-65), a policy (spending per time window, allowed tools and recipients, which actions need human approval), a signed ledger of every action judged against the policy, approvals and revocation signed by people, and a verifier that re-runs the policy over the whole ledger without trusting the agent; two branches of one ledger are a proof of equivocation anyone can check; and a wallet that pays over x402 only what the agent's ledger records and its policy allows, either holding the payment key for the owner or co-signing from a 2-of-2 contract wallet that neither the agent nor the owner can spend alone; both run on the public testnet with their evidence; and the chain as the witness of a ledger: a notarized head, read through the AIP-23 verifier under a post-quantum certified anchor, bounds entry times from below (a backdated entry is caught) |
| [`proof-of-ai/`](proof-of-ai/) | the AIP-23 envelope of an AI agent's action: model, version, weights digest, prompt, input, output, tools, agent, requester, as digests; verified by the same AIP-23 verifier as every other proof | | [`proof-of-ai/`](proof-of-ai/) | the AIP-23 envelope of an AI agent's action: model, version, weights digest, prompt, input, output, tools, agent, requester, as digests; verified by the same AIP-23 verifier as every other proof |
| [`market-settlement/`](market-settlement/) | the dispute and settlement rail of a verifiable infrastructure market, measured end to end on testnet 28001: an order with its reward in escrow and a provider bond, the result committed on chain with its execution envelope notarized under a post-quantum certified anchor, a challenge window in which anyone disputes with a recomputed result and a bond, an arbiter that can only route the escrow (with a permissionless 14-day timeout), deadline reclaims, and an AIP-23 settlement receipt; a pure judge over the job's timeline that checks deadlines, the window, who resolved, the payout, the replay and the proof |
| [`verifiable-data/`](verifiable-data/) | a dataset whose every answer is checkable against a short commitment without the data: RFC 9162 commitments per version (append-only, consistency proofs), equality and range queries proven correct AND complete (contiguous run of a sorted index plus its two neighbours), hash-based selective disclosure of fields, availability sampling against the serving endpoint, and the version's AIP-23 envelope notarized and read back under a post-quantum certified anchor; not zero knowledge, and says so | | [`verifiable-data/`](verifiable-data/) | a dataset whose every answer is checkable against a short commitment without the data: RFC 9162 commitments per version (append-only, consistency proofs), equality and range queries proven correct AND complete (contiguous run of a sorted index plus its two neighbours), hash-based selective disclosure of fields, availability sampling against the serving endpoint, and the version's AIP-23 envelope notarized and read back under a post-quantum certified anchor; not zero knowledge, and says so |
| [`ai-trust/`](ai-trust/) | the trust chain of an AI result: the agent's identity (agents/), the model artifact's attestation, the host's runtime record (verify-layer/), the policy and signed ledger, the Proof of AI envelope and the chain as witness, joined in one bundle whose verifier checks only the links between them | | [`ai-trust/`](ai-trust/) | the trust chain of an AI result: the agent's identity (agents/), the model artifact's attestation, the host's runtime record (verify-layer/), the policy and signed ledger, the Proof of AI envelope and the chain as witness, joined in one bundle whose verifier checks only the links between them |
| [`identity/`](identity/) | post-quantum credentials: an issuer signs claims (hybrid Ed25519 + ML-DSA-65, both required), the holder shows only the claims it picks (selective disclosure in the manner of SD-JWT, RFC 9901, in a format of its own), bound to the verifier's audience and nonce; delegation to a phone, an agent or a ten-minute session key that can only narrow; revocation and an issuer status list in the manner of W3C Bitstring Status List; all checked offline from the files; and compliance without surveillance: a verifier's policy (issuers, required claims) judged on a presentation, recorded as an AIP-23 envelope that carries no personal data; and the Travel Rule between VASPs, the IVMS101 data sealed for the receiving VASP with a hybrid X25519 + ML-KEM-768 key encapsulation, signed, bound to the transfer and acknowledged; and the same credentials as standard SD-JWT (IETF RFC 9901): issued with the issuer's Ed25519 key (EdDSA) or its ML-DSA-65 key, presented with a key binding JWT, and verified against the RFC's own example vectors (`identity-cli.mjs verify-sdjwt`, behind Aere Cloud's POST /v1/identity/sd-jwt/verify) | | [`identity/`](identity/) | post-quantum credentials: an issuer signs claims (hybrid Ed25519 + ML-DSA-65, both required), the holder shows only the claims it picks (selective disclosure in the manner of SD-JWT, RFC 9901, in a format of its own), bound to the verifier's audience and nonce; delegation to a phone, an agent or a ten-minute session key that can only narrow; revocation and an issuer status list in the manner of W3C Bitstring Status List; all checked offline from the files; and compliance without surveillance: a verifier's policy (issuers, required claims) judged on a presentation, recorded as an AIP-23 envelope that carries no personal data; and the Travel Rule between VASPs, the IVMS101 data sealed for the receiving VASP with a hybrid X25519 + ML-KEM-768 key encapsulation, signed, bound to the transfer and acknowledged; and the same credentials as standard SD-JWT (IETF RFC 9901): issued with the issuer's Ed25519 key (EdDSA) or its ML-DSA-65 key, presented with a key binding JWT, and verified against the RFC's own example vectors (`identity-cli.mjs verify-sdjwt`, behind Aere Cloud's POST /v1/identity/sd-jwt/verify) |
@ -41,6 +42,7 @@ test, counts as a failure of the control. Results measured on 2026-09-29 (Node.j
| identity | 44/44 (`node proba-identity.mjs`, with the AIP-23 verifier for its envelope test; without it that test is reported as skipped and the exit code is 2), compliance 15/15 (`node proba-conformitate.mjs`), Travel Rule 19/19 (`node proba-travel-rule.mjs`), SD-JWT 23/23 (`node proba-sdjwt.mjs`, the RFC 9901 vectors included), measured 2026-09-30 | 49/49 (`node control-negativ-identity.mjs`); compliance 14/14 (`node control-negativ-conformitate.mjs`); Travel Rule 19/19 (`node control-negativ-travel-rule.mjs`); SD-JWT 28/28 (`node control-negativ-sdjwt.mjs`) | | identity | 44/44 (`node proba-identity.mjs`, with the AIP-23 verifier for its envelope test; without it that test is reported as skipped and the exit code is 2), compliance 15/15 (`node proba-conformitate.mjs`), Travel Rule 19/19 (`node proba-travel-rule.mjs`), SD-JWT 23/23 (`node proba-sdjwt.mjs`, the RFC 9901 vectors included), measured 2026-09-30 | 49/49 (`node control-negativ-identity.mjs`); compliance 14/14 (`node control-negativ-conformitate.mjs`); Travel Rule 19/19 (`node control-negativ-travel-rule.mjs`); SD-JWT 28/28 (`node control-negativ-sdjwt.mjs`) |
| agents | policy 27/27 with the AIP-23 verifier (without it 25 run, 2 are reported as skipped and the exit code is 2), ledger 51/51, approval and revocation 39/39, the chain as witness 26/26 without a network and 7/7 on testnet 28001 on 2026-09-30 (`proba-agent-ancora-testnet.mjs`, needs a funded testnet key and the AIP-23 verifier), command line 23/23 through files and processes only (on Linux and macOS one more test checks the key file mode; not measured here); x402 wallet 25/25, 2-of-2 co-signing 16/16 and payment verifier 14/14 without a network; on the public testnet 28001, 9/9 with the wallet key and 13/13 with the 2-of-2 contract wallet (`x402/proba-x402-testnet.mjs`, `x402/proba-cosign-testnet.mjs`, each needs a funded testnet key); the contract's artifact recompiles byte for byte with solc 0.8.23 (`node x402/recompileaza-contract.mjs --solc <solc>`) | 26/26 (`node control-negativ-aprobare.mjs`); the chain as witness 11/11 (`node control-negativ-ancora.mjs`); x402 30/30 (`node x402/control-negativ-wallet.mjs`); the contract's own tests (7) and their negative control (4/4) run in the Aere Network contracts project, not in this repository | | agents | policy 27/27 with the AIP-23 verifier (without it 25 run, 2 are reported as skipped and the exit code is 2), ledger 51/51, approval and revocation 39/39, the chain as witness 26/26 without a network and 7/7 on testnet 28001 on 2026-09-30 (`proba-agent-ancora-testnet.mjs`, needs a funded testnet key and the AIP-23 verifier), command line 23/23 through files and processes only (on Linux and macOS one more test checks the key file mode; not measured here); x402 wallet 25/25, 2-of-2 co-signing 16/16 and payment verifier 14/14 without a network; on the public testnet 28001, 9/9 with the wallet key and 13/13 with the 2-of-2 contract wallet (`x402/proba-x402-testnet.mjs`, `x402/proba-cosign-testnet.mjs`, each needs a funded testnet key); the contract's artifact recompiles byte for byte with solc 0.8.23 (`node x402/recompileaza-contract.mjs --solc <solc>`) | 26/26 (`node control-negativ-aprobare.mjs`); the chain as witness 11/11 (`node control-negativ-ancora.mjs`); x402 30/30 (`node x402/control-negativ-wallet.mjs`); the contract's own tests (7) and their negative control (4/4) run in the Aere Network contracts project, not in this repository |
| proof-of-ai | 6/6 with the AIP-23 verifier (`AERE_VERIFY_PROOF=... node proba-proof-of-ai.mjs`); without it the probe reports NEMASURAT and exits 2 | one negative control inside the test (an envelope touched after its hash: INVALID) | | proof-of-ai | 6/6 with the AIP-23 verifier (`AERE_VERIFY_PROOF=... node proba-proof-of-ai.mjs`); without it the probe reports NEMASURAT and exits 2 | one negative control inside the test (an envelope touched after its hash: INVALID) |
| market-settlement | 20/20 on testnet 28001 on 2026-10-02 (`AERE_TESTNET_KEY_FILE=... AERE_ETHERS_DIR=... node proba-piata-28001.mjs`: five scenarios on AereComputeMarketV3 deployed from the committed artifact, every refusal by its named custom error, the provider's notarized envelope read back under a post-quantum certified anchor); the judge on the recorded timelines 16/16 without a chain (`node proba-judecata.mjs`: five honest timelines VALID, ten forged ones INVALID for their named reason, the settlement receipt) | 7/7 (`node proba-judecata.mjs --control-negativ`: deadline, window, arbiter, payout, result bytes, replay, proof, each planted out on a copy and each forged fixture then accepted) |
| verifiable-data | 22/22 with the AIP-23 verifier (`AERE_VERIFY_PROOF=... node proba-verifiable-data.mjs`: honest answers VALID, nine forged ones INVALID for their named reason, availability sampling on a local server; the envelope row is NEMASURAT without the verifier, exit 2); 24/24 with `--viu` on testnet 28001 on 2026-10-02 (a version notarized with a test key and read back under a post-quantum certified anchor; evidence in `dovezi/`) | 5/5 (`node proba-verifiable-data.mjs --control-negativ`: field commitment, index order, completeness, consistency, sampling, each planted out on a copy and each forged fixture then accepted) | | verifiable-data | 22/22 with the AIP-23 verifier (`AERE_VERIFY_PROOF=... node proba-verifiable-data.mjs`: honest answers VALID, nine forged ones INVALID for their named reason, availability sampling on a local server; the envelope row is NEMASURAT without the verifier, exit 2); 24/24 with `--viu` on testnet 28001 on 2026-10-02 (a version notarized with a test key and read back under a post-quantum certified anchor; evidence in `dovezi/`) | 5/5 (`node proba-verifiable-data.mjs --control-negativ`: field commitment, index order, completeness, consistency, sampling, each planted out on a copy and each forged fixture then accepted) |
| ai-trust | 13/13 with the AIP-23 verifier (`AERE_VERIFY_PROOF=... node proba-lant-de-incredere.mjs`: the whole chain on a committed fixture, then ten links broken on purpose, each refused for its named reason); without the verifier it exits 2 | 8/8 (`node proba-lant-de-incredere.mjs --control-negativ`) | | ai-trust | 13/13 with the AIP-23 verifier (`AERE_VERIFY_PROOF=... node proba-lant-de-incredere.mjs`: the whole chain on a committed fixture, then ten links broken on purpose, each refused for its named reason); without the verifier it exits 2 | 8/8 (`node proba-lant-de-incredere.mjs --control-negativ`) |
@ -51,4 +53,4 @@ interface, command line and data (`definePolicy`, `verifyLedger`, `approve`, ...
## Licence ## Licence
MIT, see [LICENSE](LICENSE). Files: 174 (pq-gateway 6, pq-kms 10, pq-pki 6, crypto-inventory 42, verify-layer 12, proof-kinds 3, control-plane 19, agents 39, identity 15, proof-of-ai 3, ai-trust 6, verifiable-data 7, readiness 4). MIT, see [LICENSE](LICENSE). Files: 183 (pq-gateway 6, pq-kms 10, pq-pki 6, crypto-inventory 42, verify-layer 12, proof-kinds 3, control-plane 19, agents 39, identity 15, proof-of-ai 3, ai-trust 6, verifiable-data 7, market-settlement 9, readiness 4).

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,50 @@
# AERE Market Settlement
The dispute and settlement rail of a verifiable infrastructure market, measured end to end on testnet 28001: an order with its reward in
escrow, a provider bond, the provider's result committed on chain and its proof notarized under a post-quantum certified anchor, a
challenge window in which anyone can dispute with a recomputed result and a matching bond, an arbiter who can only route the escrow
between the three parties (and a permissionless timeout if the arbiter never answers), a deadline after which an undelivered order is
reclaimed, and an AIP-23 settlement receipt of the payout. Built from what already exists: `AereComputeMarketV3` (the contract, here
as a committed artifact), `proof-kinds/` (the execution envelope and the settlement receipt), `agents/agent-ancora.mjs` (the one
notarization call of the house) and the AIP-23 reference verifier.
| step | on chain | off chain, checkable by anyone |
|---|---|---|
| order | `postJob`: reward escrowed, `specHash` of the public workload, deadline, challenge window, required provider bond | the spec itself (`sarcina.mjs`: a keccak chain anyone recomputes) |
| claim | `claimJob` with the bond, only before the deadline | |
| proof | `submitResult`: `keccak256(result)` recorded, the bytes emitted; the window starts | the provider's `aere-proof-of-execution` envelope (program, `inputHash` = specHash, `outputHash` = resultHash) notarized on AereNotary: its `firstSeen` is the chain's time, read back under a post-quantum certified anchor by `verify-proof.mjs` |
| dispute | `dispute(recomputedHash)` with a bond equal to the provider's, only inside the window | the judge replays the spec and says which side the arbiter should take |
| settlement | `finalize` by anyone after the window (Paid); `resolveDispute` by the ARBITER (Paid or Slashed); `resolveByTimeout` by anyone after 14 days (Refunded, bonds returned); `reclaimExpired` by the requester after the deadline (Refunded, bond slashed); `cancelJob` on an open order | `piata.mjs judeca`: the timeline against the rules; `chitanta`: the AIP-23 `settlement` envelope of the payout tx |
`piata.mjs` has the three pieces a reader needs: `codulAsteptat` rebuilds the runtime code a market must have from the committed
artifact and the four constructor arguments (so `eth_getCode` is compared before any event is believed), `cronologie` reads a job's
state and every event with its block time and sender, and `judeca` is a pure function over that timeline: claim and submission before
the deadline, dispute strictly inside the window, finalize only after it (unless the requester accepts early), resolution only by the
arbiter or after `ARBITRATION_TIMEOUT`, the terminal payout matching the state, the result bytes hashing to the commitment, the replay of
the public spec agreeing with whoever won, and the notarized envelope naming this job's spec and result before the window closed.
```
AERE_TESTNET_KEY_FILE=<key> AERE_ETHERS_DIR=<dir with node_modules/ethers> node proba-piata-28001.mjs # the flow on 28001 (five scenarios)
node proba-judecata.mjs # the judge on the recorded timelines: 5 honest VALID, 10 forged INVALID, the receipt
node proba-judecata.mjs --control-negativ # 7 plantings on a copy of piata.mjs; each forged fixture must then be accepted
node extrage-artefact.mjs # regenerate AereComputeMarketV3.json from the contracts build (refuses a changed source)
```
## What it is not, and what is not measured
- **Not a marketplace.** No discovery, no pricing, no reputation, no real GPU, storage or inference behind the workload; the task is a
keccak chain chosen because anyone can replay it. The rail settles; it does not find providers.
- **The arbiter is one key.** A dispute is decided by `ARBITER`, who can only route the escrow between requester, provider and
challenger, never elsewhere, and whose silence is unwound by anyone after 14 days. That is a trust assumption, named, not a proof;
the judge checks the arbiter's decision against the replay afterwards, which makes a wrong ruling visible, not impossible.
- **REPLAY mode only.** `ZK_VERIFIED` (pay only on an SP1 proof) cannot be exercised on this deployment: 28001 has no SP1 gateway, so the
market was built with a contract that has code but no `verifyProof` (the notary), and a ZK job reverts, fail-closed. `pqcSettlement`
(a Falcon-512 payout authorization) is not exercised either. The 14-day arbitration timeout and the 7-day PQC grace are not waited for:
the probe shows they refuse early (`ArbitrationTimeoutNotPassed`), not that they unwind.
- **Native tAERE only.** An allowlisted ERC-20 reward and the 2-of-2 agent wallet as requester are not measured: the contract wallet has
no function that calls `postJob`, so an agent that orders through this rail does it from its EOA, under its ledger and policy.
- **Chain 2800: nothing.** `AereComputeMarketV3` is held back from mainnet until an external audit of fund-flow contracts; everything here
ran on testnet 28001 with test keys (the provider's and the arbiter's derived from the requester's test key, never printed).
- The replay uses a dependency-free keccak-256 (BigInt): about 7 s for the 20,000 iterations of the laboratory task on a laptop, against well under a second with a native or WebAssembly keccak; the cost is the price of a component with nothing to install, and it is measured, not hidden.
- The judge trusts the RPC for the timeline (events and block times), like every reader of a chain through an endpoint; the notarized
envelope's time is checked under a post-quantum certified anchor by the reference verifier, the rest is not.

View File

@ -0,0 +1,14 @@
{
"chainId": 28001,
"market": "0x4145d6f639436d840957F2D4B9f2e73f34F56B5D",
"deployTx": "0x8f45502e40a71d45550756fdcb3e4ed3af0532134212704f161fd2c4004d8ae2",
"block": 4445446,
"args": {
"ZK_VERIFIER": "0x70099E62735500AA2F85B60C518551a57B202d54",
"PINNED_PROOF_SELECTOR": "0x4388a21c",
"ARBITER": "0x5521aC7371Afdbf6BE986B46Da4116fb256d9e58",
"GOVERNANCE": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF"
},
"artifactSourceSha256": "0x6db4d53dffb0afd79cfb41f973ac4d2ca82589ab7bdd206296b66b4c72dd4fe4",
"at": "2026-10-02T16:40:39.149Z"
}

View File

@ -0,0 +1,815 @@
{
"la": "2026-10-02T16:46:56.361Z",
"chainId": 28001,
"rpc": "https://testnet-rpc.aere.network",
"fereastra": 90,
"scenarii": {
"S1": {
"spec": {
"task": "keccak-chain",
"seed": "0xbfcf8dae6e6f105b4e7ead898a4fdb0319745deb3d38042e3815d75805e1060d",
"iters": 20000
},
"pasi": [],
"jobId": 5,
"computeMs": 597,
"envelope": {
"v": 1,
"kind": "aere-proof-of-execution-attestation",
"statement": {
"v": 1,
"kind": "aere-proof-of-execution",
"inputHash": "0xf2a0d3e03521234fd6be921e346817e6f77e20db038ff5325318a2a5a47cfd25",
"outputHash": "0xeba5b6ff363f15d358926ab16bc4c7c8c979ec2d217868134810488304dde0be",
"programSha256": "0x1824b9e133c368fab195cb2f33e35bee78abdb4de7b03f0c377ac5af951eb9e0",
"program": "keccak-chain",
"exitCode": 0,
"createdAt": "2026-10-02T16:47:11.789Z"
},
"statementHash": "0xfffa43d50b6222a17f915d528f0f636b16882f736f357b8418d30dfec2f88200",
"notarization": {
"chainId": 28001,
"notary": "0x70099E62735500AA2F85B60C518551a57B202d54",
"firstSeen": 1790959633,
"txHash": "0x86f094ae0ba1030b97060792344eda1b6018367dbf14088c6c2de1e5a4e2fc6e",
"block": 4446125
}
},
"date": {
"chainId": 28001,
"market": "0x4145d6f639436d840957F2D4B9f2e73f34F56B5D",
"jobId": "5",
"constants": {
"arbiter": "0x5521aC7371Afdbf6BE986B46Da4116fb256d9e58",
"governance": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"arbitrationTimeout": 1209600,
"zkVerifier": "0x70099E62735500AA2F85B60C518551a57B202d54",
"pinnedProofSelector": "0x4388a21c"
},
"job": {
"requester": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"provider": "0xeC2261CECBB06768F6db095822b429AF5c861E2e",
"token": "0x0000000000000000000000000000000000000000",
"reward": "0",
"providerBond": "0",
"specHash": "0xf2a0d3e03521234fd6be921e346817e6f77e20db038ff5325318a2a5a47cfd25",
"resultHash": "0xeba5b6ff363f15d358926ab16bc4c7c8c979ec2d217868134810488304dde0be",
"zkProgramVKey": "0x0000000000000000000000000000000000000000000000000000000000000000",
"mode": 0,
"status": 5,
"pqcSettlement": false,
"offchainResult": false,
"deadline": 1790960218,
"challengeWindow": 90,
"submittedAt": 1790959639,
"disputedAt": 0
},
"events": [
{
"name": "JobPosted",
"args": {
"id": "5",
"requester": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"token": "0x0000000000000000000000000000000000000000",
"reward": "10000000000000000",
"mode": "0",
"specHash": "0xf2a0d3e03521234fd6be921e346817e6f77e20db038ff5325318a2a5a47cfd25",
"deadline": "1790960218",
"pqcSettlement": false
},
"blockNumber": 4446103,
"blockHash": "0xfc22d0155293bef0b5349507a2a45495de7d3baa37b1f9e5e482585d93ecdc3e",
"timestamp": 1790959620,
"txHash": "0x5178c7b523d9ad5516f5e649f50bc98c44aacd6c4564403ac6b7528a9e63791e",
"from": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"logIndex": 0
},
{
"name": "JobClaimed",
"args": {
"id": "5",
"provider": "0xeC2261CECBB06768F6db095822b429AF5c861E2e",
"providerBond": "4000000000000000"
},
"blockNumber": 4446113,
"blockHash": "0xd59b44a60e225a952c0ef940a62f6ebe7325dc4d75627161c329aa6f48bc9ce4",
"timestamp": 1790959626,
"txHash": "0xf46a947200113d014c3e8b0282267feb2882f0d8895ae4b13a72f1db20fa7ee3",
"from": "0xeC2261CECBB06768F6db095822b429AF5c861E2e",
"logIndex": 0
},
{
"name": "ResultSubmitted",
"args": {
"id": "5",
"resultHash": "0xeba5b6ff363f15d358926ab16bc4c7c8c979ec2d217868134810488304dde0be",
"result": "0x7b22646967657374223a22307864346139306165303264396364623232353863373239633532636166613662613263623164306539323532326135313739313733383362366535386131323535222c226974657273223a32303030302c2273656564223a22307862666366386461653665366631303562346537656164383938613466646230333139373435646562336433383034326533383135643735383035653130363064222c227461736b223a226b656363616b2d636861696e227d"
},
"blockNumber": 4446136,
"blockHash": "0x7cedaf1f5da474b94c41fd0925f181459ff642fa449843886d94226daaf2650f",
"timestamp": 1790959639,
"txHash": "0xea128ff1992e39f6690ebfbfbd9896ff27bcea0db4b0070c32062d2de599d446",
"from": "0xeC2261CECBB06768F6db095822b429AF5c861E2e",
"logIndex": 0
},
{
"name": "JobPaid",
"args": {
"id": "5",
"provider": "0xeC2261CECBB06768F6db095822b429AF5c861E2e",
"token": "0x0000000000000000000000000000000000000000",
"reward": "10000000000000000",
"bondReturned": "4000000000000000"
},
"blockNumber": 4446295,
"blockHash": "0x736c818d1833f0c88ccda5ec5101d392fd842af32a6b152018a085c7a75a7c5c",
"timestamp": 1790959733,
"txHash": "0x5e4bfedda879e4e016b7f4434f75da4a8af63ccc4cce285d714b9e56e453e65f",
"from": "0x5521aC7371Afdbf6BE986B46Da4116fb256d9e58",
"logIndex": 0
}
],
"result": "0x7b22646967657374223a22307864346139306165303264396364623232353863373239633532636166613662613263623164306539323532326135313739313733383362366535386131323535222c226974657273223a32303030302c2273656564223a22307862666366386461653665366631303562346537656164383938613466646230333139373435646562336433383034326533383135643735383035653130363064222c227461736b223a226b656363616b2d636861696e227d",
"spec": {
"task": "keccak-chain",
"seed": "0xbfcf8dae6e6f105b4e7ead898a4fdb0319745deb3d38042e3815d75805e1060d",
"iters": 20000
},
"envelope": {
"v": 1,
"kind": "aere-proof-of-execution-attestation",
"statement": {
"v": 1,
"kind": "aere-proof-of-execution",
"inputHash": "0xf2a0d3e03521234fd6be921e346817e6f77e20db038ff5325318a2a5a47cfd25",
"outputHash": "0xeba5b6ff363f15d358926ab16bc4c7c8c979ec2d217868134810488304dde0be",
"programSha256": "0x1824b9e133c368fab195cb2f33e35bee78abdb4de7b03f0c377ac5af951eb9e0",
"program": "keccak-chain",
"exitCode": 0,
"createdAt": "2026-10-02T16:47:11.789Z"
},
"statementHash": "0xfffa43d50b6222a17f915d528f0f636b16882f736f357b8418d30dfec2f88200",
"notarization": {
"chainId": 28001,
"notary": "0x70099E62735500AA2F85B60C518551a57B202d54",
"firstSeen": 1790959633,
"txHash": "0x86f094ae0ba1030b97060792344eda1b6018367dbf14088c6c2de1e5a4e2fc6e",
"block": 4446125
}
}
},
"judecata": {
"verdict": "VALID",
"motive": [],
"randuri": [
"2026-10-02T16:47:00.000Z #4446103 JobPosted by 0x86D6C684.. id=5 requester=0x86D6C684.. token=0x00000000.. reward=10000000000000000 mode=0 specHash=0xf2a0d3e0.. deadline=1790960218 pqcSettlement=false",
"2026-10-02T16:47:06.000Z #4446113 JobClaimed by 0xeC2261CE.. id=5 provider=0xeC2261CE.. providerBond=4000000000000000",
"2026-10-02T16:47:19.000Z #4446136 ResultSubmitted by 0xeC2261CE.. id=5 resultHash=0xeba5b6ff..",
"2026-10-02T16:48:53.000Z #4446295 JobPaid by 0x5521aC73.. id=5 provider=0xeC2261CE.. token=0x00000000.. reward=10000000000000000 bondReturned=4000000000000000"
],
"status": "Paid",
"terminal": {
"status": "Paid",
"payee": "0xeC2261CECBB06768F6db095822b429AF5c861E2e",
"amount": "10000000000000000",
"txHash": "0x5e4bfedda879e4e016b7f4434f75da4a8af63ccc4cce285d714b9e56e453e65f",
"blockHash": "0x736c818d1833f0c88ccda5ec5101d392fd842af32a6b152018a085c7a75a7c5c"
},
"replay": {
"recomputedHash": "0xeba5b6ff363f15d358926ab16bc4c7c8c979ec2d217868134810488304dde0be",
"providerRight": true
}
},
"verifyProof": {
"verdict": "VALID",
"statementHash": "0xfffa43d50b6222a17f915d528f0f636b16882f736f357b8418d30dfec2f88200",
"levels": [
{
"level": "form",
"state": "PASSED",
"detail": "kind=aere-proof-of-execution-attestation"
},
{
"level": "integrity",
"state": "PASSED",
"detail": "sha256(statement) == statementHash (0xfffa43d50b6222a1..)"
},
{
"level": "signature",
"state": "ABSENT",
"detail": "the envelope carries no signature"
},
{
"level": "finality",
"state": "PASSED",
"detail": "post-quantum: the state of block 4446303, the parent of anchor 4446304 whose post-quantum certificate verified here records this statementHash in AereNotary 0x70099e62735500aa2f85b60c518551a57b202d54 as first seen at 2026-10-02T16:47:13.000Z (1790959633); first seen in block 4446125, transaction 0x86f094ae0ba1030b.. (located through the endpoint's logs, not part of what is certified)"
}
]
},
"chitanta": {
"v": 1,
"kind": "aere-proof-of-settlement-attestation",
"statement": {
"v": 1,
"kind": "aere-proof-of-settlement",
"instructionHash": "0xacad5a190c81f60a3051a56f2a7fad505abbbe86da6ffd130fc7f2f8964eceb9",
"txHash": "0x5e4bfedda879e4e016b7f4434f75da4a8af63ccc4cce285d714b9e56e453e65f",
"blockHash": "0x736c818d1833f0c88ccda5ec5101d392fd842af32a6b152018a085c7a75a7c5c",
"chainId": 28001,
"from": "0x4145d6f639436d840957F2D4B9f2e73f34F56B5D",
"to": "0xeC2261CECBB06768F6db095822b429AF5c861E2e",
"amount": "10000000000000000",
"asset": "native",
"createdAt": "2026-10-02T16:49:13.443Z"
},
"statementHash": "0xab6c8e8876816ff3fbaef7b3fc364ad3b4e94b98116ede3fdb982bff3b886786"
},
"chitantaVerdict": {
"verdict": "VALID",
"statementHash": "0xab6c8e8876816ff3fbaef7b3fc364ad3b4e94b98116ede3fdb982bff3b886786",
"levels": [
{
"level": "form",
"state": "PASSED",
"detail": "kind=aere-proof-of-settlement-attestation"
},
{
"level": "integrity",
"state": "PASSED",
"detail": "sha256(statement) == statementHash (0xab6c8e8876816ff3..)"
},
{
"level": "signature",
"state": "ABSENT",
"detail": "the envelope carries no signature"
},
{
"level": "finality",
"state": "ABSENT",
"detail": "no notarization claimed and no --rpc given"
}
]
}
},
"S2": {
"spec": {
"task": "keccak-chain",
"seed": "0x5de2558627fcce6d953b1a70358f167c9b364fc944a45fe0741abc0c0b8d42ce",
"iters": 20000
},
"pasi": [],
"jobId": 6,
"date": {
"chainId": 28001,
"market": "0x4145d6f639436d840957F2D4B9f2e73f34F56B5D",
"jobId": "6",
"constants": {
"arbiter": "0x5521aC7371Afdbf6BE986B46Da4116fb256d9e58",
"governance": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"arbitrationTimeout": 1209600,
"zkVerifier": "0x70099E62735500AA2F85B60C518551a57B202d54",
"pinnedProofSelector": "0x4388a21c"
},
"job": {
"requester": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"provider": "0xeC2261CECBB06768F6db095822b429AF5c861E2e",
"token": "0x0000000000000000000000000000000000000000",
"reward": "0",
"providerBond": "0",
"specHash": "0x307e7d75c2de86416fd1df625fd5c00a12a4ff6257bea57871de8c7d47cf5a89",
"resultHash": "0x2dea74737284c05166223abbb28b3eaf367e8964a5a12bffa0ab4bd1e2b300ae",
"zkProgramVKey": "0x0000000000000000000000000000000000000000000000000000000000000000",
"mode": 0,
"status": 7,
"pqcSettlement": false,
"offchainResult": false,
"deadline": 1790960353,
"challengeWindow": 90,
"submittedAt": 1790959767,
"disputedAt": 1790959773
},
"events": [
{
"name": "JobPosted",
"args": {
"id": "6",
"requester": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"token": "0x0000000000000000000000000000000000000000",
"reward": "10000000000000000",
"mode": "0",
"specHash": "0x307e7d75c2de86416fd1df625fd5c00a12a4ff6257bea57871de8c7d47cf5a89",
"deadline": "1790960353",
"pqcSettlement": false
},
"blockNumber": 4446332,
"blockHash": "0x373ef4cd4095e1db711ba4748c2a5bd2d78a26dd7892a98c14929aeb0b62cf1f",
"timestamp": 1790959755,
"txHash": "0xdf242547d54507d84bb9ac8d91ce24b65529d17940bd95504f5267ee74bdd92a",
"from": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"logIndex": 0
},
{
"name": "JobClaimed",
"args": {
"id": "6",
"provider": "0xeC2261CECBB06768F6db095822b429AF5c861E2e",
"providerBond": "4000000000000000"
},
"blockNumber": 4446342,
"blockHash": "0xa81e65065cd3c472cc571c64035031c66e975906a92e5b88acba4a6e08c9d7a1",
"timestamp": 1790959761,
"txHash": "0xfcfc913a260d29b519276a484199d372ed39aac81005166e5231025bf948d35b",
"from": "0xeC2261CECBB06768F6db095822b429AF5c861E2e",
"logIndex": 0
},
{
"name": "ResultSubmitted",
"args": {
"id": "6",
"resultHash": "0x2dea74737284c05166223abbb28b3eaf367e8964a5a12bffa0ab4bd1e2b300ae",
"result": "0x7b22646967657374223a22307865656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565222c226974657273223a32303030302c2273656564223a22307835646532353538363237666363653664393533623161373033353866313637633962333634666339343461343566653037343161626330633062386434326365222c227461736b223a226b656363616b2d636861696e227d"
},
"blockNumber": 4446353,
"blockHash": "0xd4d23bd3f6d3883330ff25de0a81696c0e9ba473c5457b918c5aa82e55601777",
"timestamp": 1790959767,
"txHash": "0x5f921a8d67de79bfcfd4d77085fa220d5ecc4191631a7e4dc050e003e612c6c6",
"from": "0xeC2261CECBB06768F6db095822b429AF5c861E2e",
"logIndex": 0
},
{
"name": "JobDisputed",
"args": {
"id": "6",
"challenger": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"bond": "4000000000000000",
"recomputedResultHash": "0xb37eb93d1b69a562f94bb2de482a0a4dd3e04493d3020cb29eeef8df82c713c3"
},
"blockNumber": 4446364,
"blockHash": "0xf6a3e7e6f51312f624cbed5251b0c5abe05f9bd2b1d255ec8fe65114d408311c",
"timestamp": 1790959773,
"txHash": "0x72c54a067ebf1341a588db66f61223a28d7f6977bc60ab8eff5e59e008ddf8de",
"from": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"logIndex": 0
},
{
"name": "JobSlashed",
"args": {
"id": "6",
"challenger": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"slashedProviderBond": "4000000000000000"
},
"blockNumber": 4446375,
"blockHash": "0xf5aa67bf1e48b8fc8a8ff44b794b0f9cd501f45d10b7979b7b5fdc37e9d70a9a",
"timestamp": 1790959780,
"txHash": "0x7b1916485981fef836d5f43c3d8c4528678931f78d4b3110859198c630e5e69f",
"from": "0x5521aC7371Afdbf6BE986B46Da4116fb256d9e58",
"logIndex": 0
},
{
"name": "DisputeResolved",
"args": {
"id": "6",
"providerWon": false
},
"blockNumber": 4446375,
"blockHash": "0xf5aa67bf1e48b8fc8a8ff44b794b0f9cd501f45d10b7979b7b5fdc37e9d70a9a",
"timestamp": 1790959780,
"txHash": "0x7b1916485981fef836d5f43c3d8c4528678931f78d4b3110859198c630e5e69f",
"from": "0x5521aC7371Afdbf6BE986B46Da4116fb256d9e58",
"logIndex": 1
}
],
"result": "0x7b22646967657374223a22307865656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565222c226974657273223a32303030302c2273656564223a22307835646532353538363237666363653664393533623161373033353866313637633962333634666339343461343566653037343161626330633062386434326365222c227461736b223a226b656363616b2d636861696e227d",
"spec": {
"task": "keccak-chain",
"seed": "0x5de2558627fcce6d953b1a70358f167c9b364fc944a45fe0741abc0c0b8d42ce",
"iters": 20000
},
"envelope": null
},
"judecata": {
"verdict": "VALID",
"motive": [],
"randuri": [
"2026-10-02T16:49:15.000Z #4446332 JobPosted by 0x86D6C684.. id=6 requester=0x86D6C684.. token=0x00000000.. reward=10000000000000000 mode=0 specHash=0x307e7d75.. deadline=1790960353 pqcSettlement=false",
"2026-10-02T16:49:21.000Z #4446342 JobClaimed by 0xeC2261CE.. id=6 provider=0xeC2261CE.. providerBond=4000000000000000",
"2026-10-02T16:49:27.000Z #4446353 ResultSubmitted by 0xeC2261CE.. id=6 resultHash=0x2dea7473..",
"2026-10-02T16:49:33.000Z #4446364 JobDisputed by 0x86D6C684.. id=6 challenger=0x86D6C684.. bond=4000000000000000 recomputedResultHash=0xb37eb93d..",
"2026-10-02T16:49:40.000Z #4446375 JobSlashed by 0x5521aC73.. id=6 challenger=0x86D6C684.. slashedProviderBond=4000000000000000",
"2026-10-02T16:49:40.000Z #4446375 DisputeResolved by 0x5521aC73.. id=6 providerWon=false"
],
"status": "Slashed",
"terminal": {
"status": "Slashed",
"payee": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"amount": "4000000000000000",
"refundedTo": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"txHash": "0x7b1916485981fef836d5f43c3d8c4528678931f78d4b3110859198c630e5e69f",
"blockHash": "0xf5aa67bf1e48b8fc8a8ff44b794b0f9cd501f45d10b7979b7b5fdc37e9d70a9a"
},
"replay": {
"recomputedHash": "0xb37eb93d1b69a562f94bb2de482a0a4dd3e04493d3020cb29eeef8df82c713c3",
"providerRight": false,
"challengerRecomputedRight": true
}
},
"gasResolve": "70471"
},
"S3": {
"spec": {
"task": "keccak-chain",
"seed": "0x53c2cdb3e3e6a740c9fd8800cca9d50ec84681b76f5547c2536090db7624b09d",
"iters": 20000
},
"pasi": [],
"jobId": 7,
"date": {
"chainId": 28001,
"market": "0x4145d6f639436d840957F2D4B9f2e73f34F56B5D",
"jobId": "7",
"constants": {
"arbiter": "0x5521aC7371Afdbf6BE986B46Da4116fb256d9e58",
"governance": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"arbitrationTimeout": 1209600,
"zkVerifier": "0x70099E62735500AA2F85B60C518551a57B202d54",
"pinnedProofSelector": "0x4388a21c"
},
"job": {
"requester": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"provider": "0xeC2261CECBB06768F6db095822b429AF5c861E2e",
"token": "0x0000000000000000000000000000000000000000",
"reward": "0",
"providerBond": "0",
"specHash": "0x33d4d459ac50fbf9089cfbbdf51416f7f9d8fd8794f03f4788bef4b679be0e06",
"resultHash": "0x20595189a5aae95406d466972f780d5b04408146e30afdcc66449836a67b240a",
"zkProgramVKey": "0x0000000000000000000000000000000000000000000000000000000000000000",
"mode": 0,
"status": 5,
"pqcSettlement": false,
"offchainResult": false,
"deadline": 1790960390,
"challengeWindow": 90,
"submittedAt": 1790959804,
"disputedAt": 1790959809
},
"events": [
{
"name": "JobPosted",
"args": {
"id": "7",
"requester": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"token": "0x0000000000000000000000000000000000000000",
"reward": "10000000000000000",
"mode": "0",
"specHash": "0x33d4d459ac50fbf9089cfbbdf51416f7f9d8fd8794f03f4788bef4b679be0e06",
"deadline": "1790960390",
"pqcSettlement": false
},
"blockNumber": 4446395,
"blockHash": "0xdaec8217b35341244eb65c7a06bc6d817c44888c0e2c673d2278091e7c308a83",
"timestamp": 1790959791,
"txHash": "0x8c0ab87432b8e3e4b8a53f9ef3fd9d217e214f106ff637368ab75f0ed68c300d",
"from": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"logIndex": 0
},
{
"name": "JobClaimed",
"args": {
"id": "7",
"provider": "0xeC2261CECBB06768F6db095822b429AF5c861E2e",
"providerBond": "4000000000000000"
},
"blockNumber": 4446404,
"blockHash": "0x31141aeb86c3bc3f7f3e0b493aa69a2a4d2e1952c09181f2ed3bc8e32b062c97",
"timestamp": 1790959797,
"txHash": "0x78e05e10e9b9aca97b0e26ce9e868dd017b57fcce4248bf65701b06ed44d1d1a",
"from": "0xeC2261CECBB06768F6db095822b429AF5c861E2e",
"logIndex": 0
},
{
"name": "ResultSubmitted",
"args": {
"id": "7",
"resultHash": "0x20595189a5aae95406d466972f780d5b04408146e30afdcc66449836a67b240a",
"result": "0x7b22646967657374223a22307838613832333664613464396231373164613863643538643832373738376535656539323739316533643262353665313537383163313938336363326464396531222c226974657273223a32303030302c2273656564223a22307835336332636462336533653661373430633966643838303063636139643530656338343638316237366635353437633235333630393064623736323462303964222c227461736b223a226b656363616b2d636861696e227d"
},
"blockNumber": 4446415,
"blockHash": "0x27d0fdbd6383be65aa8549cae7f87436255111ccb57597f92f0eb78a8c568d20",
"timestamp": 1790959804,
"txHash": "0xf4b8f42fa5cad3827bbe234348b687dfec24d0b3e55f5ae162d66891239353e8",
"from": "0xeC2261CECBB06768F6db095822b429AF5c861E2e",
"logIndex": 0
},
{
"name": "JobDisputed",
"args": {
"id": "7",
"challenger": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"bond": "4000000000000000",
"recomputedResultHash": "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
},
"blockNumber": 4446425,
"blockHash": "0x42ff0ad6b3187d212824c078f5cbb09cae1f67e5d762b1500053d34188da73e4",
"timestamp": 1790959809,
"txHash": "0x8595234a75d21301720a71505d4aff1046b8a73171da9bf24d245b5512911cd2",
"from": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"logIndex": 0
},
{
"name": "JobPaid",
"args": {
"id": "7",
"provider": "0xeC2261CECBB06768F6db095822b429AF5c861E2e",
"token": "0x0000000000000000000000000000000000000000",
"reward": "10000000000000000",
"bondReturned": "4000000000000000"
},
"blockNumber": 4446436,
"blockHash": "0x0e01a68f00de85f42a89054e785755699653ca065746982b153565be3051c2fd",
"timestamp": 1790959815,
"txHash": "0x7eb96d3a9703668a275d233daeb829435e6eb08fdb30f9179f0ed806b2926e8e",
"from": "0x5521aC7371Afdbf6BE986B46Da4116fb256d9e58",
"logIndex": 0
},
{
"name": "DisputeResolved",
"args": {
"id": "7",
"providerWon": true
},
"blockNumber": 4446436,
"blockHash": "0x0e01a68f00de85f42a89054e785755699653ca065746982b153565be3051c2fd",
"timestamp": 1790959815,
"txHash": "0x7eb96d3a9703668a275d233daeb829435e6eb08fdb30f9179f0ed806b2926e8e",
"from": "0x5521aC7371Afdbf6BE986B46Da4116fb256d9e58",
"logIndex": 1
}
],
"result": "0x7b22646967657374223a22307838613832333664613464396231373164613863643538643832373738376535656539323739316533643262353665313537383163313938336363326464396531222c226974657273223a32303030302c2273656564223a22307835336332636462336533653661373430633966643838303063636139643530656338343638316237366635353437633235333630393064623736323462303964222c227461736b223a226b656363616b2d636861696e227d",
"spec": {
"task": "keccak-chain",
"seed": "0x53c2cdb3e3e6a740c9fd8800cca9d50ec84681b76f5547c2536090db7624b09d",
"iters": 20000
},
"envelope": null
},
"judecata": {
"verdict": "VALID",
"motive": [],
"randuri": [
"2026-10-02T16:49:51.000Z #4446395 JobPosted by 0x86D6C684.. id=7 requester=0x86D6C684.. token=0x00000000.. reward=10000000000000000 mode=0 specHash=0x33d4d459.. deadline=1790960390 pqcSettlement=false",
"2026-10-02T16:49:57.000Z #4446404 JobClaimed by 0xeC2261CE.. id=7 provider=0xeC2261CE.. providerBond=4000000000000000",
"2026-10-02T16:50:04.000Z #4446415 ResultSubmitted by 0xeC2261CE.. id=7 resultHash=0x20595189..",
"2026-10-02T16:50:09.000Z #4446425 JobDisputed by 0x86D6C684.. id=7 challenger=0x86D6C684.. bond=4000000000000000 recomputedResultHash=0xdddddddd..",
"2026-10-02T16:50:15.000Z #4446436 JobPaid by 0x5521aC73.. id=7 provider=0xeC2261CE.. token=0x00000000.. reward=10000000000000000 bondReturned=4000000000000000",
"2026-10-02T16:50:15.000Z #4446436 DisputeResolved by 0x5521aC73.. id=7 providerWon=true"
],
"status": "Paid",
"terminal": {
"status": "Paid",
"payee": "0xeC2261CECBB06768F6db095822b429AF5c861E2e",
"amount": "10000000000000000",
"txHash": "0x7eb96d3a9703668a275d233daeb829435e6eb08fdb30f9179f0ed806b2926e8e",
"blockHash": "0x0e01a68f00de85f42a89054e785755699653ca065746982b153565be3051c2fd"
},
"replay": {
"recomputedHash": "0x20595189a5aae95406d466972f780d5b04408146e30afdcc66449836a67b240a",
"providerRight": true,
"challengerRecomputedRight": false
}
}
},
"S4": {
"spec": {
"task": "keccak-chain",
"seed": "0xd2356607ad7de44861e4098fc8de3628ab454527539afeb302e6cbc03450d500",
"iters": 20000
},
"pasi": [],
"jobId": 8,
"date": {
"chainId": 28001,
"market": "0x4145d6f639436d840957F2D4B9f2e73f34F56B5D",
"jobId": "8",
"constants": {
"arbiter": "0x5521aC7371Afdbf6BE986B46Da4116fb256d9e58",
"governance": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"arbitrationTimeout": 1209600,
"zkVerifier": "0x70099E62735500AA2F85B60C518551a57B202d54",
"pinnedProofSelector": "0x4388a21c"
},
"job": {
"requester": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"provider": "0xeC2261CECBB06768F6db095822b429AF5c861E2e",
"token": "0x0000000000000000000000000000000000000000",
"reward": "0",
"providerBond": "0",
"specHash": "0xe39172c8a9a6c56862bc48d25c2b20f56c585d165d8140b9cd18da9784f1f5cd",
"resultHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"zkProgramVKey": "0x0000000000000000000000000000000000000000000000000000000000000000",
"mode": 0,
"status": 6,
"pqcSettlement": false,
"offchainResult": false,
"deadline": 1790959871,
"challengeWindow": 90,
"submittedAt": 0,
"disputedAt": 0
},
"events": [
{
"name": "JobPosted",
"args": {
"id": "8",
"requester": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"token": "0x0000000000000000000000000000000000000000",
"reward": "10000000000000000",
"mode": "0",
"specHash": "0xe39172c8a9a6c56862bc48d25c2b20f56c585d165d8140b9cd18da9784f1f5cd",
"deadline": "1790959871",
"pqcSettlement": false
},
"blockNumber": 4446456,
"blockHash": "0x4f912d1702c83e1c6fdd864b75471c8f30a69f37d8decdbf121415285c94492f",
"timestamp": 1790959827,
"txHash": "0x2129c617c59cf4fce4f0bb03cfd4d1d97ff2ea256be637f553dd63093dc2cee9",
"from": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"logIndex": 0
},
{
"name": "JobClaimed",
"args": {
"id": "8",
"provider": "0xeC2261CECBB06768F6db095822b429AF5c861E2e",
"providerBond": "4000000000000000"
},
"blockNumber": 4446459,
"blockHash": "0x5e3fb425890cf5d8e65b0992db0b872cb06b62df2d3dbe404b1e73b805891df6",
"timestamp": 1790959829,
"txHash": "0x351e7248fc153fd6f46f1d5fd2480ec6428b1ce351b35232b9099d5e1cd6aec3",
"from": "0xeC2261CECBB06768F6db095822b429AF5c861E2e",
"logIndex": 0
},
{
"name": "JobRefunded",
"args": {
"id": "8",
"requester": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"token": "0x0000000000000000000000000000000000000000",
"reward": "10000000000000000"
},
"blockNumber": 4446540,
"blockHash": "0x269e3df7ecdcc85db01e702814eb766d235233eee90c0182179ddf7307c32cab",
"timestamp": 1790959877,
"txHash": "0xf041b9338843a0727acca19c794d940ec7d13df17dd17d88cc1c48da740c0a03",
"from": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"logIndex": 0
}
],
"result": null,
"spec": {
"task": "keccak-chain",
"seed": "0xd2356607ad7de44861e4098fc8de3628ab454527539afeb302e6cbc03450d500",
"iters": 20000
},
"envelope": null
},
"judecata": {
"verdict": "VALID",
"motive": [],
"randuri": [
"2026-10-02T16:50:27.000Z #4446456 JobPosted by 0x86D6C684.. id=8 requester=0x86D6C684.. token=0x00000000.. reward=10000000000000000 mode=0 specHash=0xe39172c8.. deadline=1790959871 pqcSettlement=false",
"2026-10-02T16:50:29.000Z #4446459 JobClaimed by 0xeC2261CE.. id=8 provider=0xeC2261CE.. providerBond=4000000000000000",
"2026-10-02T16:51:17.000Z #4446540 JobRefunded by 0x86D6C684.. id=8 requester=0x86D6C684.. token=0x00000000.. reward=10000000000000000"
],
"status": "Refunded",
"terminal": {
"status": "Refunded",
"payee": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"amount": "10000000000000000",
"txHash": "0xf041b9338843a0727acca19c794d940ec7d13df17dd17d88cc1c48da740c0a03",
"blockHash": "0x269e3df7ecdcc85db01e702814eb766d235233eee90c0182179ddf7307c32cab"
},
"replay": null
}
},
"S5": {
"spec": {
"task": "keccak-chain",
"seed": "0x7fbe35a5bee832d3c35f100c82ebcc863a4d17ee1be045b5dfaba46aa4b93322",
"iters": 20000
},
"pasi": [],
"jobId": 9,
"date": {
"chainId": 28001,
"market": "0x4145d6f639436d840957F2D4B9f2e73f34F56B5D",
"jobId": "9",
"constants": {
"arbiter": "0x5521aC7371Afdbf6BE986B46Da4116fb256d9e58",
"governance": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"arbitrationTimeout": 1209600,
"zkVerifier": "0x70099E62735500AA2F85B60C518551a57B202d54",
"pinnedProofSelector": "0x4388a21c"
},
"job": {
"requester": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"provider": "0x0000000000000000000000000000000000000000",
"token": "0x0000000000000000000000000000000000000000",
"reward": "0",
"providerBond": "0",
"specHash": "0xac9d0a395f1ae6e8ca7adebdd44e4454000c349e80feba2c16dc3e0749c9a9b7",
"resultHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"zkProgramVKey": "0x0000000000000000000000000000000000000000000000000000000000000000",
"mode": 0,
"status": 6,
"pqcSettlement": false,
"offchainResult": false,
"deadline": 1790959916,
"challengeWindow": 90,
"submittedAt": 0,
"disputedAt": 0
},
"events": [
{
"name": "JobPosted",
"args": {
"id": "9",
"requester": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"token": "0x0000000000000000000000000000000000000000",
"reward": "10000000000000000",
"mode": "0",
"specHash": "0xac9d0a395f1ae6e8ca7adebdd44e4454000c349e80feba2c16dc3e0749c9a9b7",
"deadline": "1790959916",
"pqcSettlement": false
},
"blockNumber": 4446558,
"blockHash": "0xb3512a658a8969ec6e6516d19c9bd9d2c99134cc3d654ba5afac6da85ffe363f",
"timestamp": 1790959888,
"txHash": "0x5aba1b174fd5948b8ff555f0b4d4584607145914eec3ed1bdde00072d31ee1af",
"from": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"logIndex": 0
},
{
"name": "JobRefunded",
"args": {
"id": "9",
"requester": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"token": "0x0000000000000000000000000000000000000000",
"reward": "10000000000000000"
},
"blockNumber": 4446614,
"blockHash": "0x6cb797489059f864e1e57a687cde487c0f8765945688cd673879c36e8be7e0e3",
"timestamp": 1790959920,
"txHash": "0xe13b6cdbb7bb49c2d74235eb3c014f793a9d62df079235a19adbe9c66020de21",
"from": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"logIndex": 0
}
],
"result": null,
"spec": {
"task": "keccak-chain",
"seed": "0x7fbe35a5bee832d3c35f100c82ebcc863a4d17ee1be045b5dfaba46aa4b93322",
"iters": 20000
},
"envelope": null
},
"judecata": {
"verdict": "VALID",
"motive": [],
"randuri": [
"2026-10-02T16:51:28.000Z #4446558 JobPosted by 0x86D6C684.. id=9 requester=0x86D6C684.. token=0x00000000.. reward=10000000000000000 mode=0 specHash=0xac9d0a39.. deadline=1790959916 pqcSettlement=false",
"2026-10-02T16:52:00.000Z #4446614 JobRefunded by 0x86D6C684.. id=9 requester=0x86D6C684.. token=0x00000000.. reward=10000000000000000"
],
"status": "Refunded",
"terminal": {
"status": "Refunded",
"payee": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF",
"amount": "10000000000000000",
"txHash": "0xe13b6cdbb7bb49c2d74235eb3c014f793a9d62df079235a19adbe9c66020de21",
"blockHash": "0x6cb797489059f864e1e57a687cde487c0f8765945688cd673879c36e8be7e0e3"
},
"replay": null
}
}
},
"desfasurare": {
"chainId": 28001,
"market": "0x4145d6f639436d840957F2D4B9f2e73f34F56B5D",
"deployTx": "0x8f45502e40a71d45550756fdcb3e4ed3af0532134212704f161fd2c4004d8ae2",
"block": 4445446,
"args": {
"ZK_VERIFIER": "0x70099E62735500AA2F85B60C518551a57B202d54",
"PINNED_PROOF_SELECTOR": "0x4388a21c",
"ARBITER": "0x5521aC7371Afdbf6BE986B46Da4116fb256d9e58",
"GOVERNANCE": "0x86D6C68408DDb2CC0d1cf4dFd6e07d3830600fAF"
},
"artifactSourceSha256": "0x6db4d53dffb0afd79cfb41f973ac4d2ca82589ab7bdd206296b66b4c72dd4fe4",
"at": "2026-10-02T16:40:39.149Z",
"codeOk": true
},
"rezumat": {
"verzi": 20,
"rosii": 0
}
}

View File

@ -0,0 +1,32 @@
#!/usr/bin/env node
// extrage-artefact.mjs: the compiled AereComputeMarketV3 as ONE file beside this component, taken from the hardhat build-info of the
// contracts project (roadmap point 28, 2026-10-02): abi, creation and runtime bytecode, the immutable references WITH their names (so a
// reader can rebuild the exact runtime code of a deployment from its constructor arguments and compare it with eth_getCode), the
// compiler and its settings, and the SHA-256 of the Solidity source the build-info holds (checked equal to the source in the tree).
// Deterministic: the same build-info gives the same file. Refuses a build-info whose source differs from the tree.
// node extrage-artefact.mjs writes AereComputeMarketV3.json
import fs from 'node:fs';
import path from 'node:path';
import crypto from 'node:crypto';
import { fileURLToPath } from 'node:url';
const AICI = path.dirname(fileURLToPath(import.meta.url));
const RAD = path.resolve(AICI, '..', '..');
const SOL = 'contracts/depin/AereComputeMarketV3.sol';
const dbg = JSON.parse(fs.readFileSync(path.join(RAD, 'contracts', 'artifacts', SOL, 'AereComputeMarketV3.dbg.json'), 'utf8'));
const bi = JSON.parse(fs.readFileSync(path.resolve(path.join(RAD, 'contracts', 'artifacts', SOL), dbg.buildInfo.replace(/\\/g, '/')), 'utf8'));
const o = bi.output.contracts[SOL].AereComputeMarketV3;
const src = bi.input.sources[SOL].content.replace(/\r\n/g, '\n');
const tree = fs.readFileSync(path.join(RAD, 'contracts', SOL), 'utf8').replace(/\r\n/g, '\n');
if (src !== tree) { console.log('REFUZ: the source in the build-info differs from contracts/' + SOL + ' (recompile first)'); process.exit(1); }
const nume = {}; (function walk(x) { if (x && typeof x === 'object') { if (x.nodeType === 'VariableDeclaration' && x.mutability === 'immutable') nume[x.id] = { name: x.name, type: x.typeDescriptions.typeString }; for (const k in x) walk(x[k]); } })(bi.output.sources[SOL].ast);
const refs = o.evm.deployedBytecode.immutableReferences || {};
const immutables = Object.fromEntries(Object.entries(refs).map(([id, pos]) => [nume[id].name, { type: nume[id].type, positions: pos }]));
const art = {
kind: 'aere-compute-market-v3-artifact', contract: 'AereComputeMarketV3', source: SOL, sourceSha256: '0x' + crypto.createHash('sha256').update(src).digest('hex'),
solc: bi.solcLongVersion, optimizer: bi.input.settings.optimizer, evmVersion: bi.input.settings.evmVersion || 'default',
abi: o.abi, bytecode: '0x' + o.evm.bytecode.object, deployedBytecode: '0x' + o.evm.deployedBytecode.object, immutables,
note: 'runtime code of a deployment = deployedBytecode with each immutable written at its positions as a 32-byte word (addresses right-aligned, bytes4 left-aligned); compare with eth_getCode',
};
fs.writeFileSync(path.join(AICI, 'AereComputeMarketV3.json'), JSON.stringify(art, null, 1) + '\n');
console.log(`AereComputeMarketV3.json: ${art.solc}, runtime ${(art.deployedBytecode.length - 2) / 2} bytes, immutables ${Object.keys(immutables).join(', ')}, source sha256 ${art.sourceSha256.slice(0, 18)}..`);

142
market-settlement/piata.mjs Normal file
View File

@ -0,0 +1,142 @@
// piata.mjs: the dispute and settlement rail of the verifiable infrastructure market (roadmap master point 28, 2026-10-02), on what is
// live: AereComputeMarketV3 (escrow, provider bond, challenge window, dispute with a challenger bond, arbiter with a permissionless
// timeout, deadline reclaim), the AIP-23 execution envelope of the provider's result notarized on AereNotary (the chain's time for the
// delivery, under a post-quantum certified anchor), and the AIP-23 settlement receipt of the terminal payout. This file has three parts:
// 1. the deployment: the runtime code a market MUST have, rebuilt from the committed artifact and the constructor arguments
// (`codulAsteptat`), so a reader checks eth_getCode before trusting any event the market emitted;
// 2. the timeline: every event of a job with its block time and the sender of its transaction (`cronologie`), a JSON a reader can keep;
// 3. the JUDGE (`judeca`): pure rules over the timeline, no chain: deadlines, the challenge window, who resolved, that the terminal
// payout matches the state, that the result bytes hash to the on-chain commitment, that a REPLAY of the public spec agrees with
// whoever the arbiter sided with, and that the provider's notarized envelope names this result and existed before the window
// closed. Every rule carries a PAZA marker for the planting control. `chitanta` turns the terminal payout into an AIP-23
// `settlement` envelope, verifiable with the reference verifier.
// ethers is injected (createRequire of the caller's ethers); the rest is node:crypto and the siblings beside this component.
import fs from 'node:fs';
import path from 'node:path';
import crypto from 'node:crypto';
import { fileURLToPath } from 'node:url';
import { buildProof } from '../proof-kinds/proof-kinds.mjs';
import { ruleaza, rezultatHash, rezultatOcteti, specHash as specHashOf, keccak, canonical } from './sarcina.mjs';
const AICI = path.dirname(fileURLToPath(import.meta.url));
export const ARTEFACT = path.join(AICI, 'AereComputeMarketV3.json');
export const STATUS = ['None', 'Open', 'Claimed', 'Submitted', 'Disputed', 'Paid', 'Refunded', 'Slashed'];
export const MODE = ['REPLAY', 'ZK_VERIFIED', 'OPTIMISTIC'];
const H32 = /^0x[0-9a-f]{64}$/i;
const sha256 = (b) => '0x' + crypto.createHash('sha256').update(b).digest('hex');
export function incarcaArtefact(f = ARTEFACT) {
const a = JSON.parse(fs.readFileSync(f, 'utf8'));
if (a.kind !== 'aere-compute-market-v3-artifact' || !a.deployedBytecode || !a.immutables || !a.abi) throw new Error('not a market artifact: ' + f);
return a;
}
/** The runtime code of a deployment: the artifact's deployedBytecode with every immutable written at its positions. */
export function codulAsteptat(a, args) {
const cod = Buffer.from(a.deployedBytecode.slice(2), 'hex');
for (const [nume, im] of Object.entries(a.immutables)) {
const v = args[nume]; if (v === undefined) throw new Error('missing immutable ' + nume);
const w = Buffer.alloc(32);
if (/^address|^contract/.test(im.type)) Buffer.from(v.replace(/^0x/, '').padStart(40, '0'), 'hex').copy(w, 12);
else if (/^bytes(\d+)$/.test(im.type)) Buffer.from(v.replace(/^0x/, ''), 'hex').copy(w, 0);
else throw new Error('immutable type not handled: ' + im.type);
for (const p of im.positions) { if (p.length !== 32) throw new Error('immutable slot not 32 bytes'); w.copy(cod, p.start); }
}
return '0x' + cod.toString('hex');
}
/** eth_getCode of the market equals the artifact with these constructor arguments; returns { ok, motiv }. */
export async function verificaDesfasurarea({ provider, market, artifact, args }) {
const viu = (await provider.getCode(market)).toLowerCase();
const asteptat = codulAsteptat(artifact, args).toLowerCase();
if (viu === '0x') return { ok: false, motiv: 'no code at ' + market };
if (viu !== asteptat) return { ok: false, motiv: `the code at ${market} is not AereComputeMarketV3 with these immutables (${viu.length / 2 - 1} vs ${asteptat.length / 2 - 1} bytes, first difference at byte ${[...viu].findIndex((c, i) => c !== asteptat[i]) >> 1})` };
return { ok: true, motiv: 'runtime code equals the artifact (' + (viu.length / 2 - 1) + ' bytes) with ZK_VERIFIER, PINNED_PROOF_SELECTOR, ARBITER, GOVERNANCE as given' };
}
export const contract = (ethers, provider, market, artifact, signer = null) => new ethers.Contract(market, artifact.abi, signer || provider);
const s = (v) => (typeof v === 'bigint' ? v.toString() : v);
export function jobDinLant(j) {
return { requester: j.requester, provider: j.provider, token: j.token, reward: s(j.reward), providerBond: s(j.providerBond), specHash: j.specHash, resultHash: j.resultHash, zkProgramVKey: j.zkProgramVKey,
mode: Number(j.mode), status: Number(j.status), pqcSettlement: j.pqcSettlement, offchainResult: j.offchainResult, deadline: Number(j.deadline), challengeWindow: Number(j.challengeWindow), submittedAt: Number(j.submittedAt), disputedAt: Number(j.disputedAt) };
}
/** The timeline of a job: its state, its constants, and every event (with block time and tx sender), oldest first. */
export async function cronologie({ ethers, provider, market, artifact, jobId, fromBlock = 0, spec = null, envelope = null }) {
const c = contract(ethers, provider, market, artifact);
const [net, job, arbiter, governance, timeout, zk, pin] = await Promise.all([provider.getNetwork(), c.getJob(jobId), c.ARBITER(), c.GOVERNANCE(), c.ARBITRATION_TIMEOUT(), c.ZK_VERIFIER(), c.PINNED_PROOF_SELECTOR()]);
const nume = artifact.abi.filter((x) => x.type === 'event' && x.inputs[0] && x.inputs[0].name === 'id').map((x) => x.name);
const brute = [];
for (const n of nume) for (const l of await c.queryFilter(c.filters[n](jobId), fromBlock, 'latest')) brute.push(l);
brute.sort((a, b) => a.blockNumber - b.blockNumber || a.index - b.index);
const blocuri = new Map(), txs = new Map();
const events = [];
for (const l of brute) {
if (!blocuri.has(l.blockHash)) blocuri.set(l.blockHash, await provider.getBlock(l.blockHash));
if (!txs.has(l.transactionHash)) txs.set(l.transactionHash, await provider.getTransaction(l.transactionHash));
const b = blocuri.get(l.blockHash), t = txs.get(l.transactionHash);
const args = {}; l.fragment.inputs.forEach((inp, i) => { args[inp.name] = s(l.args[i]); });
events.push({ name: l.fragment.name, args, blockNumber: l.blockNumber, blockHash: l.blockHash, timestamp: b.timestamp, txHash: l.transactionHash, from: t.from, logIndex: l.index });
}
const sub = events.find((e) => e.name === 'ResultSubmitted');
return { chainId: Number(net.chainId), market, jobId: String(jobId), constants: { arbiter, governance, arbitrationTimeout: Number(timeout), zkVerifier: zk, pinnedProofSelector: pin },
job: jobDinLant(job), events, result: sub ? sub.args.result : null, spec, envelope };
}
// ----- the judge: pure rules over a timeline -----
const ok = (m, cond, text) => { if (!cond) m.push(text); return !!cond; };
const low = (x) => String(x || '').toLowerCase();
export function judeca(d) {
const motive = [], randuri = []; const j = d.job, ev = d.events || []; const un = (n) => ev.find((e) => e.name === n); const toate = (n) => ev.filter((e) => e.name === n);
const t = (e) => (e ? e.timestamp : null);
if (!j || !ev.length) return { verdict: 'INCOMPLETE', motive: ['no job or no events'], randuri };
for (const e of ev) randuri.push(`${new Date(e.timestamp * 1000).toISOString()} #${e.blockNumber} ${e.name} by ${e.from.slice(0, 10)}.. ${Object.entries(e.args).filter(([k]) => k !== 'result').map(([k, v]) => k + '=' + (H32.test(String(v)) ? String(v).slice(0, 10) + '..' : String(v).length > 20 ? String(v).slice(0, 10) + '..' : v)).join(' ')}`);
const posted = un('JobPosted'), claimed = un('JobClaimed'), submitted = un('ResultSubmitted') || un('ResultReferenced'), disputed = un('JobDisputed'), resolved = un('DisputeResolved'), timedOut = un('DisputeTimedOut'), paid = un('JobPaid'), refunded = un('JobRefunded'), slashed = un('JobSlashed');
// R0 the posting matches the job (the reward is compared through the payout events: the contract zeroes j.reward on settlement)
ok(motive, posted && low(posted.args.requester) === low(j.requester) && low(posted.args.specHash) === low(j.specHash) && Number(posted.args.deadline) === j.deadline && (j.reward === '0' || posted.args.reward === j.reward), 'the JobPosted event does not match the job (requester, reward, specHash, deadline)');
// R1 deadlines: a claim and a submission only up to the deadline
if (claimed) ok(motive, t(claimed) <= j.deadline /*PAZA:termen*/, 'the job was claimed after its deadline');
if (submitted) ok(motive, t(submitted) <= j.deadline && t(submitted) === j.submittedAt /*PAZA:termen*/, 'the result was submitted after the deadline, or submittedAt does not match the event');
if (refunded && claimed && !submitted && !disputed) ok(motive, t(refunded) > j.deadline /*PAZA:termen*/, 'the escrow was reclaimed as expired before the deadline');
// R2 the challenge window: a dispute strictly inside it, a finalize (by anyone but the requester) only after it
if (disputed) ok(motive, submitted && t(disputed) < j.submittedAt + j.challengeWindow /*PAZA:fereastra*/, 'the dispute was raised after the challenge window closed');
if (paid && !resolved && submitted) ok(motive, low(paid.from) === low(j.requester) || t(paid) >= j.submittedAt + j.challengeWindow /*PAZA:fereastra*/, 'the provider was paid inside the challenge window by someone other than the requester (acceptResult is the requester\'s)');
// R3 who resolved: the arbiter, or the permissionless timeout after ARBITRATION_TIMEOUT
if (resolved) ok(motive, low(resolved.from) === low(d.constants.arbiter) /*PAZA:arbitru*/, 'the dispute was resolved by an address that is not the ARBITER');
if (timedOut) ok(motive, disputed && t(timedOut) >= j.disputedAt + d.constants.arbitrationTimeout /*PAZA:arbitru*/, 'the dispute was timed out before ARBITRATION_TIMEOUT');
if (resolved || timedOut) ok(motive, !!disputed, 'a resolution without a dispute');
// R4 the terminal payout matches the state
const st = STATUS[j.status]; let terminal = null;
if (st === 'Paid') { const okP = paid && low(paid.args.provider) === low(j.provider) && paid.args.reward === posted.args.reward && (!claimed || paid.args.bondReturned === claimed.args.providerBond) && (!resolved || resolved.args.providerWon === true) /*PAZA:plata*/; ok(motive, okP, 'status Paid without a matching JobPaid (provider, reward, bond returned, providerWon)'); terminal = paid && { status: st, payee: paid.args.provider, amount: paid.args.reward, txHash: paid.txHash, blockHash: paid.blockHash }; }
else if (st === 'Slashed') { const okS = slashed && disputed && resolved && resolved.args.providerWon === false && low(slashed.args.challenger) === low(disputed.args.challenger) && (!claimed || slashed.args.slashedProviderBond === claimed.args.providerBond) /*PAZA:plata*/; ok(motive, okS, 'status Slashed without a matching JobSlashed (challenger, provider bond) and DisputeResolved(false)'); terminal = slashed && { status: st, payee: slashed.args.challenger, amount: slashed.args.slashedProviderBond, refundedTo: j.requester, txHash: slashed.txHash, blockHash: slashed.blockHash }; }
else if (st === 'Refunded') { const okR = refunded && low(refunded.args.requester) === low(j.requester) && refunded.args.reward === posted.args.reward /*PAZA:plata*/; ok(motive, okR, 'status Refunded without a matching JobRefunded (requester, reward)'); terminal = refunded && { status: st, payee: refunded.args.requester, amount: refunded.args.reward, txHash: refunded.txHash, blockHash: refunded.blockHash }; }
ok(motive, j.status !== 5 || j.reward === '0', 'a paid job still holds a reward');
// R5 the result bytes hash to the on-chain commitment
if (d.result && submitted) ok(motive, keccak(Buffer.from(d.result.slice(2), 'hex')) === low(j.resultHash) && low(submitted.args.resultHash) === low(j.resultHash) /*PAZA:rezultat*/, 'the submitted bytes do not hash to the job\'s resultHash');
// R6 REPLAY: the public spec recomputed; the arbiter's decision must agree with the recomputation
let replay = null;
if (d.spec && d.result && submitted) {
ok(motive, specHashOf(d.spec) === low(j.specHash), 'the public spec does not hash to the job\'s specHash');
try { const r = ruleaza(d.spec); const corect = rezultatHash(r) === low(j.resultHash); replay = { recomputedHash: rezultatHash(r), providerRight: corect };
if (disputed) { replay.challengerRecomputedRight = low(disputed.args.recomputedResultHash) === rezultatHash(r); }
if (resolved) ok(motive, resolved.args.providerWon === corect /*PAZA:replay*/, `the arbiter sided with the ${resolved.args.providerWon ? 'provider' : 'challenger'} but the replay says the provider is ${corect ? 'right' : 'wrong'}`);
if (st === 'Paid' && !resolved) ok(motive, corect /*PAZA:replay*/, 'the provider was paid for a result the replay does not reproduce (no one disputed in time)');
} catch (e) { motive.push('the replay could not run: ' + e.message); }
}
// R7 the provider's envelope names this result and existed before the window closed
if (d.envelope) {
const e = d.envelope, stmt = e.statement || {};
const okE = e.kind === 'aere-proof-of-execution-attestation' && low(stmt.outputHash) === low(j.resultHash) && low(stmt.inputHash) === low(j.specHash) && sha256(Buffer.from(JSON.stringify(stmt), 'utf8')) === low(e.statementHash) /*PAZA:dovada*/;
ok(motive, okE, 'the execution envelope does not name this job\'s specHash/resultHash, or its statementHash is not its statement\'s');
if (e.notarization && submitted) ok(motive, Number(e.notarization.firstSeen) <= j.submittedAt + j.challengeWindow /*PAZA:dovada*/, 'the envelope was notarized only after the challenge window closed');
}
const terminalStatus = ['Paid', 'Refunded', 'Slashed'].includes(st);
return { verdict: motive.length ? 'INVALID' : terminalStatus ? 'VALID' : 'INCOMPLETE', motive, randuri, status: st, terminal, replay };
}
/** The AIP-23 settlement receipt of a terminal job: the payout transaction, the parties, the amount, the instruction (market, job, status). */
export function chitanta(d, { createdAt = new Date().toISOString() } = {}) {
const r = judeca(d); if (r.verdict !== 'VALID' || !r.terminal) throw new Error('no valid terminal settlement to receipt: ' + (r.motive[0] || r.verdict));
const t = r.terminal;
return buildProof('settlement', { chainId: d.chainId, txHash: t.txHash, blockHash: t.blockHash, from: d.market, to: t.payee, amount: t.amount, asset: 'native', instruction: Buffer.from(canonical({ market: d.market, jobId: d.jobId, status: t.status, resultHash: d.job.resultHash }), 'utf8'), createdAt });
}
/** The provider's execution envelope for a result (spec in, result out), to notarize before submitting. */
export function plicExecutie({ spec, result, programSha256, createdAt = new Date().toISOString() }) {
return buildProof('execution', { program: spec.task, inputHash: specHashOf(spec), outputHash: rezultatHash(result), programSha256, exitCode: 0, createdAt });
}
export { ruleaza, rezultatHash, rezultatOcteti, specHashOf as specHash, canonical };

View File

@ -0,0 +1,71 @@
#!/usr/bin/env node
// proba-judecata.mjs: the judge of the market settlement (piata.mjs) in the laboratory, without a chain, on the timelines the live run
// recorded (dovezi/piata-28001-*.json, the newest): the honest timelines are VALID for their named status, and nine forged ones are
// INVALID for their named reason. --control-negativ plants every PAZA marker out, one at a time, in a copy of piata.mjs and requires
// the forged fixture it guards to be accepted: a check that cannot be made to fail is not a check.
// Exit 0 when every row is as named; 1 otherwise; 2 when no evidence file exists (run proba-piata-28001.mjs first).
// node proba-judecata.mjs [--control-negativ] env: AERE_PIATA_MODUL (the module under test; the control sets its planted copy)
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
const AICI = path.dirname(fileURLToPath(import.meta.url));
const MODUL = process.env.AERE_PIATA_MODUL || path.join(AICI, 'piata.mjs');
const control = process.argv.includes('--control-negativ');
const dovezi = fs.existsSync(path.join(AICI, 'dovezi')) ? fs.readdirSync(path.join(AICI, 'dovezi')).filter((f) => /^piata-28001-.*\.json$/.test(f)).sort() : [];
if (!dovezi.length) { console.log('NEMASURAT: no dovezi/piata-28001-*.json (run proba-piata-28001.mjs first)'); process.exit(2); }
const D = JSON.parse(fs.readFileSync(path.join(AICI, 'dovezi', dovezi[dovezi.length - 1]), 'utf8'));
const P = await import(pathToFileURL(MODUL).href + '?t=' + Date.now());
const clona = (x) => JSON.parse(JSON.stringify(x));
const date = (s) => { const x = D.scenarii[s]; if (!x || !x.date) throw new Error('scenario ' + s + ' has no timeline in the evidence'); return clona(x.date); };
const ev = (d, n) => d.events.find((e) => e.name === n);
/** The fixtures: honest timelines (VALID, named status) and forged ones (INVALID, named reason); `paza` names the marker that guards each forgery. */
export function fixturi(P) {
const F = [];
for (const [s, st] of [['S1', 'Paid'], ['S2', 'Slashed'], ['S3', 'Paid'], ['S4', 'Refunded'], ['S5', 'Refunded']]) F.push({ nume: `${s} the recorded timeline is VALID with status ${st}`, asteptat: 'VALID', r: () => { const r = P.judeca(date(s)); return { verdict: r.verdict, detail: r.status + (r.replay ? ' replay providerRight=' + r.replay.providerRight : '') + '; ' + r.motive.join('; ') }; } });
F.push({ nume: 'N1 S1 finalized INSIDE the window by a third party (timestamp moved back)', asteptat: 'INVALID', paza: 'fereastra', r: () => { const d = date('S1'); ev(d, 'JobPaid').timestamp = d.job.submittedAt + 1; return v(P.judeca(d)); } });
F.push({ nume: 'N2 S2 dispute raised AFTER the window (timestamp moved forward)', asteptat: 'INVALID', paza: 'fereastra', r: () => { const d = date('S2'); ev(d, 'JobDisputed').timestamp = d.job.submittedAt + d.job.challengeWindow + 5; return v(P.judeca(d)); } });
F.push({ nume: 'N3 S2 resolved by an address that is not the arbiter', asteptat: 'INVALID', paza: 'arbitru', r: () => { const d = date('S2'); ev(d, 'DisputeResolved').from = d.job.requester; return v(P.judeca(d)); } });
F.push({ nume: 'N4 S1 claimed after the deadline (deadline moved before the claim)', asteptat: 'INVALID', paza: 'termen', r: () => { const d = date('S1'); d.job.deadline = ev(d, 'JobClaimed').timestamp - 1; ev(d, 'JobPosted').args.deadline = String(d.job.deadline); return v(P.judeca(d)); } });
F.push({ nume: 'N5 S1 paid to someone who is not the provider', asteptat: 'INVALID', paza: 'plata', r: () => { const d = date('S1'); ev(d, 'JobPaid').args.provider = d.job.requester; return v(P.judeca(d)); } });
F.push({ nume: 'N6 S1 result bytes that do not hash to the commitment', asteptat: 'INVALID', paza: 'rezultat', r: () => { const d = date('S1'); d.result = d.result.slice(0, -2) + (d.result.endsWith('00') ? '01' : '00'); return v(P.judeca(d)); } });
F.push({ nume: 'N7 S3 the arbiter sided with the provider although the replay contradicts it (spec swapped to another seed)', asteptat: 'INVALID', paza: 'replay', r: () => { const d = date('S3'); d.spec = { ...d.spec, seed: '0x' + 'ab'.repeat(32) }; d.job.specHash = P.specHash(d.spec); ev(d, 'JobPosted').args.specHash = d.job.specHash; return v(P.judeca(d)); } });
F.push({ nume: 'N8 S1 the envelope names another result (outputHash changed, statementHash kept)', asteptat: 'INVALID', paza: 'dovada', r: () => { const d = date('S1'); d.envelope.statement.outputHash = '0x' + 'cc'.repeat(32); return v(P.judeca(d)); } });
F.push({ nume: 'N9 S1 the envelope notarized only after the window closed', asteptat: 'INVALID', paza: 'dovada', r: () => { const d = date('S1'); d.envelope.notarization.firstSeen = d.job.submittedAt + d.job.challengeWindow + 1; return v(P.judeca(d)); } });
F.push({ nume: 'N10 S4 reclaimed as expired BEFORE the deadline', asteptat: 'INVALID', paza: 'termen', r: () => { const d = date('S4'); ev(d, 'JobRefunded').timestamp = d.job.deadline - 1; return v(P.judeca(d)); } });
F.push({ nume: 'R1 the settlement receipt of S1 is an AIP-23 settlement envelope naming the payout tx and the provider', asteptat: 'VALID', r: () => { const d = date('S1'); const c = P.chitanta(d); const t = ev(d, 'JobPaid'); return { verdict: c.kind === 'aere-proof-of-settlement-attestation' && c.statement.txHash === t.txHash && c.statement.to.toLowerCase() === d.job.provider.toLowerCase() && c.statement.chainId === 28001 ? 'VALID' : 'INVALID', detail: c.statementHash.slice(0, 18) + '..' }; } });
return F;
}
const v = (r) => ({ verdict: r.verdict, detail: r.motive.join('; ') });
if (!control) {
let verzi = 0, rosii = 0, stricate = 0;
for (const f of fixturi(P)) {
let r; try { r = f.r(); } catch (e) { r = { verdict: 'STRICAT', detail: String(e.message).slice(0, 160) }; }
const stare = r.verdict === 'STRICAT' ? 'STRICAT' : r.verdict === f.asteptat ? 'VERDE' : 'ROSU'; stare === 'VERDE' ? verzi++ : stare === 'ROSU' ? rosii++ : stricate++;
console.log(` [${stare.padEnd(7)}] ${f.nume}: ${r.verdict}${r.detail ? ' (' + r.detail.slice(0, 150) + ')' : ''}`);
}
console.log(`\nmarket judge (evidence ${dovezi[dovezi.length - 1]}): ${verzi} VERDE, ${rosii} ROSU, ${stricate} STRICAT`);
process.exitCode = rosii || stricate ? 1 : 0;
} else {
const SURSA = fs.readFileSync(MODUL, 'utf8'); const marcaje = ['termen', 'fereastra', 'arbitru', 'plata', 'rezultat', 'replay', 'dovada']; let bune = 0;
for (const m of marcaje) {
const copie = path.join(AICI, `_plantat-piata-${process.pid}-${m}.mjs`); const n = SURSA.split(`/*PAZA:${m}*/`).length - 1; const tinte = fixturi(P).filter((f) => f.paza === m);
let stare, detaliu;
try {
if (n < 1 || !tinte.length) { stare = 'STRICAT'; detaliu = `marker ${n} times, ${tinte.length} fixtures`; }
else {
fs.writeFileSync(copie, SURSA.split(`/*PAZA:${m}*/`).join("|| String(1) === '1' /*PLANTAT*/"));
const Q = await import(pathToFileURL(copie).href + '?t=' + Date.now()); const fx = fixturi(Q); const rez = [];
for (const t of tinte) { const f = fx.find((x) => x.nume === t.nume); const r = f.r(); rez.push(`${t.nume.split(' ')[0]}:${r.verdict}`); }
const toate = rez.every((x) => x.endsWith(':VALID')); stare = toate ? 'VERDE' : 'ROSU'; detaliu = (toate ? 'the forged fixture is accepted with the check planted out: ' : 'the planting did not change the verdict: ') + rez.join(' ');
}
} catch (e) { stare = 'STRICAT'; detaliu = String(e.message).slice(0, 160); } finally { fs.rmSync(copie, { force: true }); }
if (stare === 'VERDE') bune++;
console.log(` [${stare.padEnd(7)}] PAZA:${m} (${n} marker${n === 1 ? '' : 's'}) ${detaliu}`);
}
const ramase = fs.readdirSync(AICI).filter((f) => f.startsWith('_plantat-piata-'));
console.log(`CONTROL: ${bune} of ${marcaje.length} checks proven load-bearing${ramase.length ? '; copies left: ' + ramase.join(',') : ''}`);
process.exitCode = bune === marcaje.length && !ramase.length ? 0 : 1;
}

View File

@ -0,0 +1,170 @@
#!/usr/bin/env node
// proba-piata-28001.mjs: the complete market flow MEASURED on testnet 28001 (roadmap point 28): order -> proof -> dispute with a deadline
// -> settlement, on AereComputeMarketV3 deployed from the committed artifact, with test keys only (the requester's key from
// AERE_TESTNET_KEY_FILE; the provider's and the arbiter's derived from it, never printed, funded from it). Never chain 2800.
// S1 happy path post (escrow) -> claim (bond) -> compute -> execution envelope NOTARIZED -> submit -> finalize refused inside the
// window (WindowOpen) -> window passes -> dispute refused (WindowClosed) -> finalize by a third party -> Paid;
// the judge VALID; the envelope read back under a post-quantum certified anchor; the AIP-23 settlement receipt
// S2 dispute upheld post -> claim -> a WRONG result -> the requester recomputes and disputes (bond) -> timeout refused
// (ArbitrationTimeoutNotPassed), a non-arbiter refused (NotArbiter) -> the arbiter rules against the provider -> Slashed
// S3 dispute lost post -> claim -> a correct result -> a bad-faith dispute -> the arbiter rules for the provider -> Paid, challenger forfeits
// S4 deadline post (short deadline) -> claim -> no result -> reclaim refused before the deadline -> after it, reclaimExpired -> Refunded
// S5 open job post (short deadline) -> after it, claim refused (DeadlinePassed) -> cancelJob -> Refunded
// Every scenario's timeline is judged by piata.mjs (pure rules) and saved in dovezi/ for the offline laboratory (proba-judecata.mjs).
// Exit 0 when every row is as named; 1 otherwise; 2 when the chain, the key or ethers are missing.
// AERE_TESTNET_KEY_FILE=<key> AERE_ETHERS_DIR=<dir with node_modules/ethers> node proba-piata-28001.mjs [--fereastra 90] [--piata 0x..]
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import crypto from 'node:crypto';
import { createRequire } from 'node:module';
import { spawnSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';
import { incarcaArtefact, verificaDesfasurarea, contract, cronologie, judeca, chitanta, plicExecutie, ruleaza, rezultatOcteti, rezultatHash, specHash } from './piata.mjs';
import { specNou, programSha256 } from './sarcina.mjs';
import { notarizeEnvelope } from '../agents/agent-ancora.mjs';
const AICI = path.dirname(fileURLToPath(import.meta.url));
const RAD = path.resolve(AICI, '..', '..');
const a = process.argv.slice(2); const opt = (k, d) => { const i = a.indexOf(k); return i >= 0 ? a[i + 1] : d; };
const RPC = process.env.AERE_TESTNET_RPC || 'https://testnet-rpc.aere.network';
const KF = process.env.AERE_TESTNET_KEY_FILE || path.join(RAD, 'tools', 'aere-quantum-shield', 'dovezi-l1', '.chei', 'cheie-de-proba-28001.key');
const ETHERS_DIR = process.env.AERE_ETHERS_DIR || path.join(RAD, 'contracts');
const VERIFY_PROOF = process.env.AERE_VERIFY_PROOF || path.join(RAD, 'verificator-falcon', 'verify-proof.mjs');
const FEREASTRA = Number(opt('--fereastra', 90));
const NOTAR_28001 = '0x70099E62735500AA2F85B60C518551a57B202d54';
const PIN = '0x4388a21c';
const DOVEZI = path.join(AICI, 'dovezi'); fs.mkdirSync(DOVEZI, { recursive: true });
const DESF = path.join(AICI, 'desfasurare-28001.json');
const nem = (m) => { console.log('NEMASURAT: ' + m); process.exit(2); };
let verzi = 0, rosii = 0; const cer = (n, c, d = '') => { console.log(` [${c ? 'VERDE ' : 'ROSU '}] ${n}${d ? ' (' + String(d).slice(0, 200) + ')' : ''}`); c ? verzi++ : rosii++; };
if (!fs.existsSync(KF)) nem('no test key at ' + KF + ' (AERE_TESTNET_KEY_FILE)');
let ethers; try { ({ ethers } = createRequire(path.join(ETHERS_DIR, 'package.json'))('ethers')); } catch (e) { nem('ethers not loadable from ' + ETHERS_DIR + ': ' + e.message); }
const cheia = (f) => { const l = fs.readFileSync(f, 'utf8').split(/\r?\n/).map((x) => x.trim()).find((x) => /^(d|PRIVATE_KEY)=/.test(x)) || ''; const h = l.replace(/^(d|PRIVATE_KEY)=/, '').replace(/^0x/, ''); if (!/^[0-9a-fA-F]{1,64}$/.test(h)) nem('the key file has no d=<hex> line'); return '0x' + h.padStart(64, '0'); };
const provider = new ethers.JsonRpcProvider(RPC, undefined, { staticNetwork: false });
const chainId = Number((await provider.getNetwork()).chainId); if (chainId !== 28001) nem(`the RPC serves chain ${chainId}; this flow runs only on testnet 28001`);
const req = new ethers.Wallet(cheia(KF), provider);
const deriva = (rol) => new ethers.Wallet('0x' + crypto.createHash('sha256').update(req.privateKey + ':' + rol).digest('hex'), provider); // derived, never printed
const prov = deriva('provider'), arb = deriva('arbiter');
const art = incarcaArtefact();
const E = (n) => ethers.parseEther(n);
const ora = async () => (await provider.getBlock('latest')).timestamp;
const asteapta = async (ts, ce) => { process.stdout.write(` waiting for chain time >= ${ts} (${ce})`); while ((await ora()) < ts) { await new Promise((r) => setTimeout(r, 3000)); process.stdout.write('.'); } console.log(''); };
const tx = async (nume, p) => { const r = await (await p).wait(1, 180000); if (!r || r.status !== 1) throw new Error(nume + ' failed'); console.log(` ${nume}: tx ${r.hash.slice(0, 18)}.. block ${r.blockNumber} gas ${r.gasUsed}`); return r; };
// a refusal by a NAMED custom error (static call), not just "it reverted"
const refuza = async (nume, fn, eroare, c) => { try { await fn(); return cer(nume, false, 'did not revert'); } catch (e) { let n = e.revert && e.revert.name; if (!n && e.data) { try { n = c.interface.parseError(e.data).name; } catch {} } if (!n && e.info && e.info.error && e.info.error.data) { try { n = c.interface.parseError(e.info.error.data).name; } catch {} } cer(nume, n === eroare, 'reverted with ' + (n || String(e.shortMessage || e.message).slice(0, 80))); } };
const verifyProof = (env, rpc = null) => { const f = path.join(os.tmpdir(), 'piata-' + crypto.randomUUID() + '.json'); fs.writeFileSync(f, JSON.stringify(env)); try { const r = spawnSync(process.execPath, [VERIFY_PROOF, f, '--json', ...(rpc ? ['--rpc', rpc, '--chain', '28001'] : [])], { encoding: 'utf8', timeout: 300000 }); try { return JSON.parse(r.stdout); } catch { return null; } } finally { fs.rmSync(f, { force: true }); } };
const dovada = { la: new Date().toISOString(), chainId, rpc: RPC, fereastra: FEREASTRA, scenarii: {} };
// ----- deployment (reused when the code at the recorded address is the artifact with our immutables) -----
const ARGS = { ZK_VERIFIER: NOTAR_28001, PINNED_PROOF_SELECTOR: PIN, ARBITER: arb.address, GOVERNANCE: req.address };
let piata = opt('--piata', null);
if (!piata && fs.existsSync(DESF)) { const d = JSON.parse(fs.readFileSync(DESF, 'utf8')); if (d.chainId === chainId) piata = d.market; }
if (piata) { const v = await verificaDesfasurarea({ provider, market: piata, artifact: art, args: ARGS }); if (!v.ok) { console.log(' the recorded market does not verify (' + v.motiv + '); deploying anew'); piata = null; } }
if (!piata) {
console.log(` deploying AereComputeMarketV3 from the artifact (${art.solc}; ZK_VERIFIER = AereNotary 28001: a contract with code, so ZK_VERIFIED jobs can never pay here; pin ${PIN})`);
const f = new ethers.ContractFactory(art.abi, art.bytecode, req);
const c = await f.deploy(ARGS.ZK_VERIFIER, ARGS.ARBITER, ARGS.GOVERNANCE, ARGS.PINNED_PROOF_SELECTOR); const rc = await c.deploymentTransaction().wait(1, 180000);
piata = await c.getAddress();
fs.writeFileSync(DESF, JSON.stringify({ chainId, market: piata, deployTx: rc.hash, block: rc.blockNumber, args: ARGS, artifactSourceSha256: art.sourceSha256, at: new Date().toISOString() }, null, 1));
console.log(` deployed at ${piata}, tx ${rc.hash.slice(0, 18)}.. block ${rc.blockNumber} gas ${rc.gasUsed}`);
}
const desf = JSON.parse(fs.readFileSync(DESF, 'utf8'));
const vd = await verificaDesfasurarea({ provider, market: piata, artifact: art, args: ARGS });
cer('D1 the market\'s runtime code is the committed artifact with ZK_VERIFIER, pin, ARBITER, GOVERNANCE as recorded', vd.ok, vd.motiv);
dovada.desfasurare = { ...desf, codeOk: vd.ok };
// ----- funding the derived keys from the requester's test key -----
for (const [w, n, min] of [[prov, 'provider', '0.06'], [arb, 'arbiter', '0.03']]) { if ((await provider.getBalance(w.address)) < E(min)) await tx('fund ' + n, req.sendTransaction({ to: w.address, value: E(min) })); }
const C = (w) => contract(ethers, provider, piata, art, w);
const posteaza = async (spec, { deadlineIn = 600, window = FEREASTRA, reward = '0.01', bond = '0.004' } = {}) => {
const deadline = (await ora()) + deadlineIn;
const r = await tx('postJob', C(req).postJob({ token: ethers.ZeroAddress, reward: E(reward), providerBond: E(bond), specHash: specHash(spec), zkProgramVKey: ethers.ZeroHash, mode: 0, deadline, challengeWindow: window, pqcSettlement: false, offchainResult: false }, { value: E(reward) }));
const id = Number(C(req).interface.parseLog(r.logs.find((l) => l.address.toLowerCase() === piata.toLowerCase())).args.id);
return { id, deadline, bond: E(bond) };
};
const scenariu = async (nume, titlu, fn) => { console.log(`\n${nume} ${titlu}`); const spec = specNou({ iters: 20000 }); const s = { spec, pasi: [] }; dovada.scenarii[nume] = s; try { await fn(spec, s); } catch (e) { cer(nume + ' did not break', false, e.shortMessage || e.message); } };
const judecaSiSalveaza = async (nume, s, id, extra = {}) => { const d = await cronologie({ ethers, provider, market: piata, artifact: art, jobId: id, fromBlock: desf.block, spec: s.spec, envelope: extra.envelope || null }); const r = judeca(d); s.date = d; s.judecata = r; for (const l of r.randuri) console.log(' ' + l); cer(`${nume} the judge on the timeline: VALID, status ${r.status}${r.replay ? ', replay ' + (r.replay.providerRight ? 'confirms' : 'contradicts') + ' the provider' : ''}`, r.verdict === 'VALID', r.motive.join('; ')); return { d, r }; };
try {
await scenariu('S1', 'happy path: order -> proof notarized -> submit -> window -> finalize -> Paid', async (spec, s) => {
const { id } = await posteaza(spec); s.jobId = id;
await tx('claimJob (provider, bond)', C(prov).claimJob(id, '0x', { value: E('0.004') }));
const t0 = Date.now(); const rez = ruleaza(spec); s.computeMs = Date.now() - t0;
const env = plicExecutie({ spec, result: rez, programSha256: programSha256() });
// one notarization formula for the whole house (agents/agent-ancora.mjs): it reads the key from a file, so the provider's derived
// key goes through a 0600 temp file that lives only for this call
const kt = path.join(os.tmpdir(), 'piata-prov-' + process.pid + '.key'); fs.writeFileSync(kt, 'd=' + prov.privateKey.slice(2) + '\n', { mode: 0o600 });
let n; try { n = await notarizeEnvelope({ envelope: env, kind: 'aere-proof-of-execution', rpc: RPC, keyFile: kt, ethers }); } finally { fs.rmSync(kt, { force: true }); }
if (!n.ok) throw new Error('notarization of the execution envelope: ' + n.motiv);
const notarizat = n.envelope; console.log(` notarized the execution envelope (provider): firstSeen ${notarizat.notarization.firstSeen}${notarizat.notarization.txHash ? ', tx ' + notarizat.notarization.txHash.slice(0, 18) + '..' : ''}`);
s.envelope = notarizat;
await tx('submitResult (provider)', C(prov).submitResult(id, rezultatOcteti(rez)));
await refuza('S1 finalize inside the window is refused: WindowOpen', () => C(arb).finalize.staticCall(id, '0x'), 'WindowOpen', C(arb));
const j = await C(req).getJob(id); await asteapta(Number(j.submittedAt) + FEREASTRA, 'the challenge window');
await refuza('S1 a dispute after the window is refused: WindowClosed', () => C(req).dispute.staticCall(id, rezultatHash(rez), { value: E('0.004') }), 'WindowClosed', C(req));
const before = await provider.getBalance(prov.address);
await tx('finalize (a third party: the arbiter key as "anyone")', C(arb).finalize(id, '0x'));
const after = await provider.getBalance(prov.address);
cer('S1 the provider received reward + bond (0.014 tAERE) at finalize', after - before === E('0.014'), ethers.formatEther(after - before) + ' tAERE');
const { d, r } = await judecaSiSalveaza('S1', s, id, { envelope: notarizat });
let v = null; for (let k = 0; k < 24; k++) { v = verifyProof(notarizat, RPC); const f = v && (v.levels || []).find((l) => l.level === 'finality'); if (f && f.state !== 'PENDING') break; await new Promise((ok) => setTimeout(ok, 5000)); }
const fin = v && (v.levels || []).find((l) => l.level === 'finality'); s.verifyProof = v;
cer('S1 the provider\'s execution envelope verifies with the reference verifier under a post-quantum certified anchor (finality PASSED)', v && v.verdict === 'VALID' && fin && fin.state === 'PASSED', fin ? fin.state + ': ' + String(fin.detail).slice(0, 120) : '?');
cer('S1 the notarized proof existed before the window closed (firstSeen <= submittedAt + window)', notarizat.notarization.firstSeen <= d.job.submittedAt + d.job.challengeWindow, `firstSeen ${notarizat.notarization.firstSeen}, window end ${d.job.submittedAt + d.job.challengeWindow}`);
const ch = chitanta(d); s.chitanta = ch; const vc = verifyProof(ch); s.chitantaVerdict = vc;
cer('S1 the settlement receipt (AIP-23 settlement) names the payout tx, the provider and 0.01 tAERE, and verifies (VALID, finality ABSENT: not notarized)', ch.statement.txHash === r.terminal.txHash && ch.statement.to.toLowerCase() === prov.address.toLowerCase() && ch.statement.amount === E('0.01').toString() && vc && vc.verdict === 'VALID', vc && (vc.levels || []).map((l) => l.level + ':' + l.state).join(' '));
});
await scenariu('S2', 'dispute upheld: a wrong result, the requester recomputes and disputes, the arbiter slashes', async (spec, s) => {
const { id } = await posteaza(spec); s.jobId = id;
await tx('claimJob (provider, bond)', C(prov).claimJob(id, '0x', { value: E('0.004') }));
const rez = ruleaza(spec); const gresit = { ...rez, digest: '0x' + 'ee'.repeat(32) };
await tx('submitResult (provider, WRONG digest)', C(prov).submitResult(id, rezultatOcteti(gresit)));
const recomputat = rezultatHash(ruleaza(spec));
await tx('dispute (requester as challenger, recomputed hash, bond)', C(req).dispute(id, recomputat, { value: E('0.004') }));
await refuza('S2 resolveByTimeout before ARBITRATION_TIMEOUT is refused: ArbitrationTimeoutNotPassed', () => C(req).resolveByTimeout.staticCall(id), 'ArbitrationTimeoutNotPassed', C(req));
await refuza('S2 resolveDispute by a non-arbiter is refused: NotArbiter', () => C(req).resolveDispute.staticCall(id, false), 'NotArbiter', C(req));
const before = await provider.getBalance(req.address);
const r = await tx('resolveDispute(false) by the arbiter', C(arb).resolveDispute(id, false));
const after = await provider.getBalance(req.address);
cer('S2 the requester got the reward back + its bond back + the provider\'s slashed bond (0.018 tAERE)', after - before === E('0.018'), ethers.formatEther(after - before) + ' tAERE');
await judecaSiSalveaza('S2', s, id);
s.gasResolve = String(r.gasUsed);
});
await scenariu('S3', 'dispute lost: a correct result disputed in bad faith; the arbiter sides with the provider', async (spec, s) => {
const { id } = await posteaza(spec); s.jobId = id;
await tx('claimJob (provider, bond)', C(prov).claimJob(id, '0x', { value: E('0.004') }));
const rez = ruleaza(spec);
await tx('submitResult (provider, correct)', C(prov).submitResult(id, rezultatOcteti(rez)));
await tx('dispute (requester, a made-up recomputed hash, bond)', C(req).dispute(id, '0x' + 'dd'.repeat(32), { value: E('0.004') }));
const before = await provider.getBalance(prov.address);
await tx('resolveDispute(true) by the arbiter', C(arb).resolveDispute(id, true));
const after = await provider.getBalance(prov.address);
cer('S3 the provider received reward + its bond + the challenger\'s forfeited bond (0.018 tAERE)', after - before === E('0.018'), ethers.formatEther(after - before) + ' tAERE');
await judecaSiSalveaza('S3', s, id);
});
await scenariu('S4', 'deadline: claimed, never delivered; the requester reclaims after the deadline, the bond is slashed to it', async (spec, s) => {
const { id, deadline } = await posteaza(spec, { deadlineIn: 45 }); s.jobId = id;
await tx('claimJob (provider, bond)', C(prov).claimJob(id, '0x', { value: E('0.004') }));
await refuza('S4 reclaimExpired before the deadline is refused: DeadlineNotPassed', () => C(req).reclaimExpired.staticCall(id), 'DeadlineNotPassed', C(req));
await asteapta(deadline + 1, 'the deadline');
await refuza('S4 submitResult after the deadline is refused: DeadlinePassed', () => C(prov).submitResult.staticCall(id, rezultatOcteti(ruleaza(spec))), 'DeadlinePassed', C(prov));
const before = await provider.getBalance(req.address);
const r = await tx('reclaimExpired (requester)', C(req).reclaimExpired(id));
const after = await provider.getBalance(req.address); const gaz = r.gasUsed * (r.gasPrice || r.effectiveGasPrice || 0n);
cer('S4 the requester got the reward + the slashed provider bond (0.014 tAERE, minus its own gas)', after - before + gaz === E('0.014'), ethers.formatEther(after - before + gaz) + ' tAERE net of gas');
await judecaSiSalveaza('S4', s, id);
});
await scenariu('S5', 'an open job past its deadline: a claim is refused, the requester cancels', async (spec, s) => {
const { id, deadline } = await posteaza(spec, { deadlineIn: 30 }); s.jobId = id;
await asteapta(deadline + 1, 'the deadline');
await refuza('S5 claimJob after the deadline is refused: DeadlinePassed', () => C(prov).claimJob.staticCall(id, '0x', { value: E('0.004') }), 'DeadlinePassed', C(prov));
await tx('cancelJob (requester)', C(req).cancelJob(id));
await judecaSiSalveaza('S5', s, id);
});
} finally {
dovada.rezumat = { verzi, rosii }; const out = path.join(DOVEZI, 'piata-28001-' + new Date().toISOString().replace(/[:.]/g, '').slice(0, 15) + 'Z.json');
fs.writeFileSync(out, JSON.stringify(dovada, null, 1)); console.log('\n evidence: ' + path.relative(RAD, out));
console.log(`market settlement on 28001: ${verzi} VERDE, ${rosii} ROSU`);
process.exitCode = rosii ? 1 : 0;
}

View File

@ -0,0 +1,55 @@
// sarcina.mjs: the REPLAY workload of the market flow (roadmap point 28): a deterministic task anyone can recompute, so a challenger
// can dispute a wrong result with the hash it recomputed and an arbiter (or any reader) can see who is right. keccak-chain: start from a
// 32-byte seed and apply keccak256 `iters` times. The spec is public (its keccak256 is the job's specHash); the result is the JSON
// bytes { task, seed, iters, digest } whose keccak256 is the job's resultHash. Only node:crypto: keccak-256 is the dependency-free
// BigInt implementation of the Cloud gateway (cloud-gateway/gateway.mjs), copied here so the public component has no package to install;
// its positive control is the recorded testnet evidence itself (resultHash computed on chain = the replay here, proba-judecata.mjs S1/S3).
import crypto from 'node:crypto';
// ----- keccak-256 (FIPS 202 permutation, keccak padding 0x01..0x80, rate 136) -----
const MASK = (1n << 64n) - 1n;
const RC = [0x0000000000000001n, 0x0000000000008082n, 0x800000000000808an, 0x8000000080008000n, 0x000000000000808bn, 0x0000000080000001n, 0x8000000080008081n, 0x8000000000008009n,
0x000000000000008an, 0x0000000000000088n, 0x0000000080008009n, 0x000000008000000an, 0x000000008000808bn, 0x800000000000008bn, 0x8000000000008089n, 0x8000000000008003n,
0x8000000000008002n, 0x8000000000000080n, 0x000000000000800an, 0x800000008000000an, 0x8000000080008081n, 0x8000000000008080n, 0x0000000080000001n, 0x8000000080008008n];
const ROT = [[0, 36, 3, 41, 18], [1, 44, 10, 45, 2], [62, 6, 43, 15, 61], [28, 55, 25, 21, 56], [27, 20, 39, 8, 14]];
const rotl = (v, n) => (n === 0 ? v : (((v << BigInt(n)) | (v >> BigInt(64 - n))) & MASK));
function keccakF(A) {
for (let rr = 0; rr < 24; rr++) {
const C = new Array(5), D = new Array(5);
for (let x = 0; x < 5; x++) C[x] = A[x] ^ A[x + 5] ^ A[x + 10] ^ A[x + 15] ^ A[x + 20];
for (let x = 0; x < 5; x++) D[x] = C[(x + 4) % 5] ^ rotl(C[(x + 1) % 5], 1);
for (let x = 0; x < 5; x++) for (let y = 0; y < 5; y++) A[x + 5 * y] ^= D[x];
const B = new Array(25);
for (let x = 0; x < 5; x++) for (let y = 0; y < 5; y++) B[y + 5 * ((2 * x + 3 * y) % 5)] = rotl(A[x + 5 * y], ROT[x][y]);
for (let x = 0; x < 5; x++) for (let y = 0; y < 5; y++) A[x + 5 * y] = B[x + 5 * y] ^ ((~B[((x + 1) % 5) + 5 * y] & MASK) & B[((x + 2) % 5) + 5 * y]);
A[0] ^= RC[rr];
}
}
export function keccak256(bytes) {
const rate = 136; const A = new Array(25).fill(0n); const msg = Buffer.from(bytes);
const padded = Buffer.concat([msg, Buffer.alloc(rate - (msg.length % rate))]);
padded[msg.length] |= 0x01; padded[padded.length - 1] |= 0x80;
for (let off = 0; off < padded.length; off += rate) { for (let i = 0; i < rate / 8; i++) A[i] ^= padded.readBigUInt64LE(off + i * 8); keccakF(A); }
const out = Buffer.alloc(32); for (let i = 0; i < 4; i++) out.writeBigUInt64LE(A[i], i * 8); return out;
}
export const TASK = 'keccak-chain';
const hex = (b) => '0x' + Buffer.from(b).toString('hex');
export const keccak = (b) => hex(keccak256(b));
/** Canonical JSON of a spec or a result: keys sorted, no whitespace. */
export const canonical = (x) => JSON.stringify(x, Object.keys(x).sort());
export function specNou({ iters = 20000, seed = hex(crypto.randomBytes(32)) } = {}) { return { task: TASK, seed, iters }; }
export const specHash = (spec) => keccak(Buffer.from(canonical(spec), 'utf8'));
/** Runs the task: the digest after `iters` keccak256 applications to the seed. */
export function ruleaza(spec) {
if (spec.task !== TASK) throw new Error('unknown task ' + spec.task);
if (!/^0x[0-9a-f]{64}$/.test(spec.seed) || !Number.isInteger(spec.iters) || spec.iters < 1 || spec.iters > 5e6) throw new Error('bad spec');
let d = Buffer.from(spec.seed.slice(2), 'hex');
for (let i = 0; i < spec.iters; i++) d = keccak256(d);
return { task: TASK, seed: spec.seed, iters: spec.iters, digest: hex(d) };
}
/** The result bytes a provider submits (and a challenger recomputes), and their on-chain hash. */
export const rezultatOcteti = (r) => Buffer.from(canonical(r), 'utf8');
export const rezultatHash = (r) => keccak(rezultatOcteti(r));
/** The SHA-256 of this program's own source, for the `programSha256` of the execution envelope. */
export const programSha256 = () => '0x' + crypto.createHash('sha256').update(Buffer.from(ruleaza.toString() + keccak.toString(), 'utf8')).digest('hex');