payments: the public reader of a receipt now exits 2 and says PARTIAL when it did not verify the notarization (no --verify-proof, the reference verifier answering PARTIAL or PENDING, a chain that does not answer), as POST /v1/payments/verify answers; the receiptFile of /v1/payments/receipt carries the policy and the inputs it needs; new probe proba-verifica-chitanta.mjs (14 rows, no chain); README rule names as the module returns them
This commit is contained in:
parent
fcece14a12
commit
687e5e54a4
@ -1,7 +1,7 @@
|
|||||||
# Citations in this repository that you cannot open
|
# Citations in this repository that you cannot open
|
||||||
|
|
||||||
This file is generated by the publication stager of this repository (scripts/legaturi-declaratie.cjs in the Aere working
|
This file is generated by the publication stager of this repository (scripts/legaturi-declaratie.cjs in the Aere working
|
||||||
tree) and enforced by legaturi.cjs. It is the complete list, measured on 2026-10-21, of every path cited in this
|
tree) and enforced by legaturi.cjs. It is the complete list, measured on 2026-10-27, of every path cited in this
|
||||||
repository that does not resolve to a published file.
|
repository that does not resolve to a published file.
|
||||||
|
|
||||||
A citation is a promise that a claim is checkable. Where the target is not published, or names a file the reader creates
|
A citation is a promise that a claim is checkable. Where the target is not published, or names a file the reader creates
|
||||||
|
|||||||
@ -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); 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); 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) |
|
||||||
| 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: 1253 (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 8, conformance 803, device-identity 11, device-operation 114, exposure-report 18, readiness 4).
|
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).
|
||||||
|
|||||||
@ -27,16 +27,27 @@ node proba-risc.mjs # 23 rows, no chain: the verdicts unde
|
|||||||
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> node proba-plata-28001.mjs # the flow on testnet 28001
|
||||||
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
|
||||||
|
# verifier, each exit code and verdict word as POST /v1/payments/verify gives them
|
||||||
```
|
```
|
||||||
|
|
||||||
Measured on 2026-10-02 on testnet 28001 (9/9): a person's payment of 0.4 tAERE at a declared 0.5 USD/tAERE = 0.20 USD allowed, paid
|
Measured on 2026-10-02 on testnet 28001 (9/9): a person's payment of 0.4 tAERE at a declared 0.5 USD/tAERE = 0.20 USD allowed, paid
|
||||||
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.
|
hash INVALID. The public reader (`verifica-chitanta.mjs`) needs only Node.js: the chain is read by plain JSON-RPC. 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
|
||||||
|
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
|
||||||
|
not measured (no `--rpc`, no chain answer, an RPC of another chain, no `--verify-proof`, the policy or the inputs missing, finality
|
||||||
|
`PENDING` or `UNMEASURED`). Corrected 2026-10-27: until this date it wrote `VALID` and exited 0 without `--verify-proof` and while
|
||||||
|
the notarization was pending, and it called a file without the policy and the inputs `INVALID`.
|
||||||
|
|
||||||
Every refusal names its rule: `rules` on a risk verdict (form, asset, counterparty, rate, limit, window; outside the hashed body, so
|
Every refusal names its rule, in the module's working language: `rules` on a risk verdict (`forma`, `activ`, `contrapartida`,
|
||||||
the hash of a verdict already issued is unchanged), `reguli` on a receipt verdict (binding, amount, policy, risk, chain). The rate is a
|
`curs`, `limita`, `fereastra`, that is form, asset, counterparty, rate, limit and window; outside the hashed body, so the hash of a
|
||||||
|
verdict already issued is unchanged), `reguli` on a receipt verdict (`legatura`, `suma`, `politica`, `risc`, `lant`, that is binding,
|
||||||
|
amount, policy, risk and chain). The Payments API of Aere Cloud returns the risk rules under their English names since 2026-10-27,
|
||||||
|
translated at its boundary. (Corrected 2026-10-27: this paragraph gave the English names as the ones the module returns.) The rate is a
|
||||||
decimal string (a JSON number is refused, not rounded), and a history entry the engine cannot count is refused, not left out of the
|
decimal string (a JSON number is refused, not rounded), and a history entry the engine cannot count is refused, not left out of the
|
||||||
window. The conformance suite (`conformance/`) holds a vector for each of these rules and runs any implementation against them.
|
window. The conformance suite (`conformance/`) holds a vector for each of these rules and runs any implementation against them.
|
||||||
|
|
||||||
|
|||||||
102
payments/proba-verifica-chitanta.mjs
Normal file
102
payments/proba-verifica-chitanta.mjs
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
// proba-verifica-chitanta.mjs (2026-10-27): the public reader of a payment receipt (verifica-chitanta.mjs) run as a stranger runs it,
|
||||||
|
// with no chain and no network: a local JSON-RPC stub (127.0.0.1, one route per behaviour) and a stub of the AIP-23 reference verifier
|
||||||
|
// that answers the shape of verify-proof.mjs (verdict VALID / PARTIAL / INVALID, the finality level PASSED / PENDING / UNMEASURED /
|
||||||
|
// 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
|
||||||
|
// 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
|
||||||
|
// unknown transaction UNMEASURED where the route says INVALID; and it compared the transaction on an RPC of another chain. 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.
|
||||||
|
// node proba-verifica-chitanta.mjs -> 0 every row as named, 1 otherwise
|
||||||
|
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, pathToFileURL } from 'node:url';
|
||||||
|
|
||||||
|
const AICI = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
const CLI = process.env.AERE_VERIFICA_CHITANTA_CLI ? path.resolve(process.env.AERE_VERIFICA_CHITANTA_CLI) : path.join(AICI, 'verifica-chitanta.mjs');
|
||||||
|
const R = await import(pathToFileURL(path.join(AICI, 'risc.mjs')).href), C = await import(pathToFileURL(path.join(AICI, 'chitanta.mjs')).href);
|
||||||
|
const POLITICA = { v: 1, kind: 'aere-payment-risk-policy', id: 'lab-cli-2026-10-27', currency: 'USD', perPaymentMaxUsd: '1.00', window: { seconds: 86400, maxUsd: '2.50', maxCount: 3 },
|
||||||
|
allowedAssets: [{ chainId: 28001, asset: 'native', decimals: 18 }], refusedCounterparties: [{ address: '0x70099E62735500AA2F85B60C518551a57B202d54', reason: 'a contract with no path to withdraw what is sent to it (AereNotary on 28001)', source: 'contracts/deployments/notary-28001.json' }] };
|
||||||
|
const PAYER = '0x' + 'aa'.repeat(20), PAYEE = '0x' + 'bb'.repeat(20), RATE = { usdPerUnit: '0.5', source: 'declared for the laboratory', at: '2026-10-27T08:00:00.000Z' };
|
||||||
|
const INPUTS = { chainId: 28001, payer: PAYER, payee: PAYEE, asset: 'native', amount: '400000000000000000', rate: RATE, identity: null, history: [], now: 1793000000 };
|
||||||
|
const TX = '0x' + '11'.repeat(32), BLOC = '0x' + '22'.repeat(32);
|
||||||
|
const v = R.judeca(POLITICA, INPUTS);
|
||||||
|
const { instruction, envelope } = C.construiesteChitanta({ chainId: 28001, txHash: TX, blockHash: BLOC, payer: PAYER, payee: PAYEE, asset: 'native', decimals: 18, amount: INPUTS.amount, rate: RATE, verdict: v, policyId: POLITICA.id, createdAt: '2026-10-27T08:00:01.000Z' });
|
||||||
|
const plic = { ...envelope, notarization: { chainId: 28001, notary: '0x70099E62735500AA2F85B60C518551a57B202d54', firstSeen: 1793000100, txHash: '0x' + '33'.repeat(32), block: 7 } };
|
||||||
|
const D = fs.mkdtempSync(path.join(os.tmpdir(), 'aere-cititor-chitanta-'));
|
||||||
|
const scrie = (nume, o) => { const f = path.join(D, nume); fs.writeFileSync(f, JSON.stringify(o)); return f; };
|
||||||
|
const FISIER = scrie('chitanta.json', { instruction, envelope: plic, policy: POLITICA, inputs: INPUTS });
|
||||||
|
const FARA_POLITICA = scrie('chitanta-api.json', { instruction, envelope: plic }); // the shape of `receipt` in the route's answer
|
||||||
|
const fals = JSON.parse(JSON.stringify({ instruction, envelope: plic, policy: POLITICA, inputs: INPUTS })); fals.instruction.amount = '500000000000000000';
|
||||||
|
const FALS = scrie('chitanta-falsa.json', fals);
|
||||||
|
// the reference verifier, stubbed: the state comes from the environment the reader inherits
|
||||||
|
const VP = path.join(D, 'verify-proof-stub.mjs');
|
||||||
|
fs.writeFileSync(VP, "const s = process.env.AERE_STUB_FIN || 'PASSED';\n"
|
||||||
|
+ "if (s === 'NOJSON') { console.log('not json'); process.exit(2); }\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"
|
||||||
|
+ "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"
|
||||||
|
+ "console.log(JSON.stringify({ verdict: failed ? 'INVALID' : unm ? 'PARTIAL' : 'VALID', levels }));\n"
|
||||||
|
+ "process.exitCode = failed ? 1 : unm ? 2 : 0;\n");
|
||||||
|
// 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 srv = http.createServer((q, s) => {
|
||||||
|
let b = ''; q.on('data', (d) => { b += d; }); q.on('end', () => {
|
||||||
|
const mod = q.url.replace(/^\//, ''); let j = {}; try { j = JSON.parse(b); } catch { /* malformed */ }
|
||||||
|
if (mod === 'mut') { s.writeHead(500); s.end('down'); return; }
|
||||||
|
const rez = j.method === 'eth_chainId' ? (mod === 'alt-lant' ? '0xaf0' : '0x6d61')
|
||||||
|
: j.method === 'eth_getTransactionByHash' ? (mod === 'necunoscut' ? null : txBun)
|
||||||
|
: j.method === 'eth_getTransactionReceipt' ? (mod === 'necunoscut' ? null : rcBun) : null;
|
||||||
|
s.writeHead(200, { 'content-type': 'application/json' }); s.end(JSON.stringify({ jsonrpc: '2.0', id: j.id ?? 1, result: rez }));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
await new Promise((ok) => srv.listen(0, '127.0.0.1', ok));
|
||||||
|
const RPC = (mod) => `http://127.0.0.1:${srv.address().port}/${mod}`;
|
||||||
|
const ruleaza = (args, env = {}) => new Promise((ok) => {
|
||||||
|
const p = spawn(process.execPath, [CLI, ...args], { env: { ...process.env, AERE_VERIFY_PROOF: '', ...env }, 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, ultim: out.trim().split('\n').pop() || '' }); });
|
||||||
|
});
|
||||||
|
let rele = 0, n = 0;
|
||||||
|
const cer = (nume, r, cod, cuvant, alt) => {
|
||||||
|
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++;
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
cer('V1 the full receipt file, the chain agreeing, finality PASSED: VALID, notarized (the route says VALID)',
|
||||||
|
await ruleaza([FISIER, '--rpc', RPC('bun'), '--verify-proof', VP], { AERE_STUB_FIN: 'PASSED' }), 0, 'VALID, notarized');
|
||||||
|
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/);
|
||||||
|
cer('V3 (NEW) without --rpc and without --verify-proof: PARTIAL, exit 2, the chain not consulted',
|
||||||
|
await ruleaza([FISIER]), 2, 'PARTIAL', /not consulted/);
|
||||||
|
cer('V4 (NEW) the reference verifier answers PARTIAL, finality PENDING (a receipt checked right after it was issued): PARTIAL, exit 2 (the route says PARTIAL)',
|
||||||
|
await ruleaza([FISIER, '--rpc', RPC('bun'), '--verify-proof', VP], { AERE_STUB_FIN: 'PENDING' }), 2, 'PARTIAL', /finality PENDING/);
|
||||||
|
cer('V5 (NEW) finality UNMEASURED: PARTIAL, exit 2',
|
||||||
|
await ruleaza([FISIER, '--rpc', RPC('bun'), '--verify-proof', VP], { AERE_STUB_FIN: 'UNMEASURED' }), 2, 'PARTIAL', /finality UNMEASURED/);
|
||||||
|
cer('V6 finality FAILED (the verifier says INVALID): INVALID, exit 1 (the route says INVALID)',
|
||||||
|
await ruleaza([FISIER, '--rpc', RPC('bun'), '--verify-proof', VP], { AERE_STUB_FIN: 'FAILED' }), 1, 'INVALID');
|
||||||
|
cer('V7 (NEW) the chain does not answer (HTTP 500): PARTIAL, exit 2, said (the route says PARTIAL since 2026-10-27)',
|
||||||
|
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)',
|
||||||
|
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',
|
||||||
|
await ruleaza([FISIER, '--rpc', RPC('alt-lant'), '--verify-proof', VP], { AERE_STUB_FIN: 'PASSED' }), 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)',
|
||||||
|
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',
|
||||||
|
await ruleaza([FALS, '--rpc', RPC('bun'), '--verify-proof', VP], { AERE_STUB_FIN: 'PASSED' }), 1, 'INVALID', /does not commit/);
|
||||||
|
cer('V12 (NEW) --verify-proof at a path where no verifier is: PARTIAL, exit 2, said',
|
||||||
|
await ruleaza([FISIER, '--rpc', RPC('bun'), '--verify-proof', path.join(D, 'lipsa.mjs')]), 2, 'PARTIAL', /not at/);
|
||||||
|
cer('V13 the verifier answers PARTIAL for another level (witness UNMEASURED) with finality PASSED: VALID, as the route, which decides on finality',
|
||||||
|
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',
|
||||||
|
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 }); }
|
||||||
|
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;
|
||||||
@ -1,10 +1,16 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
// verifica-chitanta.mjs: a reader's check of a payment receipt (roadmap point 25), without ethers: the receipt file holds the
|
// verifica-chitanta.mjs: a reader's check of a payment receipt (roadmap point 25), without ethers: the receipt file holds the
|
||||||
// instruction, the settlement envelope, the policy and the inputs the issuer judged; with --rpc the transaction and its receipt are
|
// instruction, the settlement envelope, the policy and the inputs the issuer judged (the `receiptFile` that POST /v1/payments/receipt
|
||||||
// read from the chain by plain JSON-RPC and compared; with --verify-proof the AIP-23 reference verifier judges the envelope and,
|
// returns is exactly this file); with --rpc the transaction and its receipt are read from the chain by plain JSON-RPC and compared;
|
||||||
// with --rpc, its notarization under a post-quantum certified anchor (finality).
|
// with --verify-proof the AIP-23 reference verifier judges the envelope and, with --rpc, its notarization under a post-quantum
|
||||||
|
// certified anchor (finality).
|
||||||
// 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, 1 INVALID, 2 could not measure (no chain answer, no verifier).
|
// 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
|
||||||
|
// 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
|
||||||
|
// inputs missing from the file, or the notarization not under an anchor yet: finality PENDING or UNMEASURED). 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 reference verifier answered PARTIAL or PENDING; a file without policy or inputs was INVALID.)
|
||||||
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';
|
||||||
@ -15,22 +21,49 @@ const a = process.argv.slice(2); const opt = (k, d = null) => { const i = a.inde
|
|||||||
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 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 r = JSON.parse(fs.readFileSync(f, 'utf8'));
|
const r = JSON.parse(fs.readFileSync(f, 'utf8'));
|
||||||
const rpc = opt('--rpc'); const vp = opt('--verify-proof', process.env.AERE_VERIFY_PROOF || null); const chain = opt('--chain', r.instruction && r.instruction.chainId);
|
const rpc = opt('--rpc'); const vp = opt('--verify-proof', process.env.AERE_VERIFY_PROOF || null); const chain = opt('--chain', r.instruction && r.instruction.chainId);
|
||||||
let tx = null, receipt = null, nem = null;
|
const nem = []; // what was NOT measured: any entry makes the verdict PARTIAL (exit 2), never VALID
|
||||||
|
let tx = null, receipt = null, lantNuStie = false;
|
||||||
if (rpc) {
|
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 { tx = await apel('eth_getTransactionByHash', [r.instruction.txHash]); receipt = await apel('eth_getTransactionReceipt', [r.instruction.txHash]); if (!tx || !receipt) nem = 'the chain does not know transaction ' + r.instruction.txHash; } catch (e) { nem = 'the chain did not answer: ' + e.message; }
|
try {
|
||||||
|
// an RPC of another chain does not know the transaction, and that is not a verdict about the receipt
|
||||||
|
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`);
|
||||||
|
else {
|
||||||
|
tx = await apel('eth_getTransactionByHash', [r.instruction.txHash]); receipt = await apel('eth_getTransactionReceipt', [r.instruction.txHash]);
|
||||||
|
if (!tx || !receipt) { lantNuStie = true; tx = null; receipt = null; }
|
||||||
}
|
}
|
||||||
|
} 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');
|
||||||
|
// 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
|
||||||
|
const lipsa = [!r.policy && 'policy', !r.inputs && 'inputs'].filter(Boolean);
|
||||||
|
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)`);
|
||||||
const v = verificaChitanta({ instruction: r.instruction, envelope: r.envelope }, { policy: r.policy, inputs: r.inputs, tx, receipt });
|
const v = verificaChitanta({ instruction: r.instruction, envelope: r.envelope }, { policy: r.policy, inputs: r.inputs, tx, receipt });
|
||||||
|
const reguliRele = (v.reguli || []).filter((x) => !absente.has(x));
|
||||||
let fin = null, verdictPlic = null;
|
let fin = null, verdictPlic = null;
|
||||||
if (vp && fs.existsSync(vp)) {
|
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 {
|
||||||
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'); } catch { nem = nem || 'the reference verifier gave no JSON'; } } finally { fs.rmSync(t, { force: true }); }
|
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 }); }
|
||||||
} else if (vp) nem = nem || 'the reference verifier is not at ' + vp;
|
}
|
||||||
|
const motiveRele = [...(reguliRele.length ? v.motive.filter((m) => !/^no (policy|inputs) given/.test(m)) : []),
|
||||||
|
...(lantNuStie ? [`the chain does not know transaction ${r.instruction.txHash}`] : []),
|
||||||
|
...(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)] : [])];
|
||||||
|
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
|
||||||
|
// no finality level at all is PARTIAL
|
||||||
|
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'})`);
|
||||||
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 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)}..`,
|
||||||
`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)'}`,
|
||||||
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): ${verdictPlic}; finality ${fin ? fin.state + ': ' + String(fin.detail).slice(0, 140) : 'absent'}` : 'envelope (reference verifier): not run'];
|
||||||
console.log(randuri.join('\n'));
|
console.log(randuri.join('\n'));
|
||||||
const invalid = v.verdict === 'INVALID' || verdictPlic === 'INVALID' || (fin && fin.state === 'FAILED');
|
const valid = !invalid && !nem.length && v.chainChecked && fin && fin.state === 'PASSED';
|
||||||
process.exitCode = invalid ? 1 : nem ? 2 : 0;
|
process.exitCode = invalid ? 1 : valid ? 0 : 2;
|
||||||
if (nem && !invalid) console.log('UNMEASURED: ' + nem);
|
if (invalid) console.log('INVALID: ' + motiveRele.join('; '));
|
||||||
else console.log(invalid ? 'INVALID' : 'VALID' + (fin && fin.state === 'PASSED' ? ', 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'));
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user