payments: the public reader of a receipt is tested on 20 named cases (was 14), including a run with no usable verifier, which now stops before reading the chain and says why (proba-plata-fara-verificator.mjs, new); proba-plata-28001.mjs exits with the reason when the verifier is missing; README counts updated (1255 files).
This commit is contained in:
parent
687e5e54a4
commit
5f1b781e7f
@ -50,7 +50,7 @@ test, counts as a failure of the control. Results measured on 2026-09-29 (Node.j
|
|||||||
| identity | 52/52 (`node proba-identity.mjs`, a presentation optionally signed for one exact request (`request`) included, with the AIP-23 verifier for its envelope test; without it that test is reported as skipped and the exit code is 2), compliance 18/18 (`node proba-conformitate.mjs`) and Travel Rule 20/20 (`node proba-travel-rule.mjs`) with the same verifier (without it each reports one test unmeasured and exits 2; 2026-10-18: a missing originator id or nonce is a refusal, found when the verifier was enrolled in the conformance suite), SD-JWT 31/31 (`node proba-sdjwt.mjs`, the RFC 9901 vectors and fixtures derived from @sd-jwt/core 0.23.0 included; since 2026-10-19 the keys of an AERE identity are judged on their ML-DSA-65 half by default, the Ed25519 half only on request, and every verdict says `postQuantum`), SD-JWT VC 11/11 (`node proba-sdjwt-vc.mjs`); interoperability with @sd-jwt/core and jose in both directions 26/26 and 28/28 (`interop/`, with those libraries installed in a folder you name; otherwise reported unmeasured), measured 2026-10-18, before the 2026-10-19 change; the scripts now ask for the Ed25519 half explicitly and have not been re-run since | 57/57 (`node control-negativ-identity.mjs`); compliance 17/17 (`node control-negativ-conformitate.mjs`); Travel Rule 20/20 (`node control-negativ-travel-rule.mjs`); SD-JWT 39/39 (`node control-negativ-sdjwt.mjs`); SD-JWT VC 20/20 (`node control-negativ-sdjwt-vc.mjs`) |
|
| identity | 52/52 (`node proba-identity.mjs`, a presentation optionally signed for one exact request (`request`) included, with the AIP-23 verifier for its envelope test; without it that test is reported as skipped and the exit code is 2), compliance 18/18 (`node proba-conformitate.mjs`) and Travel Rule 20/20 (`node proba-travel-rule.mjs`) with the same verifier (without it each reports one test unmeasured and exits 2; 2026-10-18: a missing originator id or nonce is a refusal, found when the verifier was enrolled in the conformance suite), SD-JWT 31/31 (`node proba-sdjwt.mjs`, the RFC 9901 vectors and fixtures derived from @sd-jwt/core 0.23.0 included; since 2026-10-19 the keys of an AERE identity are judged on their ML-DSA-65 half by default, the Ed25519 half only on request, and every verdict says `postQuantum`), SD-JWT VC 11/11 (`node proba-sdjwt-vc.mjs`); interoperability with @sd-jwt/core and jose in both directions 26/26 and 28/28 (`interop/`, with those libraries installed in a folder you name; otherwise reported unmeasured), measured 2026-10-18, before the 2026-10-19 change; the scripts now ask for the Ed25519 half explicitly and have not been re-run since | 57/57 (`node control-negativ-identity.mjs`); compliance 17/17 (`node control-negativ-conformitate.mjs`); Travel Rule 20/20 (`node control-negativ-travel-rule.mjs`); SD-JWT 39/39 (`node control-negativ-sdjwt.mjs`); SD-JWT VC 20/20 (`node control-negativ-sdjwt-vc.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 59/59 on 2026-10-19 (since then an equivocation proof must be made with the agent's ML-DSA-65 key), 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); the hosted witness 154/154 without a network, measured from a fresh clone of this repository on 2026-10-19 (four more rows, for the Cloud gateway door, run only where the gateway lives), with a local notary mock (`AERE_VERIFY_PROOF=<verify-proof.mjs> node proba-martor-agenti.mjs`; the AIP-23 rows need the verifier, without it they are skipped by name and the exit code is 2), and 75/75 on testnet 28001 earlier on 2026-10-19 with the form before that day's corrections; the current form has not been run on testnet yet (`proba-martor-agenti.mjs --testnet`, needs a funded testnet key or the faucet: a checkpoint notarized, the receipt VALID with post-quantum finality, a backdated ledger caught with it; evidence in `agents/dovezi-martor-agenti-28001/`); x402 wallet 25/25, 2-of-2 co-signing 16/16 and payment verifier 24/24 without a network (on 2026-10-21; its `--all-transfers` reads the range in windows of at most 5,000 blocks, an unread window is `UNMEASURED`, never empty; the two recorded testnet runs checked again on 28001 that day, `node x402/verifica-plati.mjs <evidence> --rpc https://testnet-rpc.aere.network --all-transfers`: both `VALID`, about 3.2 million blocks each in 635 to 637 windows, 5.5 to 8.5 minutes in four runs; until that day both answered `UNMEASURED`, the endpoint refusing the single request); on the public testnet 28001, 9/9 with the wallet key and 13/13 with the 2-of-2 contract wallet (`agents/x402/proba-x402-testnet.mjs`, `agents/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>`) | 28/28 (`node control-negativ-aprobare.mjs`); the chain as witness 11/11 (`node control-negativ-ancora.mjs`); the hosted witness 131/131, plus three changes of order that must leave the probe green (`node control-negativ-martor-agenti.mjs`); x402 36/36 on 2026-10-21 (`node x402/control-negativ-wallet.mjs`, the windows of `--all-transfers` included); 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 59/59 on 2026-10-19 (since then an equivocation proof must be made with the agent's ML-DSA-65 key), 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); the hosted witness 154/154 without a network, measured from a fresh clone of this repository on 2026-10-19 (four more rows, for the Cloud gateway door, run only where the gateway lives), with a local notary mock (`AERE_VERIFY_PROOF=<verify-proof.mjs> node proba-martor-agenti.mjs`; the AIP-23 rows need the verifier, without it they are skipped by name and the exit code is 2), and 75/75 on testnet 28001 earlier on 2026-10-19 with the form before that day's corrections; the current form has not been run on testnet yet (`proba-martor-agenti.mjs --testnet`, needs a funded testnet key or the faucet: a checkpoint notarized, the receipt VALID with post-quantum finality, a backdated ledger caught with it; evidence in `agents/dovezi-martor-agenti-28001/`); x402 wallet 25/25, 2-of-2 co-signing 16/16 and payment verifier 24/24 without a network (on 2026-10-21; its `--all-transfers` reads the range in windows of at most 5,000 blocks, an unread window is `UNMEASURED`, never empty; the two recorded testnet runs checked again on 28001 that day, `node x402/verifica-plati.mjs <evidence> --rpc https://testnet-rpc.aere.network --all-transfers`: both `VALID`, about 3.2 million blocks each in 635 to 637 windows, 5.5 to 8.5 minutes in four runs; until that day both answered `UNMEASURED`, the endpoint refusing the single request); on the public testnet 28001, 9/9 with the wallet key and 13/13 with the 2-of-2 contract wallet (`agents/x402/proba-x402-testnet.mjs`, `agents/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>`) | 28/28 (`node control-negativ-aprobare.mjs`); the chain as witness 11/11 (`node control-negativ-ancora.mjs`); the hosted witness 131/131, plus three changes of order that must leave the probe green (`node control-negativ-martor-agenti.mjs`); x402 36/36 on 2026-10-21 (`node x402/control-negativ-wallet.mjs`, the windows of `--all-transfers` included); 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) |
|
||||||
| payments | 23/23 without a chain (`node proba-risc.mjs`: the verdicts under a policy, the reproducible verdict hash, a receipt verified, nine forged ones refused by name, a malformed payment refused by name); 14/14 without a chain (`node proba-verifica-chitanta.mjs`: the public reader of a receipt against a local RPC stub and a stub of the reference verifier, VALID only with the notarization final and the transaction compared on chain, PARTIAL when something was not measured, as POST /v1/payments/verify answers); 9/9 on testnet 28001 on 2026-10-02 (`AERE_TESTNET_KEY_FILE=... AERE_ETHERS_DIR=... node proba-plata-28001.mjs`: a payment allowed, paid, its receipt notarized and read back with finality PASSED, the public reader VALID; a payment above the limit refused with nothing sent; the fourth in the window refused; a forged receipt and a loosened policy INVALID) | 10/10 (`node proba-risc.mjs --control-negativ`: form, asset, counterparty, rate, per-payment limit, window, linkage, USD arithmetic, verdict reproduction, chain facts, each planted out on a copy and each forged fixture then accepted) |
|
| payments | 23/23 without a chain (`node proba-risc.mjs`: the verdicts under a policy, the reproducible verdict hash, a receipt verified, nine forged ones refused by name, a malformed payment refused by name); 20/20 without a chain (`node proba-verifica-chitanta.mjs`: the public reader of a receipt against a local RPC stub and a stub of the reference verifier, VALID only with the notarization final and the transaction compared on chain, PARTIAL when something was not measured, as POST /v1/payments/verify answers); 4/4 without a chain (`node proba-plata-fara-verificator.mjs`: the testnet flow refuses before any request when the reference verifier is missing, alone without its files, or does not start); 9/9 on testnet 28001 on 2026-10-02 (`AERE_TESTNET_KEY_FILE=... AERE_ETHERS_DIR=... AERE_VERIFY_PROOF=<verify-proof.mjs with its files and npm install> node proba-plata-28001.mjs`: a payment allowed, paid, its receipt notarized and read back with finality PASSED, the public reader VALID; a payment above the limit refused with nothing sent; the fourth in the window refused; a forged receipt and a loosened policy INVALID) | 10/10 (`node proba-risc.mjs --control-negativ`: form, asset, counterparty, rate, per-payment limit, window, linkage, USD arithmetic, verdict reproduction, chain facts, each planted out on a copy and each forged fixture then accepted) |
|
||||||
| 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 26/26 without a chain (`node proba-judecata.mjs`: seven honest readings VALID, eighteen forged or malformed ones INVALID for their named reason, the settlement receipt); the senders of the recorded timeline read again from the raw transactions on 2026-10-21, 21 events of 19 transactions each equal to the recorded one (`AERE_ETHERS_DIR=... node proba-cronologie-citire.mjs`, reads only; its control row, a real 0x50 transaction read raw, found none in the last 400 blocks that day and is not measured) | 10/10 (`node proba-judecata.mjs --control-negativ`: form, completeness of the timeline, posting, deadline, window, arbiter, payout, result bytes, replay, proof, each planted out on a copy and each forged fixture then accepted) |
|
| 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 26/26 without a chain (`node proba-judecata.mjs`: seven honest readings VALID, eighteen forged or malformed ones INVALID for their named reason, the settlement receipt); the senders of the recorded timeline read again from the raw transactions on 2026-10-21, 21 events of 19 transactions each equal to the recorded one (`AERE_ETHERS_DIR=... node proba-cronologie-citire.mjs`, reads only; its control row, a real 0x50 transaction read raw, found none in the last 400 blocks that day and is not measured) | 10/10 (`node proba-judecata.mjs --control-negativ`: form, completeness of the timeline, posting, deadline, window, arbiter, payout, result bytes, replay, proof, each planted out on a copy and each forged fixture then accepted) |
|
||||||
| verifiable-data | 23/23 with the AIP-23 verifier (`AERE_VERIFY_PROOF=... node proba-verifiable-data.mjs`: honest answers VALID, ten forged or malformed ones INVALID for their named reason, availability sampling on a local server; the envelope row is NEMASURAT without the verifier, exit 2); the two chain rows of `--viu` measured 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 | 23/23 with the AIP-23 verifier (`AERE_VERIFY_PROOF=... node proba-verifiable-data.mjs`: honest answers VALID, ten forged or malformed ones INVALID for their named reason, availability sampling on a local server; the envelope row is NEMASURAT without the verifier, exit 2); the two chain rows of `--viu` measured 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 | 16/16 with the AIP-23 verifier, measured 2026-10-05 (`AERE_VERIFY_PROOF=... node proba-lant-de-incredere.mjs`: the whole chain on a committed fixture, then thirteen bundles broken on purpose, each refused for its named reason, among them an agent with a classical key and an audit head signed with one); without the verifier it exits 2 | 11/11 (`node proba-lant-de-incredere.mjs --control-negativ`) |
|
| ai-trust | 16/16 with the AIP-23 verifier, measured 2026-10-05 (`AERE_VERIFY_PROOF=... node proba-lant-de-incredere.mjs`: the whole chain on a committed fixture, then thirteen bundles broken on purpose, each refused for its named reason, among them an agent with a classical key and an audit head signed with one); without the verifier it exits 2 | 11/11 (`node proba-lant-de-incredere.mjs --control-negativ`) |
|
||||||
@ -66,4 +66,4 @@ interface, command line and data (`definePolicy`, `verifyLedger`, `approve`, ...
|
|||||||
|
|
||||||
## Licence
|
## Licence
|
||||||
|
|
||||||
MIT, see [LICENSE](LICENSE). Files: 1254 (pq-gateway 9, pq-kms 10, pq-secrets 6, pq-pki 6, crypto-inventory 73, verify-layer 56, proof-kinds 3, control-plane 33, agents 44, identity 24, proof-of-ai 3, ai-trust 6, verifiable-data 7, market-settlement 10, payments 9, conformance 803, device-identity 11, device-operation 114, exposure-report 18, readiness 4).
|
MIT, see [LICENSE](LICENSE). Files: 1255 (pq-gateway 9, pq-kms 10, pq-secrets 6, pq-pki 6, crypto-inventory 73, verify-layer 56, proof-kinds 3, control-plane 33, agents 44, identity 24, proof-of-ai 3, ai-trust 6, verifiable-data 7, market-settlement 10, payments 10, conformance 803, device-identity 11, device-operation 114, exposure-report 18, readiness 4).
|
||||||
|
|||||||
@ -25,9 +25,12 @@ the inputs and was ALLOW, and, with the chain facts, that the transaction is thi
|
|||||||
node proba-risc.mjs # 23 rows, no chain: the verdicts under a policy, a receipt verified, nine forged ones refused by name,
|
node proba-risc.mjs # 23 rows, no chain: the verdicts under a policy, a receipt verified, nine forged ones refused by name,
|
||||||
# a malformed payment refused by name (a verdict, not an exception)
|
# a malformed payment refused by name (a verdict, not an exception)
|
||||||
node proba-risc.mjs --control-negativ # 10 plantings on copies of risc.mjs and chitanta.mjs; each forged fixture must then be accepted
|
node proba-risc.mjs --control-negativ # 10 plantings on copies of risc.mjs and chitanta.mjs; each forged fixture must then be accepted
|
||||||
AERE_TESTNET_KEY_FILE=<key> AERE_ETHERS_DIR=<dir with node_modules/ethers> node proba-plata-28001.mjs # the flow on testnet 28001
|
AERE_TESTNET_KEY_FILE=<key> AERE_ETHERS_DIR=<dir with node_modules/ethers> AERE_VERIFY_PROOF=<verify-proof.mjs> node proba-plata-28001.mjs
|
||||||
|
# the flow on testnet 28001; AERE_VERIFY_PROOF names the reference verifier (verify-proof.mjs as served at aere.network/tools, with verify-anchor.mjs, mpt.mjs, falcon512.mjs, registry-v3.mjs and package.json beside it and `npm install` done there),
|
||||||
|
# and without a usable one the flow stops NEMASURAT before it reads or sends anything
|
||||||
|
node proba-plata-fara-verificator.mjs # 4 rows, no chain: that refusal, before any request, against a local stub that counts them
|
||||||
node verifica-chitanta.mjs dovezi/chitanta-28001-<date>.json --rpc https://testnet-rpc.aere.network --verify-proof <verify-proof.mjs>
|
node verifica-chitanta.mjs dovezi/chitanta-28001-<date>.json --rpc https://testnet-rpc.aere.network --verify-proof <verify-proof.mjs>
|
||||||
node proba-verifica-chitanta.mjs # 14 rows, no chain: the public reader against a local RPC stub and a stub of the reference
|
node proba-verifica-chitanta.mjs # 20 rows, no chain: the public reader against a local RPC stub and a stub of the reference
|
||||||
# verifier, each exit code and verdict word as POST /v1/payments/verify gives them
|
# verifier, each exit code and verdict word as POST /v1/payments/verify gives them
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -35,13 +38,19 @@ Measured on 2026-10-02 on testnet 28001 (9/9): a person's payment of 0.4 tAERE a
|
|||||||
by a plain transfer the payer signed, its receipt issued, notarized by the payee and read back with finality PASSED under a
|
by a plain transfer the payer signed, its receipt issued, notarized by the payee and read back with finality PASSED under a
|
||||||
post-quantum certified anchor; a payment of 1.50 USD refused above the per-payment limit with no transaction sent; the fourth payment
|
post-quantum certified anchor; a payment of 1.50 USD refused above the per-payment limit with no transaction sent; the fourth payment
|
||||||
in the window refused above the count; the receipt forged (amount changed) INVALID; a looser policy shown under the receipt's policy
|
in the window refused above the count; the receipt forged (amount changed) INVALID; a looser policy shown under the receipt's policy
|
||||||
hash INVALID. The public reader (`verifica-chitanta.mjs`) needs only Node.js: the chain is read by plain JSON-RPC. Its file is the
|
hash INVALID. The public reader (`verifica-chitanta.mjs`) needs only Node.js, and reads the chain by plain JSON-RPC; its verdict `VALID`
|
||||||
|
also needs the reference verifier, verify-proof.mjs as served at aere.network/tools, with verify-anchor.mjs, mpt.mjs, falcon512.mjs, registry-v3.mjs and package.json beside it and `npm install` done there (corrected 2026-10-27: this said the reader needs only Node.js, and
|
||||||
|
`verify-proof.mjs` alone stops at its first import). Its file is the
|
||||||
receipt with the policy and the inputs the issuer judged (the `receiptFile` of the Payments API's receipt route), and its verdicts are
|
receipt with the policy and the inputs the issuer judged (the `receiptFile` of the Payments API's receipt route), and its verdicts are
|
||||||
those of POST /v1/payments/verify: exit 0 `VALID` only when the transaction is compared on chain and the reference verifier finds the
|
those of POST /v1/payments/verify: exit 0 `VALID` only when the transaction is compared on chain and the reference verifier finds the
|
||||||
notarization under a post-quantum certified anchor (finality `PASSED`), 1 `INVALID` with the reasons, 2 `PARTIAL` when something was
|
notarization under a post-quantum certified anchor (finality `PASSED`), 1 `INVALID` with the reasons, 2 `PARTIAL` when something was
|
||||||
not measured (no `--rpc`, no chain answer, an RPC of another chain, no `--verify-proof`, the policy or the inputs missing, finality
|
not measured (no `--rpc`, no chain answer, an RPC of another chain, no `--verify-proof`, a reference verifier that ran and gave no
|
||||||
`PENDING` or `UNMEASURED`). Corrected 2026-10-27: until this date it wrote `VALID` and exited 0 without `--verify-proof` and while
|
verdict, the policy or the inputs missing, finality `PENDING` or `UNMEASURED`). Corrected 2026-10-27: until this date it wrote `VALID`
|
||||||
the notarization was pending, and it called a file without the policy and the inputs `INVALID`.
|
and exited 0 without `--verify-proof` and while the notarization was pending, and it called a file without the policy and the inputs
|
||||||
|
`INVALID`. Corrected again the same day: an RPC of another chain was handed on to the reference verifier, which answered `FAILED`, so
|
||||||
|
an authentic receipt came out `INVALID` (the envelope is now judged offline and the verdict is `PARTIAL`); a verifier that did not
|
||||||
|
start was reported as "not run" (it is now "ran, gave no JSON", with its error line); a file that is not a receipt ended in an
|
||||||
|
exception with the exit code of `INVALID` (it is now `INVALID` by name).
|
||||||
|
|
||||||
Every refusal names its rule, in the module's working language: `rules` on a risk verdict (`forma`, `activ`, `contrapartida`,
|
Every refusal names its rule, in the module's working language: `rules` on a risk verdict (`forma`, `activ`, `contrapartida`,
|
||||||
`curs`, `limita`, `fereastra`, that is form, asset, counterparty, rate, limit and window; outside the hashed body, so the hash of a
|
`curs`, `limita`, `fereastra`, that is form, asset, counterparty, rate, limit and window; outside the hashed body, so the hash of a
|
||||||
@ -64,5 +73,7 @@ window. The conformance suite (`conformance/`) holds a vector for each of these
|
|||||||
receipt); a wrong declared rate gives a wrong USD figure, honestly attributed.
|
receipt); a wrong declared rate gives a wrong USD figure, honestly attributed.
|
||||||
- **Native transfers only in the measured flow.** An ERC-20 payment (the testnet USD token through x402) is supported by the policy's
|
- **Native transfers only in the measured flow.** An ERC-20 payment (the testnet USD token through x402) is supported by the policy's
|
||||||
asset list but not measured; the receipt's chain check compares `value` only for the native asset.
|
asset list but not measured; the receipt's chain check compares `value` only for the native asset.
|
||||||
- **Not a payment API.** No service, no queue, no callbacks; the issuer runs these modules in its own process.
|
- **This module is not a service.** The issuer runs it in its own process, or calls the hosted Payments API of Aere Cloud, which runs
|
||||||
|
these modules (`POST /v1/payments/risk`, `/receipt`, `/verify`, with a key); there is no queue and no callback, and nothing here
|
||||||
|
moves money. (Corrected 2026-10-27: this line said "Not a payment API", while the hosted API exists.)
|
||||||
- **Chain 2800: nothing** ran there; test keys only, the payee's derived from the payer's and never printed.
|
- **Chain 2800: nothing** ran there; test keys only, the payee's derived from the payer's and never printed.
|
||||||
|
|||||||
@ -33,6 +33,21 @@ 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, 220) + ')' : ''}`); c ? verzi++ : rosii++; };
|
let verzi = 0, rosii = 0; const cer = (n, c, d = '') => { console.log(` [${c ? 'VERDE ' : 'ROSU '}] ${n}${d ? ' (' + String(d).slice(0, 220) + ')' : ''}`); c ? verzi++ : rosii++; };
|
||||||
if (!fs.existsSync(KF)) nem('no test key at ' + KF);
|
if (!fs.existsSync(KF)) nem('no test key at ' + KF);
|
||||||
let ethers; try { ({ ethers } = createRequire(path.join(ETHERS_DIR, 'package.json'))('ethers')); } catch (e) { nem('ethers not loadable from ' + ETHERS_DIR + ': ' + e.message); }
|
let ethers; try { ({ ethers } = createRequire(path.join(ETHERS_DIR, 'package.json'))('ethers')); } catch (e) { nem('ethers not loadable from ' + ETHERS_DIR + ': ' + e.message); }
|
||||||
|
// 2026-10-27 (R9-S10): the reference verifier is checked BEFORE anything is read from the chain or sent to it. Until this date the probe
|
||||||
|
// checked only the key and ethers, sent its payments on testnet and came out red only afterwards, on the rows of the verifier, which a
|
||||||
|
// public clone does not hold at the default path. A probe that cannot measure says so before it spends (proba-plata-fara-verificator.mjs).
|
||||||
|
const CERE_VP = 'AERE_VERIFY_PROOF=<verify-proof.mjs as served at aere.network/tools, with verify-anchor.mjs, mpt.mjs, falcon512.mjs, registry-v3.mjs and package.json beside it and npm install done there>';
|
||||||
|
if (!fs.existsSync(VERIFY_PROOF)) nem('no reference verifier at ' + VERIFY_PROOF + '; give ' + CERE_VP);
|
||||||
|
for (const x of ['verify-anchor.mjs', 'mpt.mjs', 'falcon512.mjs', 'registry-v3.mjs']) if (!fs.existsSync(path.join(path.dirname(VERIFY_PROOF), x))) nem('the reference verifier at ' + VERIFY_PROOF + ' has no ' + x + ' beside it (the finality of a notarization needs it); give ' + CERE_VP);
|
||||||
|
{
|
||||||
|
// it must also START: an envelope with a statement and its hash, judged offline (nothing leaves this machine), must come back VALID
|
||||||
|
const st = { kind: 'aere-probe-start', at: new Date().toISOString() }, f = path.join(os.tmpdir(), 'plata-pornire-' + crypto.randomUUID() + '.json');
|
||||||
|
fs.writeFileSync(f, JSON.stringify({ statement: st, statementHash: '0x' + crypto.createHash('sha256').update(JSON.stringify(st), 'utf8').digest('hex') }));
|
||||||
|
let j = null, ultim = '';
|
||||||
|
try { const r = spawnSync(process.execPath, [VERIFY_PROOF, f, '--json', '--offline'], { encoding: 'utf8', timeout: 120000 }); try { j = JSON.parse(r.stdout); } catch { j = null; } ultim = String(r.stderr || r.stdout || (r.error && r.error.message) || '').split(/\r?\n/).find((l) => /Error/.test(l)) || ''; }
|
||||||
|
finally { fs.rmSync(f, { force: true }); }
|
||||||
|
if (!j || j.verdict !== 'VALID') nem('the reference verifier at ' + VERIFY_PROOF + ' does not start or does not judge an envelope offline (' + (j ? 'verdict ' + j.verdict : (ultim.trim().slice(0, 160) || 'no JSON')) + '); give ' + CERE_VP);
|
||||||
|
}
|
||||||
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 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 provider = new ethers.JsonRpcProvider(RPC, undefined, { staticNetwork: false });
|
||||||
const chainId = Number((await provider.getNetwork()).chainId); if (chainId !== 28001) nem(`the RPC serves chain ${chainId}; only testnet 28001`);
|
const chainId = Number((await provider.getNetwork()).chainId); if (chainId !== 28001) nem(`the RPC serves chain ${chainId}; only testnet 28001`);
|
||||||
|
|||||||
65
payments/proba-plata-fara-verificator.mjs
Normal file
65
payments/proba-plata-fara-verificator.mjs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
// proba-plata-fara-verificator.mjs (2026-10-27, R9-S10): the testnet flow (proba-plata-28001.mjs) refuses BEFORE it reads or sends anything
|
||||||
|
// when the AIP-23 reference verifier it needs is not usable. No key is used and nothing is sent: the flow is pointed at a local JSON-RPC stub
|
||||||
|
// that counts every request (127.0.0.1), with a key file that exists and holds no key. Until this date the flow checked only the key and
|
||||||
|
// ethers, sent its payments and came out red afterwards, on the verifier's rows; a public clone does not hold the verifier at the default path.
|
||||||
|
// F1 no verifier at the path given -> exit 2, NEMASURAT 'no reference verifier', 0 requests to the chain
|
||||||
|
// F2 the verifier alone, without the files it imports -> exit 2, NEMASURAT 'has no verify-anchor.mjs beside it', 0 requests
|
||||||
|
// F3 the verifier with its files, but it does not start -> exit 2, NEMASURAT 'does not start', 0 requests
|
||||||
|
// F4 (positive control of this harness) a verifier that starts and judges offline -> the flow goes on to the chain: at least one
|
||||||
|
// request reaches the stub (which answers chain 2800, so the flow stops there: 'only testnet 28001'), and no transaction is sent
|
||||||
|
// F1, F2 and F3 are red on the form before 2026-10-27: AERE_PROBA_PLATA_CLI=<a copy of the old flow beside its modules> runs this on it.
|
||||||
|
// node proba-plata-fara-verificator.mjs -> 0 every row as named, 1 otherwise, 2 the harness itself did not run
|
||||||
|
import fs from 'node:fs';
|
||||||
|
import os from 'node:os';
|
||||||
|
import path from 'node:path';
|
||||||
|
import http from 'node:http';
|
||||||
|
import { spawn } from 'node:child_process';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
|
const AICI = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
const CLI = process.env.AERE_PROBA_PLATA_CLI ? path.resolve(process.env.AERE_PROBA_PLATA_CLI) : path.join(AICI, 'proba-plata-28001.mjs');
|
||||||
|
const ETHERS = process.env.AERE_ETHERS_DIR || path.resolve(AICI, '..', '..', 'contracts');
|
||||||
|
const D = fs.mkdtempSync(path.join(os.tmpdir(), 'aere-plata-fara-vp-'));
|
||||||
|
const KF = path.join(D, 'cheie.key'); fs.writeFileSync(KF, 'no key here\n');
|
||||||
|
// verifiers: alone (F2), beside its files but not starting (F3), beside its files and judging offline (F4)
|
||||||
|
const singur = path.join(D, 'singur'), mort = path.join(D, 'mort'), bun = path.join(D, 'bun');
|
||||||
|
for (const d of [singur, mort, bun]) fs.mkdirSync(d);
|
||||||
|
fs.writeFileSync(path.join(singur, 'verify-proof.mjs'), "console.log(JSON.stringify({ verdict: 'VALID', levels: [] }));\n");
|
||||||
|
for (const d of [mort, bun]) for (const x of ['verify-anchor.mjs', 'mpt.mjs', 'falcon512.mjs', 'registry-v3.mjs']) fs.writeFileSync(path.join(d, x), '// beside it\n');
|
||||||
|
fs.writeFileSync(path.join(mort, 'verify-proof.mjs'), "import '@noble/pachet-care-nu-exista-r9';\n");
|
||||||
|
fs.writeFileSync(path.join(bun, 'verify-proof.mjs'), "if (!process.argv.includes('--offline')) process.exitCode = 2;\nconsole.log(JSON.stringify({ verdict: process.argv.includes('--offline') ? 'VALID' : 'PARTIAL', levels: [] }));\n");
|
||||||
|
let cereri = [];
|
||||||
|
const srv = http.createServer((q, s) => { let b = ''; q.on('data', (d) => { b += d; }); q.on('end', () => {
|
||||||
|
let j = {}; try { j = JSON.parse(b); } catch { /* */ }
|
||||||
|
for (const x of (Array.isArray(j) ? j : [j])) cereri.push(x.method);
|
||||||
|
const raspunde = (x) => ({ jsonrpc: '2.0', id: x.id ?? 1, result: x.method === 'eth_chainId' ? '0xaf0' : x.method === 'net_version' ? '2800' : null });
|
||||||
|
s.writeHead(200, { 'content-type': 'application/json' }); s.end(JSON.stringify(Array.isArray(j) ? j.map(raspunde) : raspunde(j)));
|
||||||
|
}); });
|
||||||
|
await new Promise((ok) => srv.listen(0, '127.0.0.1', ok));
|
||||||
|
const RPC = `http://127.0.0.1:${srv.address().port}`;
|
||||||
|
const ruleaza = (vp) => new Promise((ok) => {
|
||||||
|
cereri = [];
|
||||||
|
const p = spawn(process.execPath, [CLI], { cwd: AICI, env: { ...process.env, AERE_TESTNET_KEY_FILE: KF, AERE_ETHERS_DIR: ETHERS, AERE_TESTNET_RPC: RPC, AERE_VERIFY_PROOF: vp }, stdio: ['ignore', 'pipe', 'pipe'] });
|
||||||
|
let out = ''; p.stdout.on('data', (d) => { out += d; }); p.stderr.on('data', (d) => { out += d; });
|
||||||
|
const t = setTimeout(() => p.kill(), 120000);
|
||||||
|
p.on('close', (cod) => { clearTimeout(t); ok({ cod, out, cereri: [...cereri] }); });
|
||||||
|
});
|
||||||
|
let rele = 0, n = 0, stricat = null;
|
||||||
|
const cer = (nume, r, cod, re, nCereri) => {
|
||||||
|
n++; const trimise = r.cereri.filter((m) => /^eth_send/.test(m)).length;
|
||||||
|
const ok = r.cod === cod && re.test(r.out) && trimise === 0 && (nCereri === 0 ? r.cereri.length === 0 : r.cereri.length >= nCereri);
|
||||||
|
console.log(` [${ok ? 'VERDE ' : 'ROSU '}] ${nume}: exit ${r.cod}, ${r.cereri.length} request(s) to the chain (${trimise} sending), "${r.out.trim().split('\n').pop().slice(0, 150)}"`); if (!ok) rele++;
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
const r4 = await ruleaza(path.join(bun, 'verify-proof.mjs'));
|
||||||
|
if (!r4.cereri.length) stricat = 'the positive control F4 reached no request to the stub (the flow did not start, or did not read the chain): ' + r4.out.trim().split('\n').pop().slice(0, 200);
|
||||||
|
else {
|
||||||
|
cer('F1 no reference verifier at the path given: NEMASURAT before anything is read or sent', await ruleaza(path.join(D, 'lipsa', 'verify-proof.mjs')), 2, /NEMASURAT: no reference verifier at/, 0);
|
||||||
|
cer('F2 the verifier alone, without the files it imports: NEMASURAT before anything is read or sent', await ruleaza(path.join(singur, 'verify-proof.mjs')), 2, /NEMASURAT: the reference verifier at .* has no verify-anchor\.mjs beside it/, 0);
|
||||||
|
cer('F3 the verifier does not start (an import that does not resolve): NEMASURAT before anything is read or sent', await ruleaza(path.join(mort, 'verify-proof.mjs')), 2, /NEMASURAT: the reference verifier at .* does not start/, 0);
|
||||||
|
cer('F4 (positive control) a verifier that starts: the flow reads the chain (the stub answers 2800 and it stops there), nothing sent', r4, 2, /only testnet 28001/, 1);
|
||||||
|
}
|
||||||
|
} finally { await new Promise((ok) => srv.close(ok)); fs.rmSync(D, { recursive: true, force: true }); }
|
||||||
|
if (stricat) { console.log('STRICAT: ' + stricat); process.exitCode = 2; }
|
||||||
|
else { console.log(`\nthe testnet flow without a usable verifier (${path.basename(CLI)}): ${n - rele}/${n} as named`); process.exitCode = rele ? 1 : 0; }
|
||||||
@ -5,9 +5,13 @@
|
|||||||
// FAILED). Every row asks for the exit code AND the verdict word of the last line, and the verdicts are those of POST /v1/payments/verify
|
// FAILED). Every row asks for the exit code AND the verdict word of the last line, and the verdicts are those of POST /v1/payments/verify
|
||||||
// (cloud-gateway/plati-api.mjs): VALID only with the notarization's finality PASSED and the transaction compared on chain, PARTIAL when
|
// (cloud-gateway/plati-api.mjs): VALID only with the notarization's finality PASSED and the transaction compared on chain, PARTIAL when
|
||||||
// something was not measured, INVALID with a reason. Until 2026-10-27 the reader wrote VALID and exited 0 without --verify-proof, without
|
// something was not measured, INVALID with a reason. Until 2026-10-27 the reader wrote VALID and exited 0 without --verify-proof, without
|
||||||
// --rpc, and when the reference verifier answered PARTIAL / PENDING; it called a file without policy and inputs INVALID; it called an
|
// --rpc, and when the notarization was PENDING or UNMEASURED; it called a file without policy and inputs INVALID; it called an
|
||||||
// unknown transaction UNMEASURED where the route says INVALID; and it compared the transaction on an RPC of another chain. The rows
|
// unknown transaction UNMEASURED where the route says INVALID; and it compared the transaction on an RPC of another chain. An envelope
|
||||||
// marked (NEW) are red on that form: AERE_VERIFICA_CHITANTA_CLI=<a copy of the old reader beside its modules> runs the probe on it.
|
// the reference verifier calls PARTIAL on ANOTHER level (the witness, a signature scheme it cannot check) with finality PASSED stays
|
||||||
|
// VALID, as on the route (V13; corrected 2026-10-27: this note said the verifier answering PARTIAL was the case). The rows marked (NEW)
|
||||||
|
// are red on that form: AERE_VERIFICA_CHITANTA_CLI=<a copy of the old reader beside its modules> runs the probe on it. The rows marked
|
||||||
|
// (NEW2) are red on the form of the first correction of 2026-10-27 (an RPC of another chain still handed on to the verifier, a verifier
|
||||||
|
// that did not start called 'not run', a file that is not a receipt ending in an exception).
|
||||||
// node proba-verifica-chitanta.mjs -> 0 every row as named, 1 otherwise
|
// node proba-verifica-chitanta.mjs -> 0 every row as named, 1 otherwise
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import os from 'node:os';
|
import os from 'node:os';
|
||||||
@ -35,14 +39,40 @@ const fals = JSON.parse(JSON.stringify({ instruction, envelope: plic, policy: PO
|
|||||||
const FALS = scrie('chitanta-falsa.json', fals);
|
const FALS = scrie('chitanta-falsa.json', fals);
|
||||||
// the reference verifier, stubbed: the state comes from the environment the reader inherits
|
// the reference verifier, stubbed: the state comes from the environment the reader inherits
|
||||||
const VP = path.join(D, 'verify-proof-stub.mjs');
|
const VP = path.join(D, 'verify-proof-stub.mjs');
|
||||||
fs.writeFileSync(VP, "const s = process.env.AERE_STUB_FIN || 'PASSED';\n"
|
// 2026-10-27 (R9-S1): the stub answers as verify-proof.mjs answers, not as the reader assumes: with --offline the finality is UNMEASURED
|
||||||
+ "if (s === 'NOJSON') { console.log('not json'); process.exit(2); }\n"
|
// ('offline: ...'); with --rpc it asks that endpoint eth_chainId, and on ANOTHER chain than --chain it answers what the published
|
||||||
|
// verifier answers there: FAILED (the form served until 2026-10-27: 'the chain's most recent anchor did not verify: FAIL: ... not anchoring
|
||||||
|
// on a regular grid', measured from a fresh clone) or, with AERE_STUB_ALTLANT=UNMEASURED, the corrected form ('serves chain X, not
|
||||||
|
// chain Y'). Its argv is appended to AERE_STUB_ARGS, so a row can ask what the reader handed it.
|
||||||
|
fs.writeFileSync(VP, "import fs from 'node:fs';\nconst argv = process.argv.slice(2);\n"
|
||||||
|
+ "if (process.env.AERE_STUB_ARGS) fs.appendFileSync(process.env.AERE_STUB_ARGS, JSON.stringify(argv) + '\\n');\n"
|
||||||
|
+ "const s0 = process.env.AERE_STUB_FIN || 'PASSED';\n"
|
||||||
|
+ "if (s0 === 'NOJSON') { console.log('not json'); process.exit(2); }\n"
|
||||||
+ "const other = process.env.AERE_STUB_ALT || null;\n"
|
+ "const other = process.env.AERE_STUB_ALT || null;\n"
|
||||||
+ "const levels = [{ level: 'integrity', state: 'PASSED', detail: 'stub' }, { level: 'finality', state: s, detail: 'stub ' + s }];\n"
|
+ "const ir = argv.indexOf('--rpc'), ic = argv.indexOf('--chain'); const rpc = ir >= 0 ? argv[ir + 1] : null, chain = ic >= 0 ? Number(argv[ic + 1]) : null;\n"
|
||||||
|
+ "let s = s0, detail = 'stub ' + s0;\n"
|
||||||
|
+ "if (argv.includes('--offline')) { s = 'UNMEASURED'; detail = 'offline: the notarization this envelope claims was not checked'; }\n"
|
||||||
|
+ "else if (rpc && chain) {\n"
|
||||||
|
+ " const j = await fetch(rpc, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'eth_chainId', params: [] }) }).then((x) => x.json()).catch(() => null);\n"
|
||||||
|
+ " const served = j && j.result ? Number(BigInt(j.result)) : null;\n"
|
||||||
|
+ " if (served !== null && served !== chain) {\n"
|
||||||
|
+ " if ((process.env.AERE_STUB_ALTLANT || 'FAILED') === 'FAILED') { s = 'FAILED'; detail = 'the chain' + String.fromCharCode(39) + 's most recent anchor did not verify: FAIL: the two most recent anchors (24630896, 24630864) are 32 apart, which is not a multiple of 32 on the grid from 64. The chain is not anchoring on a regular grid.'; }\n"
|
||||||
|
+ " else { s = 'UNMEASURED'; detail = 'the endpoint ' + rpc + ' serves chain ' + served + ', not chain ' + chain + ': nothing about chain ' + chain + ' was measured there'; }\n"
|
||||||
|
+ " }\n}\n"
|
||||||
|
+ "const levels = [{ level: 'integrity', state: 'PASSED', detail: 'stub' }, { level: 'finality', state: s, detail }];\n"
|
||||||
+ "if (other) levels.push({ level: 'witness', state: other, detail: 'stub' });\n"
|
+ "if (other) levels.push({ level: 'witness', state: other, detail: 'stub' });\n"
|
||||||
+ "const failed = levels.some((l) => l.state === 'FAILED'), unm = levels.some((l) => l.state === 'UNMEASURED' || l.state === 'PENDING');\n"
|
+ "const failed = levels.some((l) => l.state === 'FAILED'), unm = levels.some((l) => l.state === 'UNMEASURED' || l.state === 'PENDING');\n"
|
||||||
+ "console.log(JSON.stringify({ verdict: failed ? 'INVALID' : unm ? 'PARTIAL' : 'VALID', levels }));\n"
|
+ "console.log(JSON.stringify({ verdict: failed ? 'INVALID' : unm ? 'PARTIAL' : 'VALID', levels }));\n"
|
||||||
+ "process.exitCode = failed ? 1 : unm ? 2 : 0;\n");
|
+ "process.exitCode = failed ? 1 : unm ? 2 : 0;\n");
|
||||||
|
const VP_MORT = path.join(D, 'verify-proof-mort.mjs');
|
||||||
|
fs.writeFileSync(VP_MORT, "import '@noble/pachet-care-nu-exista-r9';\nconsole.log('{}');\n");
|
||||||
|
// files that are not receipts (R9-S9): {}, a list, a text that is not JSON; and a real receipt without its instruction.rate
|
||||||
|
const GOL = scrie('gol.json', {}), LISTA = scrie('lista.json', []);
|
||||||
|
const NU_JSON = path.join(D, 'nu-json.json'); fs.writeFileSync(NU_JSON, 'nu json {');
|
||||||
|
const faraRata = JSON.parse(JSON.stringify({ instruction, envelope: plic, policy: POLITICA, inputs: INPUTS })); delete faraRata.instruction.rate;
|
||||||
|
const FARA_RATA = scrie('fara-rata.json', faraRata);
|
||||||
|
const ARGS = path.join(D, 'argumente.txt');
|
||||||
|
const argsVazute = () => { try { return fs.readFileSync(ARGS, 'utf8'); } catch { return ''; } };
|
||||||
// the chain, stubbed: /bun answers this payment, /necunoscut does not know it, /alt-lant is another chain, /mut answers 500
|
// the chain, stubbed: /bun answers this payment, /necunoscut does not know it, /alt-lant is another chain, /mut answers 500
|
||||||
const txBun = { hash: TX, from: PAYER, to: PAYEE, value: '0x58d15e176280000', blockHash: BLOC }, rcBun = { status: '0x1', blockHash: BLOC };
|
const txBun = { hash: TX, from: PAYER, to: PAYEE, value: '0x58d15e176280000', blockHash: BLOC }, rcBun = { status: '0x1', blockHash: BLOC };
|
||||||
const srv = http.createServer((q, s) => {
|
const srv = http.createServer((q, s) => {
|
||||||
@ -63,14 +93,17 @@ const ruleaza = (args, env = {}) => new Promise((ok) => {
|
|||||||
const t = setTimeout(() => p.kill(), 120000);
|
const t = setTimeout(() => p.kill(), 120000);
|
||||||
p.on('close', (cod) => { clearTimeout(t); ok({ cod, out, ultim: out.trim().split('\n').pop() || '' }); });
|
p.on('close', (cod) => { clearTimeout(t); ok({ cod, out, ultim: out.trim().split('\n').pop() || '' }); });
|
||||||
});
|
});
|
||||||
let rele = 0, n = 0;
|
let rele = 0, n = 0, stricat = null;
|
||||||
const cer = (nume, r, cod, cuvant, alt) => {
|
const cer = (nume, r, cod, cuvant, alt) => {
|
||||||
n++; const ok = r.cod === cod && r.ultim.startsWith(cuvant) && (!alt || alt.test(r.out));
|
n++; const ok = r.cod === cod && r.ultim.startsWith(cuvant) && (!alt || alt.test(r.out));
|
||||||
console.log(` [${ok ? 'VERDE ' : 'ROSU '}] ${nume}: exit ${r.cod}, "${r.ultim.slice(0, 150)}"`); if (!ok) rele++;
|
console.log(` [${ok ? 'VERDE ' : 'ROSU '}] ${nume}: exit ${r.cod}, "${r.ultim.slice(0, 150)}"`); if (!ok) rele++;
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
cer('V1 the full receipt file, the chain agreeing, finality PASSED: VALID, notarized (the route says VALID)',
|
const r1 = await ruleaza([FISIER, '--rpc', RPC('bun'), '--verify-proof', VP], { AERE_STUB_FIN: 'PASSED' });
|
||||||
await ruleaza([FISIER, '--rpc', RPC('bun'), '--verify-proof', VP], { AERE_STUB_FIN: 'PASSED' }), 0, 'VALID, notarized');
|
// 2026-10-27 (R9-S1): a reader under test that does not START (a copy put where its imports do not resolve: node then ends with its
|
||||||
|
// "Node.js vNN" line) measures nothing, and its red rows would look like a verdict about it; the run stops STRICAT (exit 2)
|
||||||
|
if (/^Node\.js v\d/.test(r1.ultim)) { stricat = 'the reader under test does not start (' + (r1.out.split('\n').find((l) => /Error/.test(l)) || r1.ultim).trim().slice(0, 200) + '); a copy of an old reader goes beside its modules'; throw new Error('stricat'); }
|
||||||
|
cer('V1 the full receipt file, the chain agreeing, finality PASSED: VALID, notarized (the route says VALID)', r1, 0, 'VALID, notarized');
|
||||||
cer('V2 (NEW) without --verify-proof: PARTIAL, exit 2, the envelope not judged (the route never answers VALID without the verifier)',
|
cer('V2 (NEW) without --verify-proof: PARTIAL, exit 2, the envelope not judged (the route never answers VALID without the verifier)',
|
||||||
await ruleaza([FISIER, '--rpc', RPC('bun')]), 2, 'PARTIAL', /no --verify-proof/);
|
await ruleaza([FISIER, '--rpc', RPC('bun')]), 2, 'PARTIAL', /no --verify-proof/);
|
||||||
cer('V3 (NEW) without --rpc and without --verify-proof: PARTIAL, exit 2, the chain not consulted',
|
cer('V3 (NEW) without --rpc and without --verify-proof: PARTIAL, exit 2, the chain not consulted',
|
||||||
@ -85,8 +118,18 @@ try {
|
|||||||
await ruleaza([FISIER, '--rpc', RPC('mut'), '--verify-proof', VP], { AERE_STUB_FIN: 'PASSED' }), 2, 'PARTIAL', /did not answer/);
|
await ruleaza([FISIER, '--rpc', RPC('mut'), '--verify-proof', VP], { AERE_STUB_FIN: 'PASSED' }), 2, 'PARTIAL', /did not answer/);
|
||||||
cer('V8 (NEW) the chain answers and does not know the transaction: INVALID, exit 1, said (as the route)',
|
cer('V8 (NEW) the chain answers and does not know the transaction: INVALID, exit 1, said (as the route)',
|
||||||
await ruleaza([FISIER, '--rpc', RPC('necunoscut'), '--verify-proof', VP], { AERE_STUB_FIN: 'PASSED' }), 1, 'INVALID', /does not know/);
|
await ruleaza([FISIER, '--rpc', RPC('necunoscut'), '--verify-proof', VP], { AERE_STUB_FIN: 'PASSED' }), 1, 'INVALID', /does not know/);
|
||||||
cer('V9 (NEW) an RPC of another chain (2800 for a 28001 receipt): PARTIAL, exit 2, the transaction not compared there',
|
fs.rmSync(ARGS, { force: true });
|
||||||
await ruleaza([FISIER, '--rpc', RPC('alt-lant'), '--verify-proof', VP], { AERE_STUB_FIN: 'PASSED' }), 2, 'PARTIAL', /serves chain 2800/);
|
const r9 = await ruleaza([FISIER, '--rpc', RPC('alt-lant'), '--verify-proof', VP], { AERE_STUB_FIN: 'PASSED', AERE_STUB_ARGS: ARGS });
|
||||||
|
// (NEW2) the verifier answers as the published one did until 2026-10-27 for an RPC of another chain (FAILED): the reader must not
|
||||||
|
// hand it that RPC at all; a reader that does comes out INVALID on an authentic receipt
|
||||||
|
r9.ultim = argsVazute().includes('/alt-lant') ? 'HANDED THE RPC OF ANOTHER CHAIN TO THE VERIFIER: ' + r9.ultim : r9.ultim;
|
||||||
|
cer('V9 (NEW, NEW2) an RPC of another chain (2800 for a 28001 receipt), the verifier as published until 2026-10-27: PARTIAL, exit 2, the transaction and the notarization not compared there, the RPC not handed to the verifier (judged offline)',
|
||||||
|
r9, 2, 'PARTIAL', /^(?=[\s\S]*serves chain 2800)(?=[\s\S]*reference verifier, offline)/);
|
||||||
|
fs.rmSync(ARGS, { force: true });
|
||||||
|
const r9b = await ruleaza([FISIER, '--rpc', RPC('alt-lant'), '--verify-proof', VP], { AERE_STUB_FIN: 'PASSED', AERE_STUB_ALTLANT: 'UNMEASURED', AERE_STUB_ARGS: ARGS });
|
||||||
|
r9b.ultim = argsVazute().includes('/alt-lant') ? 'HANDED THE RPC OF ANOTHER CHAIN TO THE VERIFIER: ' + r9b.ultim : r9b.ultim;
|
||||||
|
cer('V9b (NEW2) the same with the corrected verifier (UNMEASURED for another chain): PARTIAL, exit 2, the RPC not handed on',
|
||||||
|
r9b, 2, 'PARTIAL', /serves chain 2800/);
|
||||||
cer('V10 (NEW) the `receipt` of the route alone ({instruction, envelope}, no policy, no inputs): PARTIAL, exit 2, naming what is missing and the receiptFile that carries it (not INVALID on a good receipt)',
|
cer('V10 (NEW) the `receipt` of the route alone ({instruction, envelope}, no policy, no inputs): PARTIAL, exit 2, naming what is missing and the receiptFile that carries it (not INVALID on a good receipt)',
|
||||||
await ruleaza([FARA_POLITICA, '--rpc', RPC('bun'), '--verify-proof', VP], { AERE_STUB_FIN: 'PASSED' }), 2, 'PARTIAL', /no policy and inputs.*receiptFile/);
|
await ruleaza([FARA_POLITICA, '--rpc', RPC('bun'), '--verify-proof', VP], { AERE_STUB_FIN: 'PASSED' }), 2, 'PARTIAL', /no policy and inputs.*receiptFile/);
|
||||||
cer('V11 a forged receipt (amount changed after the envelope), everything else agreeing: INVALID, exit 1',
|
cer('V11 a forged receipt (amount changed after the envelope), everything else agreeing: INVALID, exit 1',
|
||||||
@ -97,6 +140,19 @@ try {
|
|||||||
await ruleaza([FISIER, '--rpc', RPC('bun'), '--verify-proof', VP], { AERE_STUB_FIN: 'PASSED', AERE_STUB_ALT: 'UNMEASURED' }), 0, 'VALID, notarized');
|
await ruleaza([FISIER, '--rpc', RPC('bun'), '--verify-proof', VP], { AERE_STUB_FIN: 'PASSED', AERE_STUB_ALT: 'UNMEASURED' }), 0, 'VALID, notarized');
|
||||||
cer('V14 (NEW) the verifier prints no JSON: PARTIAL, exit 2, said',
|
cer('V14 (NEW) the verifier prints no JSON: PARTIAL, exit 2, said',
|
||||||
await ruleaza([FISIER, '--rpc', RPC('bun'), '--verify-proof', VP], { AERE_STUB_FIN: 'NOJSON' }), 2, 'PARTIAL', /gave no JSON/);
|
await ruleaza([FISIER, '--rpc', RPC('bun'), '--verify-proof', VP], { AERE_STUB_FIN: 'NOJSON' }), 2, 'PARTIAL', /gave no JSON/);
|
||||||
} finally { await new Promise((ok) => srv.close(ok)); fs.rmSync(D, { recursive: true, force: true }); }
|
cer('V15 (NEW2) the verifier does not start (an import that does not resolve, as verify-proof.mjs alone without npm install): PARTIAL, exit 2, it RAN and gave no JSON, with its error line and the files it needs named (not "not run")',
|
||||||
|
await ruleaza([FISIER, '--rpc', RPC('bun'), '--verify-proof', VP_MORT]), 2, 'PARTIAL',
|
||||||
|
/envelope \(reference verifier\): ran, gave no JSON \(exit 1\): .*Cannot find package[\s\S]*verify-anchor\.mjs, mpt\.mjs, falcon512\.mjs, registry-v3\.mjs and package\.json beside it/);
|
||||||
|
cer('V16 (NEW2) a file holding {}: INVALID, exit 1, the file is not a receipt (not an exception with the same exit code)',
|
||||||
|
await ruleaza([GOL, '--rpc', RPC('bun'), '--verify-proof', VP]), 1, 'INVALID: the file is not a receipt: it has no instruction object');
|
||||||
|
cer('V17 (NEW2) a file that is not JSON: INVALID, exit 1, said',
|
||||||
|
await ruleaza([NU_JSON, '--rpc', RPC('bun'), '--verify-proof', VP]), 1, 'INVALID: the file is not a receipt: it is not JSON');
|
||||||
|
cer('V18 (NEW2) a file holding a list: INVALID, exit 1, said',
|
||||||
|
await ruleaza([LISTA]), 1, 'INVALID: the file is not a receipt: it holds a list');
|
||||||
|
cer('V19 (NEW2) a receipt without instruction.rate: INVALID, exit 1, for the named rule (the amount or the rate), not an exception',
|
||||||
|
await ruleaza([FARA_RATA, '--rpc', RPC('bun'), '--verify-proof', VP], { AERE_STUB_FIN: 'PASSED' }), 1, 'INVALID', /rules: INVALID: .*rate/);
|
||||||
|
} catch (e) { if (!stricat) throw e; } finally { await new Promise((ok) => srv.close(ok)); fs.rmSync(D, { recursive: true, force: true }); }
|
||||||
|
if (stricat) { console.log('STRICAT: ' + stricat); process.exitCode = 2; } else {
|
||||||
console.log(`\nthe public reader of a receipt (${path.basename(CLI)}): ${n - rele}/${n} as named${process.env.AERE_VERIFICA_CHITANTA_CLI ? ' (reader under test: ' + CLI + ')' : ''}`);
|
console.log(`\nthe public reader of a receipt (${path.basename(CLI)}): ${n - rele}/${n} as named${process.env.AERE_VERIFICA_CHITANTA_CLI ? ' (reader under test: ' + CLI + ')' : ''}`);
|
||||||
process.exitCode = rele ? 1 : 0;
|
process.exitCode = rele ? 1 : 0;
|
||||||
|
}
|
||||||
|
|||||||
@ -3,14 +3,22 @@
|
|||||||
// instruction, the settlement envelope, the policy and the inputs the issuer judged (the `receiptFile` that POST /v1/payments/receipt
|
// instruction, the settlement envelope, the policy and the inputs the issuer judged (the `receiptFile` that POST /v1/payments/receipt
|
||||||
// returns is exactly this file); with --rpc the transaction and its receipt are read from the chain by plain JSON-RPC and compared;
|
// returns is exactly this file); with --rpc the transaction and its receipt are read from the chain by plain JSON-RPC and compared;
|
||||||
// with --verify-proof the AIP-23 reference verifier judges the envelope and, with --rpc, its notarization under a post-quantum
|
// with --verify-proof the AIP-23 reference verifier judges the envelope and, with --rpc, its notarization under a post-quantum
|
||||||
// certified anchor (finality).
|
// certified anchor (finality). The reference verifier is verify-proof.mjs as served at aere.network/tools, with verify-anchor.mjs,
|
||||||
|
// mpt.mjs, falcon512.mjs, registry-v3.mjs and package.json from the same place beside it and `npm install` done there; this reader
|
||||||
|
// itself needs only Node.js.
|
||||||
// node verifica-chitanta.mjs <receipt.json> [--rpc URL] [--verify-proof <verify-proof.mjs>] [--chain 28001]
|
// node verifica-chitanta.mjs <receipt.json> [--rpc URL] [--verify-proof <verify-proof.mjs>] [--chain 28001]
|
||||||
// Exit 0 VALID: every rule holds, the transaction on the chain is this payment, and the reference verifier finds the envelope's
|
// Exit 0 VALID: every rule holds, the transaction on the chain is this payment, and the reference verifier finds the envelope's
|
||||||
// notarization under a post-quantum certified anchor (finality PASSED). Exit 1 INVALID, with the reasons. Exit 2 PARTIAL: something
|
// notarization under a post-quantum certified anchor (finality PASSED). Exit 1 INVALID, with the reasons; a file that is not a receipt
|
||||||
// was not measured (no --rpc, no chain answer, an RPC of another chain, no --verify-proof or no verifier at that path, the policy or the
|
// (not JSON, or no instruction and settlement envelope objects) is INVALID by name. Exit 2 PARTIAL: something was not measured (no
|
||||||
// inputs missing from the file, or the notarization not under an anchor yet: finality PENDING or UNMEASURED). These are the verdicts of
|
// --rpc, no chain answer, an RPC of another chain, no --verify-proof or no verifier at that path, a verifier that ran and gave no
|
||||||
// POST /v1/payments/verify on the same rules. (Corrected 2026-10-27: until this date the tool wrote VALID and exited 0 without
|
// verdict, the policy or the inputs missing from the file, or the notarization not under an anchor yet: finality PENDING or
|
||||||
// --verify-proof, and when the reference verifier answered PARTIAL or PENDING; a file without policy or inputs was INVALID.)
|
// UNMEASURED), or the reader itself stopped before a verdict. These are the verdicts of POST /v1/payments/verify on the same rules.
|
||||||
|
// (Corrected 2026-10-27: until this date the tool wrote VALID and exited 0 without --verify-proof, and when the notarization was PENDING
|
||||||
|
// or UNMEASURED; a file without policy or inputs was INVALID. An envelope that the reference verifier calls PARTIAL on ANOTHER level,
|
||||||
|
// the Bitcoin witness or a signature scheme it cannot check, with the finality of its notarization PASSED, is VALID here as on the
|
||||||
|
// route, which decides on finality. Corrected again the same day: an RPC of another chain was handed on to the reference verifier,
|
||||||
|
// which answered FAILED, so an authentic receipt came out INVALID; it is now judged offline and the verdict is PARTIAL. A file that
|
||||||
|
// was not a receipt ended in an exception with the exit code of INVALID; it is now refused by name.)
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import os from 'node:os';
|
import os from 'node:os';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
@ -18,52 +26,91 @@ import { spawnSync } from 'node:child_process';
|
|||||||
import { verificaChitanta } from './chitanta.mjs';
|
import { verificaChitanta } from './chitanta.mjs';
|
||||||
|
|
||||||
const a = process.argv.slice(2); const opt = (k, d = null) => { const i = a.indexOf(k); return i >= 0 && i + 1 < a.length ? a[i + 1] : d; };
|
const a = process.argv.slice(2); const opt = (k, d = null) => { const i = a.indexOf(k); return i >= 0 && i + 1 < a.length ? a[i + 1] : d; };
|
||||||
const f = a[0]; if (!f || !fs.existsSync(f)) { console.log('usage: verifica-chitanta.mjs <receipt.json> [--rpc URL] [--verify-proof path] [--chain id]'); process.exit(2); }
|
const obj = (x) => !!x && typeof x === 'object' && !Array.isArray(x);
|
||||||
const r = JSON.parse(fs.readFileSync(f, 'utf8'));
|
const ce = (x) => (x === null ? 'null' : Array.isArray(x) ? 'a list' : typeof x === 'object' ? 'an object' : 'a ' + typeof x);
|
||||||
const rpc = opt('--rpc'); const vp = opt('--verify-proof', process.env.AERE_VERIFY_PROOF || null); const chain = opt('--chain', r.instruction && r.instruction.chainId);
|
// what a verifier that did not answer printed: its error line, not the "Node.js vNN" line or a stack frame; long hex strings are cut
|
||||||
const nem = []; // what was NOT measured: any entry makes the verdict PARTIAL (exit 2), never VALID
|
const randUtil = (p) => {
|
||||||
let tx = null, receipt = null, lantNuStie = false;
|
const L = String((p.stderr || '') + '\n' + (p.stdout || '')).replace(/\r/g, '').split('\n').map((x) => x.trim()).filter(Boolean);
|
||||||
if (rpc) {
|
const r = L.find((x) => /(^|\W)(Error|\w+Error)\b[\s\[:]/.test(x)) || L.filter((x) => !/^(Node\.js v|at |[{}]|code:)/.test(x)).pop()
|
||||||
|
|| (p.error ? String(p.error.message) : 'nothing on its output');
|
||||||
|
return r.replace(/0x[0-9a-fA-F]{40,}/g, '0x..').slice(0, 160);
|
||||||
|
};
|
||||||
|
|
||||||
|
async function citeste(f) {
|
||||||
|
// the file: refused BY NAME when it is not a receipt (2026-10-27, R9-S9: until then {} or a text that is not JSON ended in an exception
|
||||||
|
// with exit 1, the code of INVALID, so the exit code did not tell a forged receipt from a reader that fell)
|
||||||
|
const nuE = (m) => { console.log('INVALID: the file is not a receipt: ' + m); process.exitCode = 1; };
|
||||||
|
let r;
|
||||||
|
try { r = JSON.parse(fs.readFileSync(f, 'utf8').replace(/^/, '')); }
|
||||||
|
catch (e) { return nuE('it is not JSON (' + String(e && e.message).split('\n')[0].slice(0, 100) + ')'); }
|
||||||
|
if (!obj(r)) return nuE('it holds ' + ce(r) + ', not an object {instruction, envelope}');
|
||||||
|
if (!obj(r.instruction) || !obj(r.envelope)) return nuE('it has no instruction object and settlement envelope object');
|
||||||
|
const ins = r.instruction;
|
||||||
|
const rpc = opt('--rpc'); const vp = opt('--verify-proof', process.env.AERE_VERIFY_PROOF || null); const chain = opt('--chain', ins.chainId);
|
||||||
|
const nem = []; // what was NOT measured: any entry makes the verdict PARTIAL (exit 2), never VALID
|
||||||
|
let tx = null, receipt = null, lantNuStie = false, altLant = null;
|
||||||
|
if (rpc) {
|
||||||
const apel = async (method, params) => { const x = await fetch(rpc, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params }), signal: AbortSignal.timeout(20000) }); const j = await x.json(); if (j.error) throw new Error(method + ': ' + j.error.message); return j.result; };
|
const apel = async (method, params) => { const x = await fetch(rpc, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params }), signal: AbortSignal.timeout(20000) }); const j = await x.json(); if (j.error) throw new Error(method + ': ' + j.error.message); return j.result; };
|
||||||
try {
|
try {
|
||||||
// an RPC of another chain does not know the transaction, and that is not a verdict about the receipt
|
// an RPC of another chain does not know the transaction, and that is not a verdict about the receipt; nor is anything it would
|
||||||
|
// tell the reference verifier about the notarization (2026-10-27, R9-S1: it is not handed on, the envelope is judged offline)
|
||||||
const cid = Number(BigInt(await apel('eth_chainId', [])));
|
const cid = Number(BigInt(await apel('eth_chainId', [])));
|
||||||
if (r.instruction && r.instruction.chainId !== undefined && cid !== Number(r.instruction.chainId)) nem.push(`the RPC serves chain ${cid}, the receipt names chain ${r.instruction.chainId}: the transaction was not compared`);
|
if (ins.chainId !== undefined && cid !== Number(ins.chainId)) { altLant = cid; nem.push(`the RPC serves chain ${cid}, the receipt names chain ${ins.chainId}: the transaction and the notarization were not compared there`); }
|
||||||
else {
|
else {
|
||||||
tx = await apel('eth_getTransactionByHash', [r.instruction.txHash]); receipt = await apel('eth_getTransactionReceipt', [r.instruction.txHash]);
|
tx = await apel('eth_getTransactionByHash', [ins.txHash]); receipt = await apel('eth_getTransactionReceipt', [ins.txHash]);
|
||||||
if (!tx || !receipt) { lantNuStie = true; tx = null; receipt = null; }
|
if (!tx || !receipt) { lantNuStie = true; tx = null; receipt = null; }
|
||||||
}
|
}
|
||||||
} catch (e) { tx = null; receipt = null; nem.push('the chain did not answer: ' + e.message); }
|
} catch (e) { tx = null; receipt = null; nem.push('the chain did not answer: ' + e.message); }
|
||||||
} else nem.push('the chain was not consulted (no --rpc): the transaction was not compared');
|
} else nem.push('the chain was not consulted (no --rpc): the transaction was not compared');
|
||||||
// the policy and the inputs are what reproduces the risk verdict; a file without them cannot be judged on those rules (PARTIAL, not
|
// the policy and the inputs are what reproduces the risk verdict; a file without them cannot be judged on those rules (PARTIAL, not
|
||||||
// INVALID): the module refuses them by name (politica, risc), and those two refusals are set aside here, every other rule still counts
|
// INVALID): the module refuses them by name (politica, risc), and those two refusals are set aside here, every other rule still counts
|
||||||
const lipsa = [!r.policy && 'policy', !r.inputs && 'inputs'].filter(Boolean);
|
const lipsa = [!r.policy && 'policy', !r.inputs && 'inputs'].filter(Boolean);
|
||||||
const absente = new Set(lipsa.length ? (r.policy ? ['risc'] : ['politica']) : []);
|
const absente = new Set(lipsa.length ? (r.policy ? ['risc'] : ['politica']) : []);
|
||||||
if (lipsa.length) nem.push(`the file carries no ${lipsa.join(' and ')}: the risk verdict was not reproduced (the receiptFile that /v1/payments/receipt returns carries both)`);
|
if (lipsa.length) nem.push(`the file carries no ${lipsa.join(' and ')}: the risk verdict was not reproduced (the receiptFile that /v1/payments/receipt returns carries both)`);
|
||||||
const v = verificaChitanta({ instruction: r.instruction, envelope: r.envelope }, { policy: r.policy, inputs: r.inputs, tx, receipt });
|
const v = verificaChitanta({ instruction: ins, envelope: r.envelope }, { policy: r.policy, inputs: r.inputs, tx, receipt });
|
||||||
const reguliRele = (v.reguli || []).filter((x) => !absente.has(x));
|
const reguliRele = (v.reguli || []).filter((x) => !absente.has(x));
|
||||||
let fin = null, verdictPlic = null;
|
let fin = null, verdictPlic = null, plicStare = 'not run';
|
||||||
if (!vp) nem.push('the envelope was not judged (no --verify-proof): its notarization under a post-quantum certified anchor was not checked');
|
if (!vp) nem.push('the envelope was not judged (no --verify-proof): its notarization under a post-quantum certified anchor was not checked');
|
||||||
else if (!fs.existsSync(vp)) nem.push('the reference verifier is not at ' + vp);
|
else if (!fs.existsSync(vp)) { plicStare = 'not run (no verifier at ' + vp + ')'; nem.push('the reference verifier is not at ' + vp); }
|
||||||
else {
|
else {
|
||||||
const t = path.join(os.tmpdir(), 'chitanta-' + process.pid + '.json'); fs.writeFileSync(t, JSON.stringify(r.envelope));
|
const t = path.join(os.tmpdir(), 'chitanta-' + process.pid + '.json'); fs.writeFileSync(t, JSON.stringify(r.envelope));
|
||||||
try { const p = spawnSync(process.execPath, [vp, t, '--json', ...(rpc ? ['--rpc', rpc, '--chain', String(chain)] : [])], { encoding: 'utf8', timeout: 300000 }); try { const j = JSON.parse(p.stdout); verdictPlic = j.verdict; fin = (j.levels || []).find((l) => l.level === 'finality') || null; } catch { nem.push('the reference verifier gave no JSON'); } } finally { fs.rmSync(t, { force: true }); }
|
const cu = altLant !== null ? ['--offline', '--chain', String(chain)] : rpc ? ['--rpc', rpc, '--chain', String(chain)] : [];
|
||||||
}
|
try {
|
||||||
const motiveRele = [...(reguliRele.length ? v.motive.filter((m) => !/^no (policy|inputs) given/.test(m)) : []),
|
const p = spawnSync(process.execPath, [vp, t, '--json', ...cu], { encoding: 'utf8', timeout: 300000 });
|
||||||
...(lantNuStie ? [`the chain does not know transaction ${r.instruction.txHash}`] : []),
|
let j = null; try { j = JSON.parse(p.stdout); } catch { j = null; }
|
||||||
|
if (j && typeof j.verdict === 'string') { verdictPlic = j.verdict; fin = (j.levels || []).find((l) => l.level === 'finality') || null; }
|
||||||
|
else {
|
||||||
|
// 2026-10-27 (R9-S3): it RAN and gave no verdict (until then this line said "not run"); its own error line is printed, and the
|
||||||
|
// usual cause named: the verifier alone, without the files it imports beside it or without npm install
|
||||||
|
plicStare = 'ran, gave no JSON (exit ' + p.status + '): ' + randUtil(p);
|
||||||
|
nem.push('the reference verifier ' + plicStare + '; it needs verify-anchor.mjs, mpt.mjs, falcon512.mjs, registry-v3.mjs and package.json beside it, with npm install done there');
|
||||||
|
}
|
||||||
|
} finally { fs.rmSync(t, { force: true }); }
|
||||||
|
}
|
||||||
|
const motiveRele = [...(reguliRele.length ? v.motive.filter((m) => !/^no (policy|inputs) given/.test(m)) : []),
|
||||||
|
...(lantNuStie ? [`the chain does not know transaction ${ins.txHash}`] : []),
|
||||||
...(verdictPlic === 'INVALID' ? ['the reference verifier finds the envelope INVALID'] : []),
|
...(verdictPlic === 'INVALID' ? ['the reference verifier finds the envelope INVALID'] : []),
|
||||||
...(fin && fin.state === 'FAILED' ? ['the notarization FAILED under the reference verifier: ' + String(fin.detail).slice(0, 140)] : [])];
|
...(fin && fin.state === 'FAILED' ? ['the notarization FAILED under the reference verifier: ' + String(fin.detail).slice(0, 140)] : [])];
|
||||||
const invalid = motiveRele.length > 0;
|
const invalid = motiveRele.length > 0;
|
||||||
// as POST /v1/payments/verify: VALID only with the finality of the notarization PASSED (and the chain compared); PENDING, UNMEASURED or
|
// as POST /v1/payments/verify: VALID only with the finality of the notarization PASSED (and the chain compared); PENDING, UNMEASURED or
|
||||||
// no finality level at all is PARTIAL
|
// no finality level at all is PARTIAL. On an RPC of another chain the envelope was judged offline, and the reason is already named.
|
||||||
if (verdictPlic && !invalid && !(fin && fin.state === 'PASSED')) nem.push(`the notarization is not under a post-quantum certified anchor yet (finality ${fin ? fin.state + ': ' + String(fin.detail).slice(0, 120) : 'absent'})`);
|
if (verdictPlic && !invalid && altLant === null && !(fin && fin.state === 'PASSED')) nem.push(`the notarization is not under a post-quantum certified anchor yet (finality ${fin ? fin.state + ': ' + String(fin.detail).slice(0, 120) : 'absent'})`);
|
||||||
const randuri = [`receipt: ${r.instruction.amountUsd} USD = ${r.instruction.amount} units of ${r.instruction.asset} at ${r.instruction.rate.usdPerUnit} USD/unit (${r.instruction.rate.source}), ${r.instruction.payer.slice(0, 10)}.. -> ${r.instruction.payee.slice(0, 10)}.., tx ${r.instruction.txHash.slice(0, 18)}.., risk ${r.instruction.risk.decision} under policy ${r.instruction.risk.policyHash.slice(0, 18)}..`,
|
const s = (x, n) => (typeof x === 'string' ? x.slice(0, n) : '?');
|
||||||
|
const rate = obj(ins.rate) ? ins.rate : {}, risk = obj(ins.risk) ? ins.risk : {};
|
||||||
|
const randuri = [`receipt: ${ins.amountUsd ?? '?'} USD = ${ins.amount ?? '?'} units of ${ins.asset ?? '?'} at ${rate.usdPerUnit ?? '?'} USD/unit (${rate.source ?? '?'}), ${s(ins.payer, 10)}.. -> ${s(ins.payee, 10)}.., tx ${s(ins.txHash, 18)}.., risk ${risk.decision ?? '?'} under policy ${s(risk.policyHash, 18)}..`,
|
||||||
reguliRele.length || !lipsa.length ? `rules: ${v.verdict}${v.motive.length ? ': ' + v.motive.join('; ') : ''}${v.chainChecked ? ' (transaction compared on chain)' : ' (chain not consulted)'}`
|
reguliRele.length || !lipsa.length ? `rules: ${v.verdict}${v.motive.length ? ': ' + v.motive.join('; ') : ''}${v.chainChecked ? ' (transaction compared on chain)' : ' (chain not consulted)'}`
|
||||||
: `rules: not all judged (no ${lipsa.join(' and ')} in the file); the others hold${v.chainChecked ? ' (transaction compared on chain)' : ' (chain not consulted)'}`,
|
: `rules: not all judged (no ${lipsa.join(' and ')} in the file); the others hold${v.chainChecked ? ' (transaction compared on chain)' : ' (chain not consulted)'}`,
|
||||||
verdictPlic ? `envelope (reference verifier): ${verdictPlic}; finality ${fin ? fin.state + ': ' + String(fin.detail).slice(0, 140) : 'absent'}` : 'envelope (reference verifier): not run'];
|
verdictPlic ? `envelope (reference verifier${altLant !== null ? ', offline: the RPC serves another chain' : ''}): ${verdictPlic}; finality ${fin ? fin.state + ': ' + String(fin.detail).slice(0, 140) : 'absent'}` : 'envelope (reference verifier): ' + plicStare];
|
||||||
console.log(randuri.join('\n'));
|
console.log(randuri.join('\n'));
|
||||||
const valid = !invalid && !nem.length && v.chainChecked && fin && fin.state === 'PASSED';
|
const valid = !invalid && !nem.length && v.chainChecked && fin && fin.state === 'PASSED';
|
||||||
process.exitCode = invalid ? 1 : valid ? 0 : 2;
|
process.exitCode = invalid ? 1 : valid ? 0 : 2;
|
||||||
if (invalid) console.log('INVALID: ' + motiveRele.join('; '));
|
if (invalid) console.log('INVALID: ' + motiveRele.join('; '));
|
||||||
else if (valid) console.log('VALID, notarized under a post-quantum certified anchor');
|
else if (valid) console.log('VALID, notarized under a post-quantum certified anchor');
|
||||||
else console.log('PARTIAL: ' + (nem.length ? nem.join('; ') : 'the transaction was not compared on chain'));
|
else console.log('PARTIAL: ' + (nem.length ? nem.join('; ') : 'the transaction was not compared on chain'));
|
||||||
|
}
|
||||||
|
|
||||||
|
const f = a[0];
|
||||||
|
if (!f || !fs.existsSync(f)) { console.log('usage: verifica-chitanta.mjs <receipt.json> [--rpc URL] [--verify-proof path] [--chain id]'); process.exitCode = 2; }
|
||||||
|
// a reader that falls is not a verdict: an exception it did not foresee ends the run PARTIAL (exit 2), named, never with the code of
|
||||||
|
// INVALID (2026-10-27, R9-S9)
|
||||||
|
else await citeste(f).catch((e) => { console.log('PARTIAL: the reader stopped before a verdict: ' + String(e && e.message || e).split('\n')[0].slice(0, 160)); process.exitCode = 2; });
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user