agents/x402: a 2-of-2 contract wallet (ERC-1271) that neither the agent nor its owner can spend from alone

AereAgentWallet2of2 holds the agent's tokens and accepts only the agent's signature followed by the policy
service's, over the same digest. In the new cosign mode the wallet service signs only its half, after every
check it already made, and the agent adds its half only after it recomputes the payment itself (payer,
recipient, amount, the nonce of its own ledger entry, validity, digest, declared policy signer).
verifica-plati.mjs requires the wallet's code on chain to be exactly the compiled contract with the two
signers; recompileaza-contract.mjs recompiles the published artifact byte for byte with solc 0.8.23.

Tests: co-signing 16/16, payment verifier 14/14, negative control 30/30, wallet 25/25. On the public
testnet 28001 on 2026-09-29: 13/13 with the 2-of-2 wallet (the agent alone and the policy signer alone
refused by the facilitator and by the token asked on chain) and 9/9 with the wallet key. Evidence in
dovezi-28001/. Nothing here has been run on the Aere Network mainnet.
This commit is contained in:
Liviu 2026-09-30 00:48:10 +03:00
parent 2293c1da86
commit 8b608fe57f
20 changed files with 1480 additions and 40 deletions

View File

@ -14,7 +14,7 @@ notarization command of the verification layer, and the agents' x402 wallet (EIP
| [`proof-kinds/`](proof-kinds/) | the AIP-23 envelope builder the verification layer uses: fourteen proof kinds, one envelope format, digests instead of raw content |
| [`readiness/`](readiness/) | the post-quantum readiness scanner of a public hostname: real TLS handshakes (hybrid only, hybrid preferred, TLS 1.2), HSTS, the certificate; no connection to an address not proven public; a rate limit per client that X-Forwarded-For cannot bypass |
| [`control-plane/`](control-plane/) | from findings to a finished migration: a prioritized plan from the inventory and the scanner, its execution through the gateway, KMS and PKI with consent per action and a measured proof afterwards, recipes for the servers the products do not touch and a rescan that judges them, a compliance report against NIST IR 8547, the EU roadmap and optionally CNSA 2.0, and a console that checks it all again |
| [`agents/`](agents/) | limits an AI agent cannot break unseen: a post-quantum identity (ML-DSA-65), a policy (spending per time window, allowed tools and recipients, which actions need human approval), a signed ledger of every action judged against the policy, approvals and revocation signed by people, and a verifier that re-runs the policy over the whole ledger without trusting the agent; two branches of one ledger are a proof of equivocation anyone can check; and a wallet that pays over x402 only what the agent's ledger records and its policy allows, run on the public testnet with its evidence |
| [`agents/`](agents/) | limits an AI agent cannot break unseen: a post-quantum identity (ML-DSA-65), a policy (spending per time window, allowed tools and recipients, which actions need human approval), a signed ledger of every action judged against the policy, approvals and revocation signed by people, and a verifier that re-runs the policy over the whole ledger without trusting the agent; two branches of one ledger are a proof of equivocation anyone can check; and a wallet that pays over x402 only what the agent's ledger records and its policy allows, either holding the payment key for the owner or co-signing from a 2-of-2 contract wallet that neither the agent nor the owner can spend alone; both run on the public testnet with their evidence |
Each component's README says what it is **not** and what is **not measured**. No third party has reviewed any of them.
@ -34,7 +34,7 @@ test, counts as a failure of the control. Results measured on 2026-09-29 (Node.j
| proof-kinds | 24/24 with the same verifier (`AERE_VERIFY_PROOF=... node proba-proof-kinds.mjs`) | six negative controls inside the test |
| readiness | 6/6 (`node proba-adrese-private.mjs`: the private-address rules, and a local listener no scan may touch) | the rate limit and the queue bound are tested where the service runs, not here (its README says so) |
| control-plane | planner 30/30, command line 9/9, execution 30/30 on real products started locally, remediation 33/33 on real TLS servers, compliance report 27/27 (with the AIP-23 verifier), console 8/8 | remediation 7/7, compliance report 3/3 in this repository |
| agents | policy 27/27 with the AIP-23 verifier (without it 25 run, 2 are reported as skipped and the exit code is 2), ledger 51/51, approval and revocation 39/39, command line 23/23 through files and processes only (on Linux and macOS one more test checks the key file mode; not measured here); x402 wallet 25/25 and payment verifier 10/10 without a network; on the public testnet 28001, 9/9 (`x402/proba-x402-testnet.mjs`, needs a funded testnet key) | 26/26 (`node control-negativ-aprobare.mjs`); x402 21/21 (`node x402/control-negativ-wallet.mjs`) |
| agents | policy 27/27 with the AIP-23 verifier (without it 25 run, 2 are reported as skipped and the exit code is 2), ledger 51/51, approval and revocation 39/39, command line 23/23 through files and processes only (on Linux and macOS one more test checks the key file mode; not measured here); x402 wallet 25/25, 2-of-2 co-signing 16/16 and payment verifier 14/14 without a network; on the public testnet 28001, 9/9 with the wallet key and 13/13 with the 2-of-2 contract wallet (`x402/proba-x402-testnet.mjs`, `x402/proba-cosign-testnet.mjs`, each needs a funded testnet key); the contract's artifact recompiles byte for byte with solc 0.8.23 (`node x402/recompileaza-contract.mjs --solc <solc>`) | 26/26 (`node control-negativ-aprobare.mjs`); x402 30/30 (`node x402/control-negativ-wallet.mjs`); the contract's own tests (7) and their negative control (4/4) run in the Aere Network contracts project, not in this repository |
Code comments, most function and variable names (also many exported between the files of a component), test names and control
messages are in Romanian, and so are the two command words of the KMS HSM tool (explained in its README). Error codes, error
@ -43,4 +43,4 @@ interface, command line and data (`definePolicy`, `verifyLedger`, `approve`, ...
## Licence
MIT, see [LICENSE](LICENSE). Files: 122 (pq-gateway 6, pq-kms 10, pq-pki 6, crypto-inventory 42, verify-layer 8, proof-kinds 3, control-plane 17, agents 24, readiness 4).
MIT, see [LICENSE](LICENSE). Files: 132 (pq-gateway 6, pq-kms 10, pq-pki 6, crypto-inventory 42, verify-layer 8, proof-kinds 3, control-plane 17, agents 34, readiness 4).

View File

@ -55,8 +55,10 @@ printed; the tool creates them with mode 0600, which has no effect on Windows (p
## Paying over x402
`x402/` holds the agent wallet: a service that keeps the payment key for the owner and signs an x402 payment only when the agent's
ledger records it and the policy allows it, with the wallet as the witness of the ledger's heads and time. It was run end to end
on the public testnet 28001 on 2026-09-29, and the evidence is there with a verifier anyone can run. See `x402/README.md`.
ledger records it and the policy allows it, with the wallet as the witness of the ledger's heads and time. With a 2-of-2 contract
wallet (`AereAgentWallet2of2`, ERC-1271) the money leaves only with the agent's signature and the wallet service's together, so
neither the agent nor the owner can spend alone. Both were run end to end on the public testnet 28001 on 2026-09-29, and the
evidence is there with a verifier anyone can run. See `x402/README.md`.
## What the verifier can and cannot see

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,10 @@ agent does **not** hold the payment key. A small service, the **agent wallet**,
when the agent has written that payment into its signed ledger (`../agent-ledger.mjs`) and the owner's policy allows it. The agent
authenticates to the wallet with the ledger entry itself, which only its ML-DSA-65 key can sign.
There are two modes. In the first (`eoa`), the wallet's address is its own key, so the owner who holds that key could also pay without
the agent. In the second (`cosign`), the money sits in a **2-of-2 contract wallet** (`AereAgentWallet2of2`, ERC-1271) that pays only
with the agent's signature **and** the wallet service's signature: neither the agent nor the owner can spend alone.
Node.js 24 and `ethers` (for EIP-712 and secp256k1: `npm install` here). Payments follow x402 version 2 (`exact` scheme, EIP-3009
`transferWithAuthorization`), with the HTTP headers `PAYMENT-REQUIRED`, `PAYMENT-SIGNATURE` and `PAYMENT-RESPONSE`.
@ -45,10 +49,33 @@ before a signature is returned. The agent side is `payWithAgent({ url, ledger, w
talks to the service. `resource-server.mjs` is a minimal x402 seller for tests; `facilitator-local.mjs` makes the same checks as an
x402 facilitator, in memory, for tests without a chain.
## The 2-of-2 contract wallet (`cosign` mode)
`contract/AereAgentWallet2of2.sol` (in the published package; compiled in `AereAgentWallet2of2.json`) holds the agent's tokens and has
no function that moves them. A token that checks signatures with ERC-1271, like the testnet's EIP-3009 token, asks it whether a
signature over a digest is valid, and it answers yes only for 130 bytes: the agent's secp256k1 signature followed by the policy
signer's, both over that digest. Both signers are fixed when it is deployed; there is no owner, no rotation and no recovery.
- The wallet service is started with `contractWallet: <contract address>` and the **policy signer's** key. It makes every check above
and returns its half of the signature with the digest, not a payment header.
- The agent (`payWithAgent({ ..., agentEvmKey })`, `completeazaCosemnarea` in `client.mjs`) adds its half only after it recomputes the
payment itself: from the contract, to the seller and for the amount of the purchase, with the nonce of **its own** ledger entry, a
bounded validity, the same digest, and a policy half that recovers to the declared policy signer. A compromised wallet service cannot
make the agent sign another payment.
- `verifica-plati.mjs`, given an evidence file with `walletContract: { agentSigner, policySigner }`, also requires the wallet's code on
chain to be **exactly** the compiled contract with those two signers in place of its immutables. A contract that only answers
`agentSigner()` could lie; its bytes cannot.
- `node recompileaza-contract.mjs --solc <solc 0.8.23>` compiles the artifact's standard input again and requires the creation and
runtime code to come out byte for byte, and the readable source to be the compiled one; a comment changed by one character must
give other code, or the check reports that it cannot tell. `--solcjs` runs `npx solc@0.8.23` instead (not measured here).
## What it does not do
- It is not trustless custody: whoever holds the wallet key (the owner) can pay without the agent. A 2-of-2 contract wallet
(the agent and the policy service, ERC-1271, which the testnet token accepts) would remove that trust; it is not built.
- In `eoa` mode it is not trustless custody: whoever holds the wallet key (the owner) can pay without the agent. The `cosign` mode
removes that; its contract has no recovery, so if either key is lost the tokens stay in it.
- In both modes the payment signatures are classical (secp256k1): EIP-3009 requires it from a key, and this contract checks
secp256k1 too. A contract could check a post-quantum signature through ERC-1271 instead; this one does not. What is post-quantum
is the agent's identity and ledger (ML-DSA-65), which the wallet service checks before it signs.
- An authorization that is signed but never settled still counts against the limit until the window passes (conservative).
- The wallet API must be reached over a trusted channel: an entry intercepted on the way could be submitted by someone else, who
would then receive the payment payload (the money still goes to the seller the entry names).
@ -62,14 +89,21 @@ Measured on 2026-09-29 (Node.js 24.14.1, ethers 6.16.0):
| test | result |
|---|---|
| `node proba-wallet.mjs` | 25/25 without a network, real EIP-712 signatures and ML-DSA-65 keys: five paid purchases through a local facilitator, the sixth refused by the policy; attacks by the holder of the agent key (a forged "allowed" entry over the limit, an entry for another amount, the same entry twice, a branch with its equivocation proof, a new ledger under a changed policy, a backdated entry, two branches sent at the same time), the owner's revocation, human approval, a payment for another requirement and a replayed payment at the seller |
| `node proba-verifica-plati.mjs` | 10/10 without a network, on the chain responses recorded for the testnet run below |
| `node control-negativ-wallet.mjs` | 21/21: each guard of the wallet, the client, the seller and the verifier removed in a copy, and the named check must fail |
| `node proba-cosign.mjs` | 16/16 without a network (the contract's ERC-1271 check emulated in the local facilitator): three purchases from a 2-of-2 wallet with both halves in order, the fourth refused by the policy; the agent alone, and the policy signer alone, cannot pay; a compromised wallet service that changes the recipient, the payer, the amount, the nonce, the validity, the digest, or signs with another key: the agent refuses to co-sign; the co-signing service over HTTP |
| `node proba-verifica-plati.mjs` | 14/14 without a network, on the chain responses recorded for both testnet runs below, including the contract's code (one byte changed, another signer named, or no code at the wallet: `INVALID`) |
| `node control-negativ-wallet.mjs` | 30/30: each guard of the wallet, the client (including the agent's co-signing checks), the seller and the verifier (including the contract code) removed in a copy, and the named check must fail |
| `AERE_TESTNET_KEY_FILE=<funded testnet key> node proba-x402-testnet.mjs` | 9/9 on the public testnet 28001 through its x402 facilitator: a fresh wallet funded with 0.05 tUSD, three purchases of 0.01 paid and settled on chain, the fourth refused by the policy, a replayed payment refused, the balances and the used authorization nonces read on chain, and the evidence file verified `VALID` (a changed amount, or a payment removed from the file: `INVALID`) |
| `AERE_TESTNET_KEY_FILE=<funded testnet key> node proba-cosign-testnet.mjs` | 13/13 on the public testnet 28001: an `AereAgentWallet2of2` deployed from `AereAgentWallet2of2.json` with two fresh keys, its code on chain exactly the compiled code; `isValidSignature` on chain yes only for agent then policy signer, no for seven other forms; three purchases of 0.01 from it through the testnet facilitator, the fourth refused by the policy; the agent alone and the policy signer alone refused by the facilitator, and by the token itself asked on chain without the facilitator; balances and nonces on chain; the evidence verified `VALID` with the contract code (another policy signer named: `INVALID`) |
| `node recompileaza-contract.mjs --solc <solc 0.8.23>` | `IDENTICAL`: 1193 bytes of creation code and 934 of runtime code, with the native solc 0.8.23+commit.f704f362 |
The testnet run of 2026-09-29 is in `dovezi-28001/`: the evidence file and the recorded chain responses. Anyone can check it:
The contract's own tests (7, hardhat, with the testnet token) and their negative control (each of its four guards removed in a copy,
the named test must fail: 4/4) run in the Aere Network contracts project and are not part of this package.
The testnet runs of 2026-09-29 are in `dovezi-28001/`: the evidence files and the recorded chain responses. Anyone can check them:
```
node verifica-plati.mjs dovezi-28001/plati-agent-2026-09-29-20-23-16.json --rpc https://testnet-rpc.aere.network --all-transfers
node verifica-plati.mjs dovezi-28001/plati-agent-2of2-2026-09-29-21-29-48.json --rpc https://testnet-rpc.aere.network --all-transfers
```
tUSD is the testnet's EIP-3009 token, minted by a faucet and worth nothing. Nothing here has been run on the Aere Network mainnet.

View File

@ -2,11 +2,38 @@
// registrul agentului, apoi cerand portofelului (wallet.mjs) semnatura, apoi reluand cererea cu antetul PAYMENT-SIGNATURE. Numele
// antetelor si forma lor sunt ale specificatiei x402 v2 (transportul HTTP): PAYMENT-REQUIRED, PAYMENT-SIGNATURE, PAYMENT-RESPONSE, toate
// JSON in base64. Daca politica refuza plata, clientul se opreste inainte de portofel (si portofelul ar refuza oricum).
import { x402Action } from './wallet.mjs';
import { x402Action, nonceForEntry, incarcaEthers, EIP3009_TYPES } from './wallet.mjs';
export const b64json = (o) => Buffer.from(JSON.stringify(o), 'utf8').toString('base64');
export function dinB64json(s) { try { return JSON.parse(Buffer.from(String(s), 'base64').toString('utf8')); } catch { return null; } }
/**
* Modul cosign (portofel-contract 2-din-2): agentul isi adauga jumatatea de semnatura NUMAI dupa ce recalculeaza singur ce semneaza.
* Un serviciu de politica compromis nu poate face agentul sa semneze alta plata decat cea din intrarea lui: se cer platitorul (contractul),
* destinatarul si suma din cerinta, nonce-ul = sha256(hash-ul intrarii), un termen marginit, digestul recalculat local, si jumatatea
* serviciului recuperata la semnatarul de politica declarat.
*/
export function completeazaCosemnarea({ payload, req, status, entryHash, agentEvmKey, cosign, now = Math.floor(Date.now() / 1000) }) {
const ethers = incarcaEthers();
const a = payload && payload.payload && payload.payload.authorization;
if (!a) return { ok: false, error: 'the wallet returned no authorization' };
const eq = (x, y) => String(x).toLowerCase() === String(y).toLowerCase();
if (!eq(a.from, status.address)) return { ok: false, error: 'the authorization is not from the 2-of-2 wallet' };
if (!eq(a.to, req.payTo) || String(a.value) !== String(req.amount)) return { ok: false, error: 'the authorization pays another recipient or amount than the purchase' };
if (!eq(a.nonce, nonceForEntry(entryHash))) return { ok: false, error: "the authorization nonce is not the agent's ledger entry" };
if (!(Number(a.validBefore) > now && Number(a.validBefore) <= now + Number(req.maxTimeoutSeconds) + 60)) return { ok: false, error: 'the authorization validity is not the requirement timeout' };
const domeniu = { name: (req.extra && req.extra.name) || status.tokenName, version: (req.extra && req.extra.version) || status.tokenVersion,
chainId: Number(String(req.network).split(':')[1]), verifyingContract: ethers.getAddress(req.asset) };
const digest = ethers.TypedDataEncoder.hash(domeniu, EIP3009_TYPES, { ...a, value: BigInt(a.value), validAfter: BigInt(a.validAfter), validBefore: BigInt(a.validBefore) });
if (!cosign || digest !== cosign.digest) return { ok: false, error: 'the digest the wallet signed is not the one the agent computes' };
let semnatarPolitica = null; try { semnatarPolitica = ethers.recoverAddress(digest, cosign.policySignature); } catch { semnatarPolitica = null; }
if (!semnatarPolitica || !eq(semnatarPolitica, status.policySigner)) return { ok: false, error: 'the policy half is not signed by the declared policy signer' };
const agent = typeof agentEvmKey === 'string' ? new ethers.Wallet(agentEvmKey) : agentEvmKey;
const signature = ethers.concat([agent.signingKey.sign(digest).serialized, cosign.policySignature]);
const complet = { ...payload, payload: { ...payload.payload, signature } };
return { ok: true, header: Buffer.from(JSON.stringify(complet), 'utf8').toString('base64'), paymentPayload: complet };
}
/** portofelul prin HTTP (serviciul wallet.mjs serve), cu aceeasi forma ca obiectul din createWallet */
export function walletOverHttp(baseUrl, fetchImpl = fetch) {
const b = String(baseUrl).replace(/\/+$/, '');
@ -22,10 +49,11 @@ export function walletOverHttp(baseUrl, fetchImpl = fetch) {
* @param {object} o.ledger registrul agentului (openLedger / resumeLedger)
* @param {object} o.wallet portofelul (createWallet sau walletOverHttp)
* @param {Array} [o.approvals] aprobari umane pentru aceasta plata, cand politica le cere
* @param {string|object} [o.agentEvmKey] cheia secp256k1 a agentului, ceruta numai de un portofel-contract 2-din-2 (modul cosign)
* @param {Function} [o.fetchImpl]
* @returns {Promise<{paid:boolean, status:number, reason?:string, body?:string, settlement?:object, entry?:object, receipt?:object, decision?:object}>}
*/
export async function payWithAgent({ url, ledger, wallet, approvals = [], fetchImpl = fetch }) {
export async function payWithAgent({ url, ledger, wallet, approvals = [], agentEvmKey = null, fetchImpl = fetch }) {
const r0 = await fetchImpl(url);
if (r0.status !== 402) return { paid: false, status: r0.status, body: await r0.text(), reason: r0.ok ? 'no payment was required' : `the resource answered ${r0.status}` };
const pr = dinB64json(r0.headers.get('payment-required')) || (await r0.json().catch(() => null));
@ -39,8 +67,16 @@ export async function payWithAgent({ url, ledger, wallet, approvals = [], fetchI
// 2. semnatura portofelului, care judeca din nou tot registrul
const a = await wallet.authorize({ requirements: req, resource: pr.resource || null, ledger: ledger.export() });
if (!a.ok) return { paid: false, status: 402, reason: `the wallet refused: ${a.error}`, entry: r.entry, equivocation: a.equivocation };
// 2b. portofelul-contract 2-din-2: agentul verifica si isi adauga jumatatea
let header = a.header;
if (a.cosign) {
if (!agentEvmKey) return { paid: false, status: 402, reason: "the wallet is a 2-of-2 contract and needs the agent's signature: agentEvmKey is missing", entry: r.entry };
const c = completeazaCosemnarea({ payload: a.paymentPayload, req, status: w, entryHash: r.entry.hash, agentEvmKey, cosign: a.cosign });
if (!c.ok) return { paid: false, status: 402, reason: `the agent refused to co-sign: ${c.error}`, entry: r.entry };
header = c.header;
}
// 3. cererea reluata, cu plata
const r1 = await fetchImpl(url, { headers: { 'PAYMENT-SIGNATURE': a.header } });
const r1 = await fetchImpl(url, { headers: { 'PAYMENT-SIGNATURE': header } });
const settlement = dinB64json(r1.headers.get('payment-response'));
const body = await r1.text();
return { paid: r1.ok && !!(settlement && settlement.success), status: r1.status, body, settlement, entry: r.entry, receipt: a.receipt, decision: a.decision,

View File

@ -0,0 +1,37 @@
// Portofelul-contract 2-din-2 al agentului (AereAgentWallet2of2.sol), vazut din afara (2026-09-29): artefactul compilat si codul de
// rulare ASTEPTAT pentru o pereche de semnatari. Un contract care doar RASPUNDE agentSigner()/policySigner() ar putea minti; octetii de
// pe lant nu pot: codul de rulare e cel compilat din sursa publicata, cu cele doua adrese scrise in locul imutabilelor, si nimic altceva.
import fs from 'node:fs';
import path from 'node:path';
import crypto from 'node:crypto';
import { fileURLToPath } from 'node:url';
const AICI = path.dirname(fileURLToPath(import.meta.url));
export const ARTEFACT = path.join(AICI, 'AereAgentWallet2of2.json');
export function incarcaArtefact(f = ARTEFACT) {
const a = JSON.parse(fs.readFileSync(f, 'utf8'));
if (a.kind !== 'aere-agent-wallet-2of2-artifact' || !a.deployedBytecode || !a.immutables || !a.immutables.agentSigner || !a.immutables.policySigner)
throw new Error('not an AereAgentWallet2of2 artifact');
return a;
}
/** sha256 al sursei contractului, citit din intrarea compilatorului (ce s-a compilat), nu dintr-un camp declarat */
export function amprentaSursei(a) {
const s = a.standardJsonInput && a.standardJsonInput.sources && a.standardJsonInput.sources[a.sourcePath];
return s ? crypto.createHash('sha256').update(s.content, 'utf8').digest('hex') : null;
}
/** codul de rulare pe care il are pe lant un AereAgentWallet2of2(agentSigner, policySigner), in hex cu litere mici */
export function codulAsteptat(a, { agentSigner, policySigner }) {
const cod = Buffer.from(a.deployedBytecode.replace(/^0x/, ''), 'hex');
const pune = (poz, adresa) => {
const h = String(adresa).toLowerCase().replace(/^0x/, '');
if (!/^[0-9a-f]{40}$/.test(h)) throw new Error(`not an address: ${adresa}`);
const cuvant = Buffer.from(h.padStart(64, '0'), 'hex');
for (const p of poz) { if (p.length !== 32) throw new Error('immutable of unexpected length'); cuvant.copy(cod, p.start); }
};
pune(a.immutables.agentSigner, agentSigner);
pune(a.immutables.policySigner, policySigner);
return '0x' + cod.toString('hex');
}

View File

@ -0,0 +1,53 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol";
/**
* @title AereAgentWallet2of2, an AI agent's payment wallet that neither the agent nor its owner can spend from alone
*
* @notice The wallet holds tokens and has no function that moves them. Tokens leave it only through a token that asks it,
* by ERC-1271, whether a signature over a digest is valid (EIP-3009 transferWithAuthorization in the testnet token
* AereTestUSD3009 does that for a contract `from`). The answer is yes only for a 130-byte signature that is the agent's
* secp256k1 signature followed by the policy signer's, both over that digest, both non-malleable (OpenZeppelin ECDSA).
*
* The agent holds the first key. The owner's policy service holds the second and co-signs a payment only when the
* agent's post-quantum ledger records it and the owner's policy allows it (tools/agent-policy/x402/wallet.mjs, the
* `cosign` mode). So a stolen agent key cannot spend past the policy, and the owner cannot spend without the agent.
*
* HONEST SCOPE. Both signers are fixed at deployment; there is no rotation, no recovery and no owner: if either key
* is lost, the tokens stay in the wallet. The agent's key here is classical (secp256k1); the post-quantum binding is
* the ledger the policy service checks before it co-signs, not this contract. Any digest both keys sign is valid, as
* in any 2-of-2 multisig: the software signs only EIP-3009 digests. Written 2026-09-29 for the public testnet 28001;
* not deployed on chain 2800 (that is the founder's decision).
*/
contract AereAgentWallet2of2 is IERC1271 {
bytes4 private constant MAGIC = 0x1626ba7e;
bytes4 private constant INVALID = 0xffffffff;
/// @notice the agent's key (first 65 bytes of a valid signature)
address public immutable agentSigner;
/// @notice the owner's policy service key (last 65 bytes of a valid signature)
address public immutable policySigner;
error ZeroSigner();
error SameSigner();
constructor(address agent, address policy) {
if (agent == address(0) || policy == address(0)) revert ZeroSigner();
if (agent == policy) revert SameSigner();
agentSigner = agent;
policySigner = policy;
}
/// @inheritdoc IERC1271
function isValidSignature(bytes32 hash, bytes calldata signature) external view returns (bytes4) {
if (signature.length != 130) return INVALID;
(address a, ECDSA.RecoverError ea) = ECDSA.tryRecover(hash, signature[0:65]);
if (ea != ECDSA.RecoverError.NoError || a != agentSigner) return INVALID;
(address p, ECDSA.RecoverError ep) = ECDSA.tryRecover(hash, signature[65:130]);
if (ep != ECDSA.RecoverError.NoError || p != policySigner) return INVALID;
return MAGIC;
}
}

View File

@ -1,4 +1,5 @@
// Controlul negativ al portofelului de plati x402 (proba-wallet.mjs) si al verificatorului de plati (proba-verifica-plati.mjs): fiecare paznic se strica intr-o COPIE (agent-policy/*.mjs si
// Controlul negativ al portofelului de plati x402 (proba-wallet.mjs), al modului cosign 2-din-2 (proba-cosign.mjs) si al verificatorului de plati
// (proba-verifica-plati.mjs, inclusiv codul portofelului-contract): fiecare paznic se strica intr-o COPIE (agent-policy/*.mjs si
// x402/*.mjs, in aceeasi asezare), proba ruleaza pe copie si trebuie sa iasa rosie EXACT pe verificarea numita, cu proba chiar rulata.
// Plantarile sunt conditii false la rulare sau randuri scoase, deci copia se incarca intotdeauna; o ancora care nu apare exact o data,
// sau o proba care nu ajunge la rezumat, e un esec al controlului (STRICAT), nu o linie informativa.
@ -16,7 +17,7 @@ const SUS = path.resolve(AICI, '..');
let ETHERS = process.env.AERE_ETHERS || null; // o copie a acestui dosar (controlul portii) primeste calea din mediu
if (!ETHERS) try { ETHERS = createRequire(path.join(AICI, 'wallet.mjs')).resolve('ethers'); }
catch { try { ETHERS = createRequire(path.join(AICI, 'wallet.mjs')).resolve(path.resolve(SUS, '..', '..', 'contracts', 'node_modules', 'ethers')); } catch { console.log('NEMASURAT: ethers nu se gaseste (npm install aici, sau AERE_ETHERS=<cale>)'); process.exit(2); } }
const V = 'proba-verifica-plati.mjs';
const V = 'proba-verifica-plati.mjs', K = 'proba-cosign.mjs';
const PLANTARI = [
// [nume, fisier (relativ la x402/), tipar, inlocuire, textul verificarii care trebuie sa iasa rosie]
['cererile nu mai sunt judecate una cate una', 'wallet.mjs', 'function authorize(x) { const r = coada.then(() => autorizeaza(x)); coada = r.catch(() => {}); return r; }', 'function authorize(x) { return autorizeaza(x); }', '12. ATAC'],
@ -32,6 +33,13 @@ const PLANTARI = [
['serverul de resurse primeste o plata pentru alta cerinta', 'resource-server.mjs', 'if (cheie(payload.accepted) !== cheie(requirement)) return cere(', "if (cheie(payload.accepted) !== cheie(requirement) && process.env.AERE_PLANTA_NICIODATA === 'da') return cere(", '11. CONTROL'],
['clientul cere semnatura si cand politica a refuzat', 'client.mjs', "if (!r.allowed) return { paid: false, status: 402, reason: `the agent's policy refused the payment: ${r.reason}`, entry: r.entry };", '', '2. CONTROL'],
['portofelul porneste si fara limita in activul lui', 'wallet.mjs', 'if (!policy.spend || policy.spend.asset !== assetId(network, token)) throw', "if ((!policy.spend || policy.spend.asset !== assetId(network, token)) && process.env.AERE_PLANTA_NICIODATA === 'da') throw", 'o politica fara limita'],
// modul cosign (portofel-contract 2-din-2): ce verifica agentul inainte sa-si adauge jumatatea de semnatura
['agentul semneaza o autorizare din alt portofel', 'client.mjs', 'if (!eq(a.from, status.address)) return', 'if (false) return', 'schimba platitorul', K],
['agentul semneaza alt destinatar sau alta suma', 'client.mjs', 'if (!eq(a.to, req.payTo) || String(a.value) !== String(req.amount)) return', 'if (false) return', 'schimba destinatarul', K],
['agentul semneaza alt nonce decat intrarea lui', 'client.mjs', 'if (!eq(a.nonce, nonceForEntry(entryHash))) return', 'if (false) return', 'alt nonce', K],
['agentul semneaza un termen oricat de lung', 'client.mjs', 'if (!(Number(a.validBefore) > now && Number(a.validBefore) <= now + Number(req.maxTimeoutSeconds) + 60)) return', 'if (false) return', 'lungeste termenul', K],
['agentul nu mai compara digestul cu al lui', 'client.mjs', 'if (!cosign || digest !== cosign.digest) return', 'if (!cosign) return', 'alt digest', K],
['agentul nu mai cere jumatatea semnatarului declarat', 'client.mjs', 'if (!semnatarPolitica || !eq(semnatarPolitica, status.policySigner)) return', 'if (false) return', 'alta cheie decat semnatarul declarat', K],
// verificatorul platilor, pe raspunsurile inregistrate de pe 28001
['verificatorul nu mai cere ca registrul sa verifice', 'verifica-plati.mjs', 'entries)`, v.ok, v.error', 'entries)`, true, v.error', '2. CONTROL: o suma schimbata', V],
['verificatorul nu mai cere ca intrarea platii sa fie in registru', 'verifica-plati.mjs', '!!e && e.hash === p.entryHash)', '!!e)', '3. CONTROL', V],
@ -41,12 +49,17 @@ const PLANTARI = [
['verificatorul nu mai cere suma din Transfer', 'verifica-plati.mjs', '&& BigInt(l.data) === BigInt(a.amount)));', '));', '7. CONTROL', V],
['verificatorul nu mai cere ca orice Transfer sa fie in registru', 'verifica-plati.mjs', 'straine.length === 0,', 'true,', '8. CONTROL', V],
['verificatorul nu mai cere lantul dosarului', 'verifica-plati.mjs', 'lantul === chain,', 'true,', '9. CONTROL', V],
// portofelul-contract 2-din-2 in dosar: codul de pe lant
['verificatorul nu mai judeca portofelul-contract', 'verifica-plati.mjs', ' if (d.walletContract) {', " if (d.walletContract && process.env.AERE_PLANTA_NICIODATA === 'da') {", '14. CONTROL', V],
['verificatorul cere doar lungimea codului, nu codul', 'verifica-plati.mjs', 'cod === asteptat, cod === asteptat ?', 'cod.length === asteptat.length, cod === asteptat ?', '12. CONTROL', V],
['codul asteptat nu mai poarta semnatarii din dosar', 'contract-2of2.mjs', 'cuvant.copy(cod, p.start); }', '}', '11. dosarul portofelului 2-din-2', V],
];
function copie() {
const t = fs.mkdtempSync(path.join(os.tmpdir(), 'aere-wallet-ctl-'));
for (const f of fs.readdirSync(SUS).filter((n) => n.endsWith('.mjs'))) fs.copyFileSync(path.join(SUS, f), path.join(t, f));
fs.mkdirSync(path.join(t, 'x402'));
for (const f of fs.readdirSync(AICI).filter((n) => n.endsWith('.mjs'))) fs.copyFileSync(path.join(AICI, f), path.join(t, 'x402', f));
// modulele si artefactul contractului 2-din-2 (verificatorul il citeste de langa el); nu package*.json si nu node_modules
for (const f of fs.readdirSync(AICI).filter((n) => n.endsWith('.mjs') || n === 'AereAgentWallet2of2.json')) fs.copyFileSync(path.join(AICI, f), path.join(t, 'x402', f));
fs.cpSync(path.join(AICI, 'dovezi-28001'), path.join(t, 'x402', 'dovezi-28001'), { recursive: true });
return t;
}
@ -57,13 +70,13 @@ function ruleaza(t, proba = 'proba-wallet.mjs') {
const c = spawn(process.execPath, [path.join(t, 'x402', proba)], { env: { ...process.env, AERE_ETHERS: ETHERS } }); let out = '';
const ceas = setTimeout(() => c.kill(), 240000);
c.stdout.on('data', (x) => { out += x; }); c.stderr.on('data', (x) => { out += x; });
c.on('close', (cod) => { clearTimeout(ceas); resolve({ cod, rulat: /(agent-wallet|verifica-plati): \d+\/\d+/.test(out), rosii: out.split('\n').filter((l) => /^\s+RAU\s/.test(l)) }); });
c.on('close', (cod) => { clearTimeout(ceas); resolve({ cod, rulat: /(agent-wallet|verifica-plati|agent-cosign): \d+\/\d+/.test(out), rosii: out.split('\n').filter((l) => /^\s+RAU\s/.test(l)) }); });
});
}
async function inGrup(lucrari) { const rez = new Array(lucrari.length); let i = 0;
await Promise.all(Array.from({ length: PARALEL }, async () => { while (i < lucrari.length) { const k = i++; rez[k] = await lucrari[k](); } })); return rez; }
let esecuri = 0;
const martori = await inGrup(['proba-wallet.mjs', V].map((proba) => async () => { const t0 = copie(); try { return [proba, await ruleaza(t0, proba)]; } finally { fs.rmSync(t0, { recursive: true, force: true }); } }));
const martori = await inGrup(['proba-wallet.mjs', V, K].map((proba) => async () => { const t0 = copie(); try { return [proba, await ruleaza(t0, proba)]; } finally { fs.rmSync(t0, { recursive: true, force: true }); } }));
for (const [proba, m0] of martori) {
if (m0.cod === 0 && m0.rulat && !m0.rosii.length) console.log(` OK martorul ${proba}: copia neatinsa verde`);
else { esecuri++; console.log(` RAU martorul ${proba} nu e verde (cod ${m0.cod}, ${m0.rulat ? m0.rosii.length + ' RAU' : 'nu a ajuns la rezumat'})`); }

View File

@ -0,0 +1,164 @@
{
"v": 1,
"kind": "aere-agent-x402-payments",
"network": "eip155:28001",
"token": "0x8215bA247a3574af8EBC36606eB437811E318FBd",
"wallet": "0xD07ff8B519EDf9c2EbecFe230602fb4598334a37",
"fromBlock": 4030703,
"facilitator": "https://testnet-rpc.aere.network/x402",
"policy": {
"v": 1,
"kind": "aere-agent-policy",
"agentId": "aere-agent:52e7dea6745a533c34eb58c23eae115eb7e752fb",
"spend": {
"amount": "30000",
"windowSeconds": 3600,
"asset": "eip155:28001/erc20:0x8215ba247a3574af8ebc36606eb437811e318fbd"
},
"tools": null,
"recipients": [
"0x7bdf94f6de68720a494917fec1114745da71cb9e"
],
"wallet": "0xd07ff8b519edf9c2ebecfe230602fb4598334a37"
},
"policyHash": "0xed6e1a16ad1fe3f48640be27480fd4a553f71f1240a520e78251ad5b8627dc69",
"ledger": {
"version": "aere-agent-ledger/2 (2026-09-29)",
"agentId": "aere-agent:52e7dea6745a533c34eb58c23eae115eb7e752fb",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\n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\ntgab72wT\n-----END PUBLIC KEY-----\n",
"policyHash": "0xed6e1a16ad1fe3f48640be27480fd4a553f71f1240a520e78251ad5b8627dc69",
"session": "dac452b8757cf2df425001a198fbaf38",
"entries": [
{
"seq": 0,
"prev": "0000000000000000000000000000000000000000000000000000000000000000",
"body": {
"v": 2,
"agentId": "aere-agent:52e7dea6745a533c34eb58c23eae115eb7e752fb",
"policyHash": "0xed6e1a16ad1fe3f48640be27480fd4a553f71f1240a520e78251ad5b8627dc69",
"session": "dac452b8757cf2df425001a198fbaf38",
"at": 1790718113,
"action": {
"kind": "payment",
"from": "0xd07ff8b519edf9c2ebecfe230602fb4598334a37",
"to": "0x7bdf94f6de68720a494917fec1114745da71cb9e",
"amount": "10000",
"asset": "eip155:28001/erc20:0x8215ba247a3574af8ebc36606eb437811e318fbd",
"ref": "0x76ea9efa9a280992b196159c34a66078498144010d04fc573ea5aa7c072efe5b",
"at": 1790718113
},
"decision": {
"allowed": true,
"reason": "under the limit"
},
"provenance": null,
"seq": 0
},
"signature": "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",
"hash": "bbcc4949c1c561ad02699577f4c2755d90f06b737b0a08bf15caad3e9b3998b4"
},
{
"seq": 1,
"prev": "bbcc4949c1c561ad02699577f4c2755d90f06b737b0a08bf15caad3e9b3998b4",
"body": {
"v": 2,
"agentId": "aere-agent:52e7dea6745a533c34eb58c23eae115eb7e752fb",
"policyHash": "0xed6e1a16ad1fe3f48640be27480fd4a553f71f1240a520e78251ad5b8627dc69",
"session": "dac452b8757cf2df425001a198fbaf38",
"at": 1790718118,
"action": {
"kind": "payment",
"from": "0xd07ff8b519edf9c2ebecfe230602fb4598334a37",
"to": "0x7bdf94f6de68720a494917fec1114745da71cb9e",
"amount": "10000",
"asset": "eip155:28001/erc20:0x8215ba247a3574af8ebc36606eb437811e318fbd",
"ref": "0x76ea9efa9a280992b196159c34a66078498144010d04fc573ea5aa7c072efe5b",
"at": 1790718118
},
"decision": {
"allowed": true,
"reason": "under the limit"
},
"provenance": null,
"seq": 1
},
"signature": "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",
"hash": "0fa5efcd15c432ef8326839f0c796c6a6e98cbec371f5c40460326845719ac3a"
},
{
"seq": 2,
"prev": "0fa5efcd15c432ef8326839f0c796c6a6e98cbec371f5c40460326845719ac3a",
"body": {
"v": 2,
"agentId": "aere-agent:52e7dea6745a533c34eb58c23eae115eb7e752fb",
"policyHash": "0xed6e1a16ad1fe3f48640be27480fd4a553f71f1240a520e78251ad5b8627dc69",
"session": "dac452b8757cf2df425001a198fbaf38",
"at": 1790718122,
"action": {
"kind": "payment",
"from": "0xd07ff8b519edf9c2ebecfe230602fb4598334a37",
"to": "0x7bdf94f6de68720a494917fec1114745da71cb9e",
"amount": "10000",
"asset": "eip155:28001/erc20:0x8215ba247a3574af8ebc36606eb437811e318fbd",
"ref": "0x76ea9efa9a280992b196159c34a66078498144010d04fc573ea5aa7c072efe5b",
"at": 1790718122
},
"decision": {
"allowed": true,
"reason": "under the limit"
},
"provenance": null,
"seq": 2
},
"signature": "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",
"hash": "2bf1b50bf7e833f2d49cc9c14ae7c64abcc033da7b59daa440bbd398ff49f159"
},
{
"seq": 3,
"prev": "2bf1b50bf7e833f2d49cc9c14ae7c64abcc033da7b59daa440bbd398ff49f159",
"body": {
"v": 2,
"agentId": "aere-agent:52e7dea6745a533c34eb58c23eae115eb7e752fb",
"policyHash": "0xed6e1a16ad1fe3f48640be27480fd4a553f71f1240a520e78251ad5b8627dc69",
"session": "dac452b8757cf2df425001a198fbaf38",
"at": 1790718127,
"action": {
"kind": "payment",
"from": "0xd07ff8b519edf9c2ebecfe230602fb4598334a37",
"to": "0x7bdf94f6de68720a494917fec1114745da71cb9e",
"amount": "10000",
"asset": "eip155:28001/erc20:0x8215ba247a3574af8ebc36606eb437811e318fbd",
"ref": "0x76ea9efa9a280992b196159c34a66078498144010d04fc573ea5aa7c072efe5b",
"at": 1790718127
},
"decision": {
"allowed": false,
"reason": "over the limit: 40000 > 30000 per 3600s"
},
"provenance": null,
"seq": 3
},
"signature": "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",
"hash": "dab777eecfa1ee52eda69e9f1e23880e4ac6b1df8dcb23636ea149bc0461273e"
}
]
},
"payments": [
{
"entrySeq": 0,
"entryHash": "bbcc4949c1c561ad02699577f4c2755d90f06b737b0a08bf15caad3e9b3998b4",
"transaction": "0xd00d81dbeac63874f11146b10008eb614a0c8c550176ed1e46a5989de41f2d20"
},
{
"entrySeq": 1,
"entryHash": "0fa5efcd15c432ef8326839f0c796c6a6e98cbec371f5c40460326845719ac3a",
"transaction": "0xdc654e77fb993a7434b1146fdb94a45ad592eaa42e7099bf0df7db1eae3271ec"
},
{
"entrySeq": 2,
"entryHash": "2bf1b50bf7e833f2d49cc9c14ae7c64abcc033da7b59daa440bbd398ff49f159",
"transaction": "0xe01fe7e8cf63237a2fc1fd58a416c84ce051bb372a806bcf228f843f87699a55"
}
],
"createdAt": "2026-09-29T21:42:10.588Z"
}

View File

@ -0,0 +1,171 @@
{
"v": 1,
"kind": "aere-agent-x402-payments",
"network": "eip155:28001",
"token": "0x8215bA247a3574af8EBC36606eB437811E318FBd",
"wallet": "0xB148fE63BFaf760A3B741CE0B68ef1B9a04A7826",
"fromBlock": 4029318,
"walletContract": {
"contract": "AereAgentWallet2of2",
"sourceSha256": "dcdc02a7bd5b94b203a4aa889a4fac10d06f4aa24fe8e6c88d2a1ecfd582de03",
"agentSigner": "0x9c32bAB9c0d9c992166f8C0994770806562c082E",
"policySigner": "0x0852C8FC71Ec6F632Af628442A0efA1294612Ce5",
"deployTransaction": "0x650303ec976706e6d55cc18b1440c94bcb83257f4b7100bcfe0d641a87fd5c65"
},
"facilitator": "https://testnet-rpc.aere.network/x402",
"policy": {
"v": 1,
"kind": "aere-agent-policy",
"agentId": "aere-agent:9a3c54ce2ab65619fa51003ae602805104f6a208",
"spend": {
"amount": "30000",
"windowSeconds": 3600,
"asset": "eip155:28001/erc20:0x8215ba247a3574af8ebc36606eb437811e318fbd"
},
"tools": null,
"recipients": [
"0x388e8ed4eaa0d51ce0e3c228c7514a3e64c434ca"
],
"wallet": "0xb148fe63bfaf760a3b741ce0b68ef1b9a04a7826"
},
"policyHash": "0x87ebb73fd622d5ab78e4f7b021f037322b104c18db53949d0e07d147d2b65dd4",
"ledger": {
"version": "aere-agent-ledger/2 (2026-09-29)",
"agentId": "aere-agent:9a3c54ce2ab65619fa51003ae602805104f6a208",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\n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\nkecWJpYj\n-----END PUBLIC KEY-----\n",
"policyHash": "0x87ebb73fd622d5ab78e4f7b021f037322b104c18db53949d0e07d147d2b65dd4",
"session": "a60c6ae307a33cc1f395669f85d1c0db",
"entries": [
{
"seq": 0,
"prev": "0000000000000000000000000000000000000000000000000000000000000000",
"body": {
"v": 2,
"agentId": "aere-agent:9a3c54ce2ab65619fa51003ae602805104f6a208",
"policyHash": "0x87ebb73fd622d5ab78e4f7b021f037322b104c18db53949d0e07d147d2b65dd4",
"session": "a60c6ae307a33cc1f395669f85d1c0db",
"at": 1790717361,
"action": {
"kind": "payment",
"from": "0xb148fe63bfaf760a3b741ce0b68ef1b9a04a7826",
"to": "0x388e8ed4eaa0d51ce0e3c228c7514a3e64c434ca",
"amount": "10000",
"asset": "eip155:28001/erc20:0x8215ba247a3574af8ebc36606eb437811e318fbd",
"ref": "0x03f3dc7265c2210686dcc7dc947c1c0e59f3d18b86099c0f195b1ed96ea8f3c5",
"at": 1790717361
},
"decision": {
"allowed": true,
"reason": "under the limit"
},
"provenance": null,
"seq": 0
},
"signature": "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",
"hash": "bb0d70cead599bac06f27a989a20b5845adcb405ae6e7f4db93fe638c432643b"
},
{
"seq": 1,
"prev": "bb0d70cead599bac06f27a989a20b5845adcb405ae6e7f4db93fe638c432643b",
"body": {
"v": 2,
"agentId": "aere-agent:9a3c54ce2ab65619fa51003ae602805104f6a208",
"policyHash": "0x87ebb73fd622d5ab78e4f7b021f037322b104c18db53949d0e07d147d2b65dd4",
"session": "a60c6ae307a33cc1f395669f85d1c0db",
"at": 1790717368,
"action": {
"kind": "payment",
"from": "0xb148fe63bfaf760a3b741ce0b68ef1b9a04a7826",
"to": "0x388e8ed4eaa0d51ce0e3c228c7514a3e64c434ca",
"amount": "10000",
"asset": "eip155:28001/erc20:0x8215ba247a3574af8ebc36606eb437811e318fbd",
"ref": "0x03f3dc7265c2210686dcc7dc947c1c0e59f3d18b86099c0f195b1ed96ea8f3c5",
"at": 1790717368
},
"decision": {
"allowed": true,
"reason": "under the limit"
},
"provenance": null,
"seq": 1
},
"signature": "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",
"hash": "6c18e4a83daace82588fbdf2570dd0351ea0947cd002625c0955a82bfca35b51"
},
{
"seq": 2,
"prev": "6c18e4a83daace82588fbdf2570dd0351ea0947cd002625c0955a82bfca35b51",
"body": {
"v": 2,
"agentId": "aere-agent:9a3c54ce2ab65619fa51003ae602805104f6a208",
"policyHash": "0x87ebb73fd622d5ab78e4f7b021f037322b104c18db53949d0e07d147d2b65dd4",
"session": "a60c6ae307a33cc1f395669f85d1c0db",
"at": 1790717374,
"action": {
"kind": "payment",
"from": "0xb148fe63bfaf760a3b741ce0b68ef1b9a04a7826",
"to": "0x388e8ed4eaa0d51ce0e3c228c7514a3e64c434ca",
"amount": "10000",
"asset": "eip155:28001/erc20:0x8215ba247a3574af8ebc36606eb437811e318fbd",
"ref": "0x03f3dc7265c2210686dcc7dc947c1c0e59f3d18b86099c0f195b1ed96ea8f3c5",
"at": 1790717374
},
"decision": {
"allowed": true,
"reason": "under the limit"
},
"provenance": null,
"seq": 2
},
"signature": "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",
"hash": "fc918dd42c240c4d25c1652d40dc1dbd8ec6f4c1d12f4f32f90c31a8848e469f"
},
{
"seq": 3,
"prev": "fc918dd42c240c4d25c1652d40dc1dbd8ec6f4c1d12f4f32f90c31a8848e469f",
"body": {
"v": 2,
"agentId": "aere-agent:9a3c54ce2ab65619fa51003ae602805104f6a208",
"policyHash": "0x87ebb73fd622d5ab78e4f7b021f037322b104c18db53949d0e07d147d2b65dd4",
"session": "a60c6ae307a33cc1f395669f85d1c0db",
"at": 1790717379,
"action": {
"kind": "payment",
"from": "0xb148fe63bfaf760a3b741ce0b68ef1b9a04a7826",
"to": "0x388e8ed4eaa0d51ce0e3c228c7514a3e64c434ca",
"amount": "10000",
"asset": "eip155:28001/erc20:0x8215ba247a3574af8ebc36606eb437811e318fbd",
"ref": "0x03f3dc7265c2210686dcc7dc947c1c0e59f3d18b86099c0f195b1ed96ea8f3c5",
"at": 1790717379
},
"decision": {
"allowed": false,
"reason": "over the limit: 40000 > 30000 per 3600s"
},
"provenance": null,
"seq": 3
},
"signature": "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",
"hash": "36fc236fe803d05f787c8ca31005cda8445c573e4ee8cad05ba889a01cd0ba63"
}
]
},
"payments": [
{
"entrySeq": 0,
"entryHash": "bb0d70cead599bac06f27a989a20b5845adcb405ae6e7f4db93fe638c432643b",
"transaction": "0x5517ca46b81e474b73ff45476d7b70eceec22be963ef734c651d1edd24d041a4"
},
{
"entrySeq": 1,
"entryHash": "6c18e4a83daace82588fbdf2570dd0351ea0947cd002625c0955a82bfca35b51",
"transaction": "0x8d709c307943baecb9223cb32f3f5f1f3e4f1a6442925d60f1be470a02821675"
},
{
"entrySeq": 2,
"entryHash": "fc918dd42c240c4d25c1652d40dc1dbd8ec6f4c1d12f4f32f90c31a8848e469f",
"transaction": "0x4c0779db602945d787f2b795a803922b14030da67bfaa689103b85e344dd12ac"
}
],
"createdAt": "2026-09-29T21:29:48.714Z"
}

View File

@ -0,0 +1,205 @@
{
"v": 1,
"recordedAt": "2026-09-29T21:42:19.343Z",
"rpc": "https://testnet-rpc.aere.network",
"chainId": "0x6d61",
"receipts": {
"0xd00d81dbeac63874f11146b10008eb614a0c8c550176ed1e46a5989de41f2d20": {
"blockHash": "0x2b37241d2181dadfaa03f8a07d84cc268556782a8c73b14435b82b6e0415b6bd",
"blockNumber": "0x3d80f9",
"contractAddress": null,
"cumulativeGasUsed": "0x144b8",
"from": "0x50f2a153be2c248b7050914a08f4f6f4498c4e48",
"gasUsed": "0x144b8",
"effectiveGasPrice": "0x3b9aca00",
"logs": [
{
"address": "0x8215ba247a3574af8ebc36606eb437811e318fbd",
"topics": [
"0x98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a5",
"0x000000000000000000000000d07ff8b519edf9c2ebecfe230602fb4598334a37",
"0xa97a7a593a9badd25da8eec3c1cb9c277748c213179ccbbaa15ffc12e916581c"
],
"data": "0x",
"blockNumber": "0x3d80f9",
"transactionHash": "0xd00d81dbeac63874f11146b10008eb614a0c8c550176ed1e46a5989de41f2d20",
"transactionIndex": "0x0",
"blockHash": "0x2b37241d2181dadfaa03f8a07d84cc268556782a8c73b14435b82b6e0415b6bd",
"blockTimestamp": "0x6abc30a2",
"logIndex": "0x0",
"removed": false
},
{
"address": "0x8215ba247a3574af8ebc36606eb437811e318fbd",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000d07ff8b519edf9c2ebecfe230602fb4598334a37",
"0x0000000000000000000000007bdf94f6de68720a494917fec1114745da71cb9e"
],
"data": "0x0000000000000000000000000000000000000000000000000000000000002710",
"blockNumber": "0x3d80f9",
"transactionHash": "0xd00d81dbeac63874f11146b10008eb614a0c8c550176ed1e46a5989de41f2d20",
"transactionIndex": "0x0",
"blockHash": "0x2b37241d2181dadfaa03f8a07d84cc268556782a8c73b14435b82b6e0415b6bd",
"blockTimestamp": "0x6abc30a2",
"logIndex": "0x1",
"removed": false
}
],
"logsBloom": "0x00000000000000000080000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000200000000008000000000000000000008000000000000000000000080000000002000000000000000000080000000000000000000000000000000000000000010000200000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000004000000000000020000000020000000000002000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000001000000000000000000000000000",
"status": "0x1",
"to": "0x8215ba247a3574af8ebc36606eb437811e318fbd",
"transactionHash": "0xd00d81dbeac63874f11146b10008eb614a0c8c550176ed1e46a5989de41f2d20",
"transactionIndex": "0x0",
"type": "0x2"
},
"0xdc654e77fb993a7434b1146fdb94a45ad592eaa42e7099bf0df7db1eae3271ec": {
"blockHash": "0x4b82db2558d02fecb52d54d41c78082505dcaf4d7dd92f65d91d68d568e93da0",
"blockNumber": "0x3d8101",
"contractAddress": null,
"cumulativeGasUsed": "0x101f8",
"from": "0x50f2a153be2c248b7050914a08f4f6f4498c4e48",
"gasUsed": "0x101f8",
"effectiveGasPrice": "0x3b9aca00",
"logs": [
{
"address": "0x8215ba247a3574af8ebc36606eb437811e318fbd",
"topics": [
"0x98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a5",
"0x000000000000000000000000d07ff8b519edf9c2ebecfe230602fb4598334a37",
"0x8285c4690c836b1196d71a44dbca6859a603e8a66baf993c542c58c5f243c8de"
],
"data": "0x",
"blockNumber": "0x3d8101",
"transactionHash": "0xdc654e77fb993a7434b1146fdb94a45ad592eaa42e7099bf0df7db1eae3271ec",
"transactionIndex": "0x0",
"blockHash": "0x4b82db2558d02fecb52d54d41c78082505dcaf4d7dd92f65d91d68d568e93da0",
"blockTimestamp": "0x6abc30a6",
"logIndex": "0x0",
"removed": false
},
{
"address": "0x8215ba247a3574af8ebc36606eb437811e318fbd",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000d07ff8b519edf9c2ebecfe230602fb4598334a37",
"0x0000000000000000000000007bdf94f6de68720a494917fec1114745da71cb9e"
],
"data": "0x0000000000000000000000000000000000000000000000000000000000002710",
"blockNumber": "0x3d8101",
"transactionHash": "0xdc654e77fb993a7434b1146fdb94a45ad592eaa42e7099bf0df7db1eae3271ec",
"transactionIndex": "0x0",
"blockHash": "0x4b82db2558d02fecb52d54d41c78082505dcaf4d7dd92f65d91d68d568e93da0",
"blockTimestamp": "0x6abc30a6",
"logIndex": "0x1",
"removed": false
}
],
"logsBloom": "0x00000000000000000080000000000000000000000100000000000000000000000000000000000000200000000000000000000000000000000000000200000000008000000000200000000008000000000000000000000080000000002000000000000000000080000000000000000000000000000000000000000010000000000000000000000000000000000000000004000000000000004000000000000000000000000000000000000000000000000004000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000001000000000000000000000000000",
"status": "0x1",
"to": "0x8215ba247a3574af8ebc36606eb437811e318fbd",
"transactionHash": "0xdc654e77fb993a7434b1146fdb94a45ad592eaa42e7099bf0df7db1eae3271ec",
"transactionIndex": "0x0",
"type": "0x2"
},
"0xe01fe7e8cf63237a2fc1fd58a416c84ce051bb372a806bcf228f843f87699a55": {
"blockHash": "0x53e86d6e001807b5b02bde5c3bf1378e9a724035e63779390a4af904aaedaf8b",
"blockNumber": "0x3d810a",
"contractAddress": null,
"cumulativeGasUsed": "0x101f8",
"from": "0x50f2a153be2c248b7050914a08f4f6f4498c4e48",
"gasUsed": "0x101f8",
"effectiveGasPrice": "0x3b9aca00",
"logs": [
{
"address": "0x8215ba247a3574af8ebc36606eb437811e318fbd",
"topics": [
"0x98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a5",
"0x000000000000000000000000d07ff8b519edf9c2ebecfe230602fb4598334a37",
"0x6eb5c75028a636c1fa1cde1bb84b1bd4552b0ddebf254f75cbfd9b0279ce91a6"
],
"data": "0x",
"blockNumber": "0x3d810a",
"transactionHash": "0xe01fe7e8cf63237a2fc1fd58a416c84ce051bb372a806bcf228f843f87699a55",
"transactionIndex": "0x0",
"blockHash": "0x53e86d6e001807b5b02bde5c3bf1378e9a724035e63779390a4af904aaedaf8b",
"blockTimestamp": "0x6abc30ab",
"logIndex": "0x0",
"removed": false
},
{
"address": "0x8215ba247a3574af8ebc36606eb437811e318fbd",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000d07ff8b519edf9c2ebecfe230602fb4598334a37",
"0x0000000000000000000000007bdf94f6de68720a494917fec1114745da71cb9e"
],
"data": "0x0000000000000000000000000000000000000000000000000000000000002710",
"blockNumber": "0x3d810a",
"transactionHash": "0xe01fe7e8cf63237a2fc1fd58a416c84ce051bb372a806bcf228f843f87699a55",
"transactionIndex": "0x0",
"blockHash": "0x53e86d6e001807b5b02bde5c3bf1378e9a724035e63779390a4af904aaedaf8b",
"blockTimestamp": "0x6abc30ab",
"logIndex": "0x1",
"removed": false
}
],
"logsBloom": "0x00000000000000000080000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000200000000008000000000000000000008000000000000000000000080000000002000000000000001000080000000000000000000000000000000000000000010000000000000000000000000000000000000008000000000000000004020000000000000000000000000000000000000000000000004000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000001000000000000000000000000000",
"status": "0x1",
"to": "0x8215ba247a3574af8ebc36606eb437811e318fbd",
"transactionHash": "0xe01fe7e8cf63237a2fc1fd58a416c84ce051bb372a806bcf228f843f87699a55",
"transactionIndex": "0x0",
"type": "0x2"
}
},
"transfersOut": [
{
"logIndex": "0x1",
"removed": false,
"blockNumber": "0x3d80f9",
"blockHash": "0x2b37241d2181dadfaa03f8a07d84cc268556782a8c73b14435b82b6e0415b6bd",
"blockTimestamp": "0x6abc30a2",
"transactionHash": "0xd00d81dbeac63874f11146b10008eb614a0c8c550176ed1e46a5989de41f2d20",
"transactionIndex": "0x0",
"address": "0x8215ba247a3574af8ebc36606eb437811e318fbd",
"data": "0x0000000000000000000000000000000000000000000000000000000000002710",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000d07ff8b519edf9c2ebecfe230602fb4598334a37",
"0x0000000000000000000000007bdf94f6de68720a494917fec1114745da71cb9e"
]
},
{
"logIndex": "0x1",
"removed": false,
"blockNumber": "0x3d8101",
"blockHash": "0x4b82db2558d02fecb52d54d41c78082505dcaf4d7dd92f65d91d68d568e93da0",
"blockTimestamp": "0x6abc30a6",
"transactionHash": "0xdc654e77fb993a7434b1146fdb94a45ad592eaa42e7099bf0df7db1eae3271ec",
"transactionIndex": "0x0",
"address": "0x8215ba247a3574af8ebc36606eb437811e318fbd",
"data": "0x0000000000000000000000000000000000000000000000000000000000002710",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000d07ff8b519edf9c2ebecfe230602fb4598334a37",
"0x0000000000000000000000007bdf94f6de68720a494917fec1114745da71cb9e"
]
},
{
"logIndex": "0x1",
"removed": false,
"blockNumber": "0x3d810a",
"blockHash": "0x53e86d6e001807b5b02bde5c3bf1378e9a724035e63779390a4af904aaedaf8b",
"blockTimestamp": "0x6abc30ab",
"transactionHash": "0xe01fe7e8cf63237a2fc1fd58a416c84ce051bb372a806bcf228f843f87699a55",
"transactionIndex": "0x0",
"address": "0x8215ba247a3574af8ebc36606eb437811e318fbd",
"data": "0x0000000000000000000000000000000000000000000000000000000000002710",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000d07ff8b519edf9c2ebecfe230602fb4598334a37",
"0x0000000000000000000000007bdf94f6de68720a494917fec1114745da71cb9e"
]
}
]
}

View File

@ -0,0 +1,206 @@
{
"v": 1,
"recordedAt": "2026-09-29T21:29:57.375Z",
"rpc": "https://testnet-rpc.aere.network",
"chainId": "0x6d61",
"receipts": {
"0x5517ca46b81e474b73ff45476d7b70eceec22be963ef734c651d1edd24d041a4": {
"blockHash": "0x284f0e5cc02f6d1caa6accb3e03fc33e7af9ac71fd9d0e72a8ab7ef95c79977a",
"blockNumber": "0x3d7baa",
"contractAddress": null,
"cumulativeGasUsed": "0x169c7",
"from": "0x50f2a153be2c248b7050914a08f4f6f4498c4e48",
"gasUsed": "0x169c7",
"effectiveGasPrice": "0x3b9aca00",
"logs": [
{
"address": "0x8215ba247a3574af8ebc36606eb437811e318fbd",
"topics": [
"0x98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a5",
"0x000000000000000000000000b148fe63bfaf760a3b741ce0b68ef1b9a04a7826",
"0xf3355acc270b45363d4394d6368731469a7d80b28a3fa118d93a80dff251310c"
],
"data": "0x",
"blockNumber": "0x3d7baa",
"transactionHash": "0x5517ca46b81e474b73ff45476d7b70eceec22be963ef734c651d1edd24d041a4",
"transactionIndex": "0x0",
"blockHash": "0x284f0e5cc02f6d1caa6accb3e03fc33e7af9ac71fd9d0e72a8ab7ef95c79977a",
"blockTimestamp": "0x6abc2db4",
"logIndex": "0x0",
"removed": false
},
{
"address": "0x8215ba247a3574af8ebc36606eb437811e318fbd",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000b148fe63bfaf760a3b741ce0b68ef1b9a04a7826",
"0x000000000000000000000000388e8ed4eaa0d51ce0e3c228c7514a3e64c434ca"
],
"data": "0x0000000000000000000000000000000000000000000000000000000000002710",
"blockNumber": "0x3d7baa",
"transactionHash": "0x5517ca46b81e474b73ff45476d7b70eceec22be963ef734c651d1edd24d041a4",
"transactionIndex": "0x0",
"blockHash": "0x284f0e5cc02f6d1caa6accb3e03fc33e7af9ac71fd9d0e72a8ab7ef95c79977a",
"blockTimestamp": "0x6abc2db4",
"logIndex": "0x1",
"removed": false
}
],
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000200000000000000000040000080000008000000000000000000000080000000000000000000000000000080000000000000002000000000000000000000000010000000000000002000000000000000000000000008000000000000004000000000400000000000000000000000000000000000000004000000000000000000040000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000004000000000",
"status": "0x1",
"to": "0x8215ba247a3574af8ebc36606eb437811e318fbd",
"transactionHash": "0x5517ca46b81e474b73ff45476d7b70eceec22be963ef734c651d1edd24d041a4",
"transactionIndex": "0x0",
"type": "0x2"
},
"0x8d709c307943baecb9223cb32f3f5f1f3e4f1a6442925d60f1be470a02821675": {
"blockHash": "0x998310a4704bad6815bd9e0be054a71fc15b2df7d88f29c12fab26de3bf2261d",
"blockNumber": "0x3d7bb5",
"contractAddress": null,
"cumulativeGasUsed": "0x126ef",
"from": "0x50f2a153be2c248b7050914a08f4f6f4498c4e48",
"gasUsed": "0x126ef",
"effectiveGasPrice": "0x3b9aca00",
"logs": [
{
"address": "0x8215ba247a3574af8ebc36606eb437811e318fbd",
"topics": [
"0x98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a5",
"0x000000000000000000000000b148fe63bfaf760a3b741ce0b68ef1b9a04a7826",
"0xdc6d51611ec4debb1fb46620288c02390a84fcbcb6506f4f7c71c31c9b4b71ef"
],
"data": "0x",
"blockNumber": "0x3d7bb5",
"transactionHash": "0x8d709c307943baecb9223cb32f3f5f1f3e4f1a6442925d60f1be470a02821675",
"transactionIndex": "0x0",
"blockHash": "0x998310a4704bad6815bd9e0be054a71fc15b2df7d88f29c12fab26de3bf2261d",
"blockTimestamp": "0x6abc2dba",
"logIndex": "0x0",
"removed": false
},
{
"address": "0x8215ba247a3574af8ebc36606eb437811e318fbd",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000b148fe63bfaf760a3b741ce0b68ef1b9a04a7826",
"0x000000000000000000000000388e8ed4eaa0d51ce0e3c228c7514a3e64c434ca"
],
"data": "0x0000000000000000000000000000000000000000000000000000000000002710",
"blockNumber": "0x3d7bb5",
"transactionHash": "0x8d709c307943baecb9223cb32f3f5f1f3e4f1a6442925d60f1be470a02821675",
"transactionIndex": "0x0",
"blockHash": "0x998310a4704bad6815bd9e0be054a71fc15b2df7d88f29c12fab26de3bf2261d",
"blockTimestamp": "0x6abc2dba",
"logIndex": "0x1",
"removed": false
}
],
"logsBloom": "0x00000000000000000000000000000000000000000000020000000000000800000000000000000000000000000000000000000000000000000000000200000000000000000000000000000008000000000000000000000080000000000000000000000000000080000000000000000000000000000000000000000010002000000000002000000000000000000000000008000000000000004000000000400000000000000000000000000000000000000004000000000000000000040000100000000002000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000004000000000",
"status": "0x1",
"to": "0x8215ba247a3574af8ebc36606eb437811e318fbd",
"transactionHash": "0x8d709c307943baecb9223cb32f3f5f1f3e4f1a6442925d60f1be470a02821675",
"transactionIndex": "0x0",
"type": "0x2"
},
"0x4c0779db602945d787f2b795a803922b14030da67bfaa689103b85e344dd12ac": {
"blockHash": "0x838535ad20ad0f21a5b0af47bcb50cf9d6f6c5563f5e29ee5e081b9f7372eb6d",
"blockNumber": "0x3d7bbd",
"contractAddress": null,
"cumulativeGasUsed": "0x126fb",
"from": "0x50f2a153be2c248b7050914a08f4f6f4498c4e48",
"gasUsed": "0x126fb",
"effectiveGasPrice": "0x3b9aca00",
"logs": [
{
"address": "0x8215ba247a3574af8ebc36606eb437811e318fbd",
"topics": [
"0x98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a5",
"0x000000000000000000000000b148fe63bfaf760a3b741ce0b68ef1b9a04a7826",
"0x3a059e188e3220d176e7d84f6b1c3adecffb595713deb266b2a9f4f9b863ca1c"
],
"data": "0x",
"blockNumber": "0x3d7bbd",
"transactionHash": "0x4c0779db602945d787f2b795a803922b14030da67bfaa689103b85e344dd12ac",
"transactionIndex": "0x0",
"blockHash": "0x838535ad20ad0f21a5b0af47bcb50cf9d6f6c5563f5e29ee5e081b9f7372eb6d",
"blockTimestamp": "0x6abc2dbf",
"logIndex": "0x0",
"removed": false
},
{
"address": "0x8215ba247a3574af8ebc36606eb437811e318fbd",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000b148fe63bfaf760a3b741ce0b68ef1b9a04a7826",
"0x000000000000000000000000388e8ed4eaa0d51ce0e3c228c7514a3e64c434ca"
],
"data": "0x0000000000000000000000000000000000000000000000000000000000002710",
"blockNumber": "0x3d7bbd",
"transactionHash": "0x4c0779db602945d787f2b795a803922b14030da67bfaa689103b85e344dd12ac",
"transactionIndex": "0x0",
"blockHash": "0x838535ad20ad0f21a5b0af47bcb50cf9d6f6c5563f5e29ee5e081b9f7372eb6d",
"blockTimestamp": "0x6abc2dbf",
"logIndex": "0x1",
"removed": false
}
],
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000200000002000000000000000000000008000000000000000000000080000000000000000000000000000080000000000000000000000001000000000000000010000000000000002000000000000000000000000008000000000000004000000000400000000000000000000000000000000000000004000000000000000000040000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000200000000000000004000000000",
"status": "0x1",
"to": "0x8215ba247a3574af8ebc36606eb437811e318fbd",
"transactionHash": "0x4c0779db602945d787f2b795a803922b14030da67bfaa689103b85e344dd12ac",
"transactionIndex": "0x0",
"type": "0x2"
}
},
"transfersOut": [
{
"logIndex": "0x1",
"removed": false,
"blockNumber": "0x3d7baa",
"blockHash": "0x284f0e5cc02f6d1caa6accb3e03fc33e7af9ac71fd9d0e72a8ab7ef95c79977a",
"blockTimestamp": "0x6abc2db4",
"transactionHash": "0x5517ca46b81e474b73ff45476d7b70eceec22be963ef734c651d1edd24d041a4",
"transactionIndex": "0x0",
"address": "0x8215ba247a3574af8ebc36606eb437811e318fbd",
"data": "0x0000000000000000000000000000000000000000000000000000000000002710",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000b148fe63bfaf760a3b741ce0b68ef1b9a04a7826",
"0x000000000000000000000000388e8ed4eaa0d51ce0e3c228c7514a3e64c434ca"
]
},
{
"logIndex": "0x1",
"removed": false,
"blockNumber": "0x3d7bb5",
"blockHash": "0x998310a4704bad6815bd9e0be054a71fc15b2df7d88f29c12fab26de3bf2261d",
"blockTimestamp": "0x6abc2dba",
"transactionHash": "0x8d709c307943baecb9223cb32f3f5f1f3e4f1a6442925d60f1be470a02821675",
"transactionIndex": "0x0",
"address": "0x8215ba247a3574af8ebc36606eb437811e318fbd",
"data": "0x0000000000000000000000000000000000000000000000000000000000002710",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000b148fe63bfaf760a3b741ce0b68ef1b9a04a7826",
"0x000000000000000000000000388e8ed4eaa0d51ce0e3c228c7514a3e64c434ca"
]
},
{
"logIndex": "0x1",
"removed": false,
"blockNumber": "0x3d7bbd",
"blockHash": "0x838535ad20ad0f21a5b0af47bcb50cf9d6f6c5563f5e29ee5e081b9f7372eb6d",
"blockTimestamp": "0x6abc2dbf",
"transactionHash": "0x4c0779db602945d787f2b795a803922b14030da67bfaa689103b85e344dd12ac",
"transactionIndex": "0x0",
"address": "0x8215ba247a3574af8ebc36606eb437811e318fbd",
"data": "0x0000000000000000000000000000000000000000000000000000000000002710",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000b148fe63bfaf760a3b741ce0b68ef1b9a04a7826",
"0x000000000000000000000000388e8ed4eaa0d51ce0e3c228c7514a3e64c434ca"
]
}
],
"code": "0x6080604052600436101561001257600080fd5b6000803560e01c80631626ba7e146100ca57806338eab6b9146100855763f4e2592b1461003e57600080fd5b346100825780600319360112610082576040517f0000000000000000000000009c32bab9c0d9c992166f8c0994770806562c082e6001600160a01b03168152602090f35b80fd5b50346100825780600319360112610082576040517f0000000000000000000000000852c8fc71ec6f632af628442a0efa1294612ce56001600160a01b03168152602090f35b5034610082576040366003190112610082576001600160401b03906024359082821161008257366023830112156100825781600401359283116100825736602484840101116100825760206101258460248501600435610193565b6040516001600160e01b03199091168152f35b60405191929190608082016001600160401b0381118382101761017d57604052819360418352604182011161017857816041606192602060009501370152565b600080fd5b634e487b7160e01b600052604160045260246000fd5b90916082810361026b5780604111610178576101b86101b23685610138565b836102b3565b600581101561025557159081159161027b575b5061026b57608211610178576101e86101ee926041369101610138565b906102b3565b600581101561025557159081159161021d575b5061021157630b135d3f60e11b90565b6001600160e01b031990565b7f0000000000000000000000000852c8fc71ec6f632af628442a0efa1294612ce56001600160a01b0390811691161415905038610201565b634e487b7160e01b600052602160045260246000fd5b506001600160e01b031992915050565b7f0000000000000000000000009c32bab9c0d9c992166f8c0994770806562c082e6001600160a01b03908116911614159050386101cb565b9060418151146000146102e1576102dd916020820151906060604084015193015160001a906102eb565b9091565b5050600090600290565b9291906fa2a8918ca85bafe22016d0b997e4df60600160ff1b0383116103645791608094939160ff602094604051948552168484015260408301526060820152600093849182805260015afa156103575781516001600160a01b03811615610351579190565b50600190565b50604051903d90823e3d90fd5b5050505060009060039056fea2646970667358221220c1859b9ad2a87f5981b6e6ade9d1980dabedd70235a69d674774c6603ec6ceac64736f6c63430008170033"
}

View File

@ -5,7 +5,9 @@
import http from 'node:http';
import { incarcaEthers, EIP3009_TYPES } from './wallet.mjs';
export function createLocalFacilitator({ network, token, balances = {} }) {
// `contracts`: portofele-contract 2-din-2 emulate (ERC-1271 ca in AereAgentWallet2of2.sol): { [adresa]: { agentSigner, policySigner } };
// logica adevarata a contractului o proba hardhat (contracts/test/AereAgentWallet2of2.test.js) si testnetul
export function createLocalFacilitator({ network, token, balances = {}, contracts = {} }) {
const ethers = incarcaEthers();
const domeniu = { name: token.name, version: token.version, chainId: Number(network.split(':')[1]), verifyingContract: ethers.getAddress(token.address) };
const sold = new Map(Object.entries(balances).map(([a, v]) => [a.toLowerCase(), BigInt(v)]));
@ -23,8 +25,16 @@ export function createLocalFacilitator({ network, token, balances = {} }) {
if (acum <= BigInt(a.validAfter)) return { ok: false, reason: 'authorization_not_yet_valid', payer: a.from };
if (acum + 6n >= BigInt(a.validBefore)) return { ok: false, reason: 'authorization_expired', payer: a.from };
let semnatar = null;
try { semnatar = ethers.verifyTypedData(domeniu, EIP3009_TYPES, { ...a, value: BigInt(a.value), validAfter: BigInt(a.validAfter), validBefore: BigInt(a.validBefore) }, sig); } catch { semnatar = null; }
if (!semnatar || semnatar.toLowerCase() !== String(a.from).toLowerCase()) return { ok: false, reason: 'invalid_signature', payer: a.from };
const mesaj = { ...a, value: BigInt(a.value), validAfter: BigInt(a.validAfter), validBefore: BigInt(a.validBefore) };
try { semnatar = ethers.verifyTypedData(domeniu, EIP3009_TYPES, mesaj, sig); } catch { semnatar = null; }
let valid = !!semnatar && semnatar.toLowerCase() === String(a.from).toLowerCase();
const k = Object.entries(contracts).find(([adr]) => adr.toLowerCase() === String(a.from).toLowerCase());
if (!valid && k && /^0x[0-9a-fA-F]{260}$/.test(String(sig))) {
const digest = ethers.TypedDataEncoder.hash(domeniu, EIP3009_TYPES, mesaj);
const rec = (h) => { try { return ethers.recoverAddress(digest, h).toLowerCase(); } catch { return null; } };
valid = rec(ethers.dataSlice(sig, 0, 65)) === k[1].agentSigner.toLowerCase() && rec(ethers.dataSlice(sig, 65, 130)) === k[1].policySigner.toLowerCase();
}
if (!valid) return { ok: false, reason: 'invalid_signature', payer: a.from };
if (folosite.has(`${a.from.toLowerCase()}:${a.nonce}`)) return { ok: false, reason: 'nonce_already_used', payer: a.from };
if ((sold.get(a.from.toLowerCase()) || 0n) < BigInt(a.value)) return { ok: false, reason: 'insufficient_funds', payer: a.from };
return { ok: true, payer: a.from, a };

View File

@ -1,6 +1,6 @@
#!/usr/bin/env node
// inregistreaza-rpc.mjs: scoate de pe lant, O DATA, raspunsurile RPC de care are nevoie verifica-plati.mjs pentru un dosar-dovada
// (eth_chainId, chitanta fiecarei plati, Transfer-urile din portofel), ca proba verificatorului sa ruleze apoi fara retea pe date REALE.
// (eth_chainId, chitanta fiecarei plati, Transfer-urile din portofel, si codul portofelului cand e un contract 2-din-2), ca proba verificatorului sa ruleze apoi fara retea pe date REALE.
// node inregistreaza-rpc.mjs <dovada.json> --rpc <url> --out <rpc-inregistrat.json>
import fs from 'node:fs';
import { incarcaEthers } from './wallet.mjs';
@ -16,5 +16,6 @@ const receipts = {};
for (const p of d.payments) receipts[p.transaction.toLowerCase()] = await apel('eth_getTransactionReceipt', [p.transaction]);
const logs = await apel('eth_getLogs', [{ address: d.token, fromBlock: '0x' + Number(d.fromBlock).toString(16), toBlock: 'latest',
topics: [ethers.id('Transfer(address,address,uint256)'), '0x' + '0'.repeat(24) + d.wallet.toLowerCase().slice(2)] }]);
fs.writeFileSync(out, JSON.stringify({ v: 1, recordedAt: new Date().toISOString(), rpc, chainId, receipts, transfersOut: logs }, null, 1) + '\n');
console.log(`recorded chain ${Number(chainId)}: ${Object.keys(receipts).length} receipts, ${logs.length} transfers out of the wallet -> ${out}`);
const code = d.walletContract ? await apel('eth_getCode', [d.wallet, 'latest']) : undefined;
fs.writeFileSync(out, JSON.stringify({ v: 1, recordedAt: new Date().toISOString(), rpc, chainId, receipts, transfersOut: logs, ...(code ? { code } : {}) }, null, 1) + '\n');
console.log(`recorded chain ${Number(chainId)}: ${Object.keys(receipts).length} receipts, ${logs.length} transfers out of the wallet${code ? `, the wallet code (${(code.length - 2) / 2} bytes)` : ''} -> ${out}`);

View File

@ -0,0 +1,145 @@
#!/usr/bin/env node
// Proba cap la cap a portofelului-contract 2-din-2 pe testnetul PUBLIC 28001 (2026-09-29, punctele 23, 25): banii agentului stau intr-un
// AereAgentWallet2of2 desfasurat aici, si ies numai cu semnatura agentului SI a serviciului de politica, prin facilitatorul x402 al
// testnetului (ERC-1271) si prin tokenul EIP-3009 (SignatureChecker).
// 1. contractul se desfasoara din artefactul publicat (AereAgentWallet2of2.json), cu doua chei NOI tinute numai in memorie (agentul,
// serviciul de politica); codul de pe lant trebuie sa fie EXACT codul compilat cu cei doi semnatari;
// 2. primeste 0,05 tUSD de la cheia de dezvoltator a testnetului;
// 3. contractul, masurat pe lant (eth_call isValidSignature): DA numai pentru agent || politica; NU pentru fiecare jumatate singura,
// dublata, in ordine inversa, sau cu o cheie straina;
// 4. trei cumparaturi de 0,01 platite din contract si servite; a patra refuzata de politica;
// 5. ATACURI prin facilitatorul testnetului: agentul singur si serviciul de politica singur nu pot plati (402); si TOKENUL insusi,
// intrebat pe lant fara facilitator (eth_call transferWithAuthorization): refuza jumatatea agentului, primeste perechea;
// 6. pe lant: soldurile, nonce-urile folosite (sha256 al intrarii), iar nonce-ul atacului nefolosit;
// 7. dosarul-dovada (cu walletContract) verificat cu verifica-plati.mjs: VALID, inclusiv codul contractului; un dosar care numeste
// alt semnatar de politica: INVALID.
// Tranzactii trimise de proba: desfasurarea si finantarea (cheia de dezvoltator); decontarile le plateste facilitatorul. Refuza orice
// alt lant decat 28001. Cheia de dezvoltator se citeste din AERE_TESTNET_KEY_FILE (d=<hex> sau PRIVATE_KEY=0x<hex>) si nu se tipareste.
// AERE_TESTNET_KEY_FILE=<fisier> node proba-cosign-testnet.mjs [--rpc URL] [--facilitator URL]
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { definePolicy } from '../agent-policy.mjs';
import { newAgentIdentity, openLedger } from '../agent-ledger.mjs';
import { createWallet, assetId, nonceForEntry, incarcaEthers, EIP3009_TYPES } from './wallet.mjs';
import { payWithAgent } from './client.mjs';
import { createResourceServer } from './resource-server.mjs';
import { verificaPlati } from './verifica-plati.mjs';
import { incarcaArtefact, codulAsteptat, amprentaSursei } from './contract-2of2.mjs';
const AICI = path.dirname(fileURLToPath(import.meta.url));
const a = process.argv.slice(2); const get = (f, d) => { const i = a.indexOf(f); return i >= 0 ? a[i + 1] : d; };
const RPC = get('--rpc', 'https://testnet-rpc.aere.network');
const FAC = get('--facilitator', 'https://testnet-rpc.aere.network/x402');
const NET = 'eip155:28001';
const TOKEN = { address: '0x8215bA247a3574af8EBC36606eB437811E318FBd', name: 'AereTestUSD', version: '2' };
const MAGIC = '0x1626ba7e', INVALID = '0xffffffff';
const ethers = incarcaEthers();
let ok = 0, rau = 0;
const cer = (c, ce) => { console.log((c ? ' OK ' : ' RAU ') + ce); c ? ok++ : rau++; return c; };
const taie = (s) => String(s || '').replace(/(0x)?[0-9a-fA-F]{60,}/g, '<hex>').slice(0, 160);
const kf = process.env.AERE_TESTNET_KEY_FILE;
if (!kf || !fs.existsSync(kf)) { console.log('NEMASURAT: AERE_TESTNET_KEY_FILE nu numeste un fisier cu cheia de dezvoltator a testnetului'); process.exit(2); }
const rand = fs.readFileSync(kf, 'utf8').split(/\r?\n/).map((l) => l.trim()).find((l) => /^(d|PRIVATE_KEY)=/.test(l)) || '';
const hex = rand.replace(/^(d|PRIVATE_KEY)=/, '').replace(/^0x/, '').trim();
if (!/^[0-9a-fA-F]{1,64}$/.test(hex)) { console.log('NEMASURAT: fisierul cheii nu are un rand d=<hex> sau PRIVATE_KEY=0x<hex>'); process.exit(2); }
const provider = new ethers.JsonRpcProvider(RPC, undefined, { staticNetwork: false });
const lant = Number((await provider.getNetwork()).chainId);
if (lant !== 28001) { console.log(`REFUZ: RPC-ul serveste lantul ${lant}; proba ruleaza numai pe testnetul 28001`); process.exit(2); }
const dev = new ethers.Wallet('0x' + hex.padStart(64, '0'), provider);
const art = incarcaArtefact();
const tUSD = new ethers.Contract(TOKEN.address, ['function transfer(address,uint256) returns (bool)', 'function balanceOf(address) view returns (uint256)',
'function authorizationState(address,bytes32) view returns (bool)',
'function transferWithAuthorization(address,address,uint256,uint256,uint256,bytes32,bytes)'], dev);
const DOM = { name: TOKEN.name, version: TOKEN.version, chainId: 28001, verifyingContract: TOKEN.address };
const digestul = (au) => ethers.TypedDataEncoder.hash(DOM, EIP3009_TYPES, { ...au, value: BigInt(au.value), validAfter: BigInt(au.validAfter), validBefore: BigInt(au.validBefore) });
const deschise = [];
try {
// 1. desfasurarea, din artefactul publicat
const agentEvm = ethers.Wallet.createRandom(), politica = ethers.Wallet.createRandom(), strain = ethers.Wallet.createRandom();
const payee = ethers.Wallet.createRandom().address;
const c = await new ethers.ContractFactory(art.abi, art.bytecode, dev).deploy(agentEvm.address, politica.address);
const rcD = await c.deploymentTransaction().wait(1, 120000);
const portofel = await c.getAddress();
const cod = String(await provider.getCode(portofel)).toLowerCase();
cer(rcD && rcD.status === 1 && cod === codulAsteptat(art, { agentSigner: agentEvm.address, policySigner: politica.address }),
`1. AereAgentWallet2of2 desfasurat la ${portofel} (bloc ${rcD && rcD.blockNumber}): codul de pe lant e exact codul compilat din sursa publicata (sha256 ${String(amprentaSursei(art)).slice(0, 16)}), cu cei doi semnatari`);
// 2. finantarea
const rc0 = await (await tUSD.transfer(portofel, 50000n)).wait(1, 120000);
cer(rc0 && rc0.status === 1 && (await tUSD.balanceOf(portofel)) === 50000n, `2. portofelul-contract primeste 0,05 tUSD (bloc ${rc0 && rc0.blockNumber})`);
// 3. contractul, pe lant
const w = new ethers.Contract(portofel, art.abi, provider);
const h = ethers.hexlify(ethers.randomBytes(32));
const sA = agentEvm.signingKey.sign(h).serialized, sP = politica.signingKey.sign(h).serialized, sS = strain.signingKey.sign(h).serialized;
const raspunsuri = {
'agent || politica': await w.isValidSignature(h, ethers.concat([sA, sP])),
'agentul singur (65 de octeti)': await w.isValidSignature(h, sA), 'politica singura (65 de octeti)': await w.isValidSignature(h, sP),
'agentul de doua ori': await w.isValidSignature(h, ethers.concat([sA, sA])), 'politica de doua ori': await w.isValidSignature(h, ethers.concat([sP, sP])),
'ordinea inversa': await w.isValidSignature(h, ethers.concat([sP, sA])), 'agent || cheie straina': await w.isValidSignature(h, ethers.concat([sA, sS])),
'cheie straina || politica': await w.isValidSignature(h, ethers.concat([sS, sP])),
};
const [primul, ...restul] = Object.entries(raspunsuri);
cer(primul[1] === MAGIC && restul.every(([, r]) => r === INVALID),
`3. pe lant, isValidSignature: DA numai pentru agent || politica; NU pentru ${restul.length} alte forme (${restul.filter(([, r]) => r !== INVALID).map(([k]) => k).join(', ') || 'toate refuzate'})`);
// 4. cumparaturile, prin facilitatorul testnetului
const agent = newAgentIdentity();
const { policy, policyHash } = definePolicy({ agentId: agent.agentId, spend: { amount: '30000', windowSeconds: 3600, asset: assetId(NET, TOKEN.address) },
recipients: [payee], wallet: portofel });
const serviciu = createWallet({ policy, policyHash, evmPrivateKey: politica.privateKey, contractWallet: portofel, network: NET, token: TOKEN });
const L = openLedger({ identity: agent, policy, policyHash });
const cerinta = { scheme: 'exact', network: NET, amount: '10000', asset: TOKEN.address, payTo: payee, maxTimeoutSeconds: 120, extra: { name: TOKEN.name, version: TOKEN.version } };
const rs = createResourceServer({ requirement: cerinta, facilitator: FAC, content: 'the paid content' });
const url = await rs.listen(); deschise.push(rs.server);
const plati = [];
for (let i = 0; i < 3; i++) plati.push(await payWithAgent({ url, ledger: L, wallet: serviciu, agentEvmKey: agentEvm.privateKey }));
cer(plati.every((p) => p.paid && p.status === 200 && p.body === 'the paid content' && /^0x[0-9a-f]{64}$/.test(p.settlement.transaction || '')),
`4. trei cumparaturi de 0,01 tUSD platite din portofelul 2-din-2 prin facilitatorul testnetului si servite (${plati.map((p) => p.status + (p.reason ? ' ' + taie(p.reason) : '')).join('; ')})`);
const s0 = rs.lastPayloads[0] && rs.lastPayloads[0].payload.signature;
cer(!!s0 && ethers.dataLength(s0) === 130, '4. fiecare plata poarta 130 de octeti de semnatura: jumatatea agentului, apoi a serviciului de politica');
const p4 = await payWithAgent({ url, ledger: L, wallet: serviciu, agentEvmKey: agentEvm.privateKey });
cer(!p4.paid && /policy refused.*over the limit/.test(p4.reason || '') && serviciu.status().signed === 3, `4. CONTROL: a patra depaseste 0,03 pe ora: refuzata de politica, serviciul a semnat tot 3 (${taie(p4.reason)})`);
// 5. atacurile: o autorizare noua catre vanzator, semnata de o singura parte
const acum = Math.floor(Date.now() / 1000);
const au = { from: portofel, to: payee, value: '10000', validAfter: String(acum - 5), validBefore: String(acum + 100), nonce: ethers.hexlify(ethers.randomBytes(32)) };
const d5 = digestul(au);
const a5 = agentEvm.signingKey.sign(d5).serialized, p5 = politica.signingKey.sign(d5).serialized;
const trimite = async (sig) => { const pl = { x402Version: 2, accepted: cerinta, payload: { signature: sig, authorization: au } };
const r = await fetch(url, { headers: { 'PAYMENT-SIGNATURE': Buffer.from(JSON.stringify(pl)).toString('base64') } }); return [r.status, await r.text()]; };
const [cA, tA] = await trimite(ethers.concat([a5, a5]));
cer(cA === 402 && /invalid_signature/.test(tA), `5. ATAC: agentul singur (jumatatea lui de doua ori) prin facilitatorul testnetului -> ${cA} (${taie(tA.match(/payment not valid: [a-z_]+/) || tA)})`);
const [cP, tP] = await trimite(ethers.concat([p5, p5]));
cer(cP === 402 && /invalid_signature/.test(tP), `5. ATAC: serviciul de politica singur (proprietarul) prin facilitatorul testnetului -> ${cP} (${taie(tP.match(/payment not valid: [a-z_]+/) || tP)})`);
const apelTok = (sig) => tUSD.transferWithAuthorization.staticCall(au.from, au.to, BigInt(au.value), BigInt(au.validAfter), BigInt(au.validBefore), au.nonce, sig, { from: dev.address });
let refuzAgent = false; try { await apelTok(ethers.concat([a5, a5])); } catch { refuzAgent = true; }
let refuzPol = false; try { await apelTok(ethers.concat([p5, p5])); } catch { refuzPol = true; }
let primestePerechea = false; try { await apelTok(ethers.concat([a5, p5])); primestePerechea = true; } catch { primestePerechea = false; }
cer(refuzAgent && refuzPol && primestePerechea, `5. pe lant, fara facilitator (eth_call transferWithAuthorization): tokenul refuza agentul singur (${refuzAgent}) si politica singura (${refuzPol}), primeste perechea (${primestePerechea})`);
rs.server.close();
// 6. pe lant
await new Promise((r) => setTimeout(r, 2000));
const soldPayee = await tUSD.balanceOf(payee), soldPortofel = await tUSD.balanceOf(portofel);
cer(soldPayee === 30000n && soldPortofel === 20000n, `6. pe lant: vanzatorul are 30000, portofelul-contract 20000 (${soldPayee}, ${soldPortofel})`);
const folosite = await Promise.all(plati.map((p) => tUSD.authorizationState(portofel, nonceForEntry(p.entry.hash))));
const atacFolosit = await tUSD.authorizationState(portofel, au.nonce);
cer(folosite.every(Boolean) && !atacFolosit, '6. pe lant: nonce-ul sha256(hash-ul intrarii) e folosit pentru fiecare plata; nonce-ul atacurilor nu');
// 7. dosarul-dovada, verificat din afara
const dovada = { v: 1, kind: 'aere-agent-x402-payments', network: NET, token: TOKEN.address, wallet: portofel, fromBlock: rcD.blockNumber,
walletContract: { contract: 'AereAgentWallet2of2', sourceSha256: amprentaSursei(art), agentSigner: agentEvm.address, policySigner: politica.address, deployTransaction: rcD.hash },
facilitator: FAC, policy, policyHash, ledger: L.export(),
payments: plati.map((p) => ({ entrySeq: p.entry.seq, entryHash: p.entry.hash, transaction: p.settlement.transaction })), createdAt: new Date().toISOString() };
const dir = path.join(AICI, 'dovezi-28001'); fs.mkdirSync(dir, { recursive: true });
const f = path.join(dir, `plati-agent-2of2-${dovada.createdAt.slice(0, 19).replace(/[:T]/g, '-')}.json`);
fs.writeFileSync(f, JSON.stringify(dovada, null, 1) + '\n');
const v = await verificaPlati(dovada, { rpc: RPC, allTransfers: true });
cer(v.verdict === 'VALID' && v.checks.some((x) => x.pass === true && /is the 2-of-2 contract/.test(x.name)),
`7. verifica-plati pe dosarul-dovada: ${v.verdict} (${v.checks.length} verificari, printre ele: codul portofelului e contractul 2-din-2)`);
const alt = JSON.parse(JSON.stringify(dovada)); alt.walletContract.policySigner = strain.address;
const va = await verificaPlati(alt, { rpc: RPC });
cer(va.verdict === 'INVALID', `7. CONTROL: un dosar care numeste alt semnatar de politica -> ${va.verdict} (codul de pe lant nu e cel cu acel semnatar)`);
console.log(` dosarul-dovada: ${path.relative(process.cwd(), f)}`);
} catch (e) { cer(false, `proba s-a oprit: ${taie(e.shortMessage || e.message)}`); }
finally { for (const s of deschise) { try { s.closeAllConnections && s.closeAllConnections(); s.close(); } catch { /* inchis */ } } }
console.log(`\nagent-cosign-testnet: ${ok}/${ok + rau} ${rau ? 'CU ESECURI' : 'cum trebuia'}`);
process.exitCode = rau ? 1 : 0;

View File

@ -0,0 +1,98 @@
// Proba modului cosign (portofel-contract 2-din-2, AereAgentWallet2of2.sol), fara retea: facilitatorul local emuleaza ERC-1271-ul
// contractului (logica lui adevarata o proba hardhat si testnetul). Adversarii: agentul singur, serviciul de politica singur, si un
// serviciu de politica COMPROMIS care incearca sa-l faca pe agent sa semneze alta plata decat cea din intrarea lui.
// node proba-cosign.mjs iesire 0 = toate cum trebuia
import { definePolicy } from '../agent-policy.mjs';
import { newAgentIdentity, openLedger } from '../agent-ledger.mjs';
import { createWallet, serve, assetId, x402Action, incarcaEthers, EIP3009_TYPES } from './wallet.mjs';
import { payWithAgent, completeazaCosemnarea, walletOverHttp } from './client.mjs';
import { createResourceServer } from './resource-server.mjs';
import { createLocalFacilitator } from './facilitator-local.mjs';
const ethers = incarcaEthers();
let ok = 0, rau = 0;
const cer = (c, ce) => { console.log((c ? ' OK ' : ' RAU ') + ce); c ? ok++ : rau++; };
const NET = 'eip155:28001';
const TOKEN = { address: '0x8215bA247a3574af8EBC36606eB437811E318FBd', name: 'AereTestUSD', version: '2' };
const cerinta = (payTo, amount = '10000') => ({ scheme: 'exact', network: NET, amount, asset: TOKEN.address, payTo, maxTimeoutSeconds: 60, extra: { name: TOKEN.name, version: TOKEN.version } });
const deschise = []; const inchide = (s) => { try { s.closeAllConnections && s.closeAllConnections(); s.close(); } catch { /* inchis */ } };
try {
const contract = ethers.Wallet.createRandom().address; // adresa portofelului-contract (emulat)
const agentEvm = ethers.Wallet.createRandom(), politica = ethers.Wallet.createRandom();
const payee = ethers.Wallet.createRandom().address.toLowerCase();
const agent = newAgentIdentity();
const { policy, policyHash } = definePolicy({ agentId: agent.agentId, spend: { amount: '30000', windowSeconds: 3600, asset: assetId(NET, TOKEN.address) },
recipients: [payee], wallet: contract });
const wallet = createWallet({ policy, policyHash, evmPrivateKey: politica.privateKey, contractWallet: contract, network: NET, token: TOKEN });
const fac = createLocalFacilitator({ network: NET, token: TOKEN, balances: { [contract]: '100000' }, contracts: { [contract]: { agentSigner: agentEvm.address, policySigner: politica.address } } });
const facUrl = await fac.listen(); deschise.push(fac.server);
const rs = createResourceServer({ requirement: cerinta(payee), facilitator: facUrl }); const url = await rs.listen(); deschise.push(rs.server);
const L = openLedger({ identity: agent, policy, policyHash });
const st = wallet.status();
cer(st.mode === 'cosign-2of2' && st.address.toLowerCase() === contract.toLowerCase() && st.policySigner === politica.address, '1. status: modul cosign, portofelul e contractul, semnatarul de politica numit');
// 2. trei plati, cu jumatatea agentului adaugata de client
const plati = [];
for (let i = 0; i < 3; i++) plati.push(await payWithAgent({ url, ledger: L, wallet, agentEvmKey: agentEvm.privateKey }));
cer(plati.every((p) => p.paid) && fac.balanceOf(payee) === 30000n && fac.balanceOf(contract) === 70000n, `2. trei plati din portofelul 2-din-2, fiecare cu ambele semnaturi (${plati.map((p) => p.status).join(',')})`);
const s0 = rs.lastPayloads[0].payload.signature;
const dig0 = ethers.TypedDataEncoder.hash({ name: TOKEN.name, version: TOKEN.version, chainId: 28001, verifyingContract: TOKEN.address }, EIP3009_TYPES,
{ ...rs.lastPayloads[0].payload.authorization, value: BigInt(rs.lastPayloads[0].payload.authorization.value), validAfter: BigInt(rs.lastPayloads[0].payload.authorization.validAfter), validBefore: BigInt(rs.lastPayloads[0].payload.authorization.validBefore) });
cer(ethers.dataLength(s0) === 130 && ethers.recoverAddress(dig0, ethers.dataSlice(s0, 0, 65)) === agentEvm.address && ethers.recoverAddress(dig0, ethers.dataSlice(s0, 65, 130)) === politica.address,
'2. semnatura platii are 130 de octeti: intai agentul, apoi semnatarul de politica, peste acelasi digest');
const p4 = await payWithAgent({ url, ledger: L, wallet, agentEvmKey: agentEvm.privateKey });
cer(!p4.paid && /over the limit/.test(p4.reason || ''), `2. CONTROL: a patra depaseste limita -> refuzata (${(p4.reason || '').slice(0, 60)})`);
// 3. agentul SINGUR: isi semneaza singur o autorizare catre vanzator, fara serviciu
const acum = Math.floor(Date.now() / 1000);
const a3 = { from: contract, to: ethers.getAddress(payee), value: '10000', validAfter: String(acum - 5), validBefore: String(acum + 50), nonce: ethers.hexlify(ethers.randomBytes(32)) };
const d3 = ethers.TypedDataEncoder.hash({ name: TOKEN.name, version: TOKEN.version, chainId: 28001, verifyingContract: TOKEN.address }, EIP3009_TYPES, { ...a3, value: 10000n, validAfter: BigInt(a3.validAfter), validBefore: BigInt(a3.validBefore) });
const trimite = async (sig) => { const pl = { x402Version: 2, accepted: cerinta(payee), payload: { signature: sig, authorization: a3 } };
const r = await fetch(url, { headers: { 'PAYMENT-SIGNATURE': Buffer.from(JSON.stringify(pl)).toString('base64') } }); return [r.status, await r.text()]; };
const [c3, t3] = await trimite(agentEvm.signingKey.sign(d3).serialized);
const [c3b, t3b] = await trimite(ethers.concat([agentEvm.signingKey.sign(d3).serialized, agentEvm.signingKey.sign(d3).serialized]));
cer(c3 === 402 && /invalid_signature/.test(t3) && c3b === 402 && /invalid_signature/.test(t3b), '3. ATAC: agentul singur (o jumatate, sau jumatatea lui de doua ori) nu poate plati din portofel');
// 4. serviciul de politica SINGUR
const [c4, t4] = await trimite(ethers.concat([politica.signingKey.sign(d3).serialized, politica.signingKey.sign(d3).serialized]));
cer(c4 === 402 && /invalid_signature/.test(t4) && fac.balanceOf(payee) === 30000n, '4. ATAC: serviciul de politica singur nu poate plati din portofel');
// 5. serviciul de politica COMPROMIS: raspunsuri falsificate, fiecare semnat consecvent cu cheia lui; agentul trebuie sa refuze
const A = newAgentIdentity(); const pay2 = ethers.Wallet.createRandom().address.toLowerCase(); const atacator = ethers.Wallet.createRandom().address;
const p5 = definePolicy({ agentId: A.agentId, spend: { amount: '1000000', windowSeconds: 3600, asset: assetId(NET, TOKEN.address) }, wallet: contract });
const W5 = createWallet({ policy: p5.policy, policyHash: p5.policyHash, evmPrivateKey: politica.privateKey, contractWallet: contract, network: NET, token: TOKEN });
const L5 = openLedger({ identity: A, policy: p5.policy, policyHash: p5.policyHash });
const req5 = cerinta(pay2); const e5 = L5.record(x402Action(contract, { url: 'http://x/5' }, req5)).entry;
const bun = await W5.authorize({ requirements: req5, resource: { url: 'http://x/5' }, ledger: L5.export() });
const dom = { name: TOKEN.name, version: TOKEN.version, chainId: 28001, verifyingContract: TOKEN.address };
const refa = (schimba, cheie = politica) => { const pl = JSON.parse(JSON.stringify(bun.paymentPayload)); schimba(pl.payload.authorization);
const au = pl.payload.authorization; const dg = ethers.TypedDataEncoder.hash(dom, EIP3009_TYPES, { ...au, value: BigInt(au.value), validAfter: BigInt(au.validAfter), validBefore: BigInt(au.validBefore) });
return { payload: pl, cosign: { digest: dg, policySignature: cheie.signingKey.sign(dg).serialized } }; };
const judeca = (x) => completeazaCosemnarea({ payload: x.payload, req: req5, status: W5.status(), entryHash: e5.hash, agentEvmKey: agentEvm.privateKey, cosign: x.cosign });
cer(bun.ok && judeca({ payload: bun.paymentPayload, cosign: bun.cosign }).ok, '5. CONTROL: raspunsul cinstit al serviciului e completat de agent');
cer(/another recipient or amount/.test(judeca(refa((a) => { a.to = atacator; })).error || ''), '5. ATAC: serviciul schimba destinatarul -> agentul refuza sa semneze');
cer(/not from the 2-of-2 wallet/.test(judeca(refa((a) => { a.from = atacator; })).error || ''), '5. ATAC: serviciul schimba platitorul (alt portofel) -> agentul refuza');
cer(/another recipient or amount/.test(judeca(refa((a) => { a.value = '999999'; })).error || ''), '5. ATAC: serviciul schimba suma -> agentul refuza');
cer(/nonce is not/.test(judeca(refa((a) => { a.nonce = ethers.hexlify(ethers.randomBytes(32)); })).error || ''), "5. ATAC: serviciul pune alt nonce decat intrarea agentului -> agentul refuza");
cer(/validity/.test(judeca(refa((a) => { a.validBefore = String(Number(a.validBefore) + 86400); })).error || ''), '5. ATAC: serviciul lungeste termenul autorizarii cu o zi -> agentul refuza');
cer(/digest the wallet signed/.test(judeca({ payload: bun.paymentPayload, cosign: { ...bun.cosign, digest: '0x' + 'ab'.repeat(32) } }).error || ''), '5. ATAC: serviciul declara alt digest decat cel pe care il calculeaza agentul -> agentul refuza');
cer(/not signed by the declared policy signer/.test(judeca(refa(() => {}, ethers.Wallet.createRandom())).error || ''), '5. ATAC: jumatatea serviciului semnata de alta cheie decat semnatarul declarat -> agentul refuza');
// 6. fara cheia agentului, clientul se opreste cu motivul
const B = newAgentIdentity(); const p6 = definePolicy({ agentId: B.agentId, spend: { amount: '100000', windowSeconds: 3600, asset: assetId(NET, TOKEN.address) }, recipients: [payee], wallet: contract });
const W6 = createWallet({ policy: p6.policy, policyHash: p6.policyHash, evmPrivateKey: politica.privateKey, contractWallet: contract, network: NET, token: TOKEN });
const r6 = await payWithAgent({ url, ledger: openLedger({ identity: B, policy: p6.policy, policyHash: p6.policyHash }), wallet: W6 });
cer(!r6.paid && /agentEvmKey is missing/.test(r6.reason || ''), `6. CONTROL: fara cheia agentului, un portofel 2-din-2 nu poate plati, si clientul spune de ce (${(r6.reason || '').slice(0, 60)})`);
// 7. serviciul cosign prin HTTP (wallet.mjs serve + walletOverHttp), cap la cap
const C7 = newAgentIdentity(); const p7 = definePolicy({ agentId: C7.agentId, spend: { amount: '10000', windowSeconds: 3600, asset: assetId(NET, TOKEN.address) }, recipients: [payee], wallet: contract });
const W7 = createWallet({ policy: p7.policy, policyHash: p7.policyHash, evmPrivateKey: politica.privateKey, contractWallet: contract, network: NET, token: TOKEN });
const srv7 = await serve(W7, { port: 0 }); deschise.push(srv7);
const H7 = walletOverHttp(`http://127.0.0.1:${srv7.address().port}`);
const st7 = await H7.status();
const r7 = await payWithAgent({ url, ledger: openLedger({ identity: C7, policy: p7.policy, policyHash: p7.policyHash }), wallet: H7, agentEvmKey: agentEvm.privateKey });
cer(st7.mode === 'cosign-2of2' && st7.policySigner === politica.address && r7.paid && fac.balanceOf(payee) === 40000n,
`7. prin HTTP: /status spune modul si semnatarul de politica, iar plata cu jumatatea agentului trece (${r7.status}${r7.reason ? ' ' + r7.reason.slice(0, 60) : ''})`);
} catch (e) { cer(false, `proba s-a oprit: ${String(e.message || e).slice(0, 160)}`); } finally { for (const s of deschise) inchide(s); }
console.log(`\nagent-cosign: ${ok}/${ok + rau} ${rau ? 'CU ESECURI' : 'cum trebuia'}`);
process.exitCode = rau ? 1 : 0;

View File

@ -1,5 +1,6 @@
// Proba verificatorului de plati (verifica-plati.mjs), fara retea: un RPC local care serveste raspunsurile INREGISTRATE de pe testnetul
// 28001 pentru dosarul-dovada din dovezi-28001/ (inregistreaza-rpc.mjs), deci date reale de pe lant. Fiecare verificare are cazul ei
// 28001 pentru cele doua dosare-dovada din dovezi-28001/ (portofelul EOA si portofelul-contract 2-din-2, inregistreaza-rpc.mjs), deci
// date reale de pe lant. Fiecare verificare are cazul ei
// care trebuie sa o inroseasca, construit din datele reale schimbate intr-un singur loc.
// node proba-verifica-plati.mjs iesire 0 = toate cum trebuia
import fs from 'node:fs';
@ -10,13 +11,17 @@ import { verificaPlati } from './verifica-plati.mjs';
const AICI = path.dirname(fileURLToPath(import.meta.url));
const D = path.join(AICI, 'dovezi-28001');
const dovadaF = fs.readdirSync(D).filter((n) => n.startsWith('plati-agent-') && n.endsWith('.json')).sort().pop();
const inregF = fs.readdirSync(D).filter((n) => n.startsWith('rpc-inregistrat-') && n.endsWith('.json')).sort().pop();
// doua rulari pe 28001: portofelul EOA (plati-agent-<data>) si portofelul-contract 2-din-2 (plati-agent-2of2-<data>), fiecare cu inregistrarea ei
const ultimul = (re) => fs.readdirSync(D).filter((n) => re.test(n)).sort().pop();
const dovadaF = ultimul(/^plati-agent-\d.*\.json$/), inregF = ultimul(/^rpc-inregistrat-\d.*\.json$/);
const dovada2F = ultimul(/^plati-agent-2of2-.*\.json$/), inreg2F = ultimul(/^rpc-inregistrat-2of2-.*\.json$/);
let ok = 0, rau = 0;
const cer = (c, ce) => { console.log((c ? ' OK ' : ' RAU ') + ce); c ? ok++ : rau++; };
if (!dovadaF || !inregF) { console.log(`NEMASURAT: lipseste dosarul-dovada sau inregistrarea RPC in ${D}`); process.exit(2); }
if (!dovadaF || !inregF || !dovada2F || !inreg2F) { console.log(`NEMASURAT: lipseste un dosar-dovada sau o inregistrare RPC in ${D}`); process.exit(2); }
const dovada = JSON.parse(fs.readFileSync(path.join(D, dovadaF), 'utf8'));
const inreg = JSON.parse(fs.readFileSync(path.join(D, inregF), 'utf8'));
const dovada2 = JSON.parse(fs.readFileSync(path.join(D, dovada2F), 'utf8'));
const inreg2 = JSON.parse(fs.readFileSync(path.join(D, inreg2F), 'utf8'));
const copie = (o) => JSON.parse(JSON.stringify(o));
// un RPC local peste inregistrare (sau peste o varianta schimbata a ei)
@ -26,6 +31,7 @@ async function rpcDin(rec) {
if (q.method === 'eth_chainId') result = rec.chainId;
else if (q.method === 'eth_getTransactionReceipt') result = rec.receipts[String(q.params[0]).toLowerCase()] || null;
else if (q.method === 'eth_getLogs') result = rec.transfersOut;
else if (q.method === 'eth_getCode') result = rec.code || '0x';
res.writeHead(200, { 'content-type': 'application/json' }); res.end(JSON.stringify({ jsonrpc: '2.0', id: q.id, result }));
}); });
await new Promise((r) => srv.listen(0, '127.0.0.1', r));
@ -58,6 +64,18 @@ try {
cer(v9.verdict === 'UNMEASURED', `9. CONTROL: un RPC al altui lant (2800) -> ${v9.verdict}, nu VALID si nu INVALID`);
const v10 = await verificaPlati(dovada, { rpc: 'http://127.0.0.1:9', allTransfers: true });
cer(v10.verdict === 'UNMEASURED', `10. CONTROL: un RPC care nu raspunde -> ${v10.verdict}`);
// portofelul-contract 2-din-2: codul de pe lant trebuie sa fie contractul compilat cu cei doi semnatari din dosar
const e2 = /is the 2-of-2 contract/;
const v11 = await judeca(dovada2, inreg2);
cer(v11.verdict === 'VALID' && v11.checks.every((c) => c.pass === true) && v11.checks.some((c) => e2.test(c.name)),
`11. dosarul portofelului 2-din-2, peste raspunsurile reale de pe 28001: VALID (${v11.checks.length} verificari, cu codul contractului)`);
const r12 = copie(inreg2); const pozitie = r12.code.length - 120; r12.code = r12.code.slice(0, pozitie) + (r12.code[pozitie] === '0' ? '1' : '0') + r12.code.slice(pozitie + 1);
cer(pica(await judeca(dovada2, r12), e2), '12. CONTROL: un singur octet schimbat in codul de pe lant -> INVALID');
const d13 = copie(dovada2); d13.walletContract.agentSigner = '0x' + '22'.repeat(20);
cer(pica(await judeca(d13, inreg2), e2), '13. CONTROL: dosarul numeste alt semnatar al agentului decat cel din codul de pe lant -> INVALID');
const r14 = copie(inreg2); delete r14.code;
cer(pica(await judeca(dovada2, r14), e2), '14. CONTROL: la adresa portofelului nu e niciun cod (un cont cu o cheie), dar dosarul spune 2-din-2 -> INVALID');
} catch (e) { cer(false, `proba s-a oprit: ${String(e.message || e).slice(0, 160)}`); }
console.log(`\nverifica-plati: ${ok}/${ok + rau} ${rau ? 'CU ESECURI' : 'cum trebuia'}`);
process.exitCode = rau ? 1 : 0;

View File

@ -0,0 +1,52 @@
#!/usr/bin/env node
// recompileaza-contract.mjs: arata ca AereAgentWallet2of2.json spune adevarul despre sursa lui (2026-09-29). Da intrarea standard a
// compilatorului din artefact (sursele si setarile) unui solc 0.8.23 ales de cel care verifica, si cere ca codul de desfasurare si
// codul de rulare sa iasa OCTET CU OCTET cele din artefact; codul de pe lant il compara apoi verifica-plati.mjs. Controlul metodei e
// inauntru: aceeasi intrare cu un singur caracter schimbat intr-un comentariu al sursei TREBUIE sa dea alt cod (amprenta metadatelor),
// altfel comparatia nu poate deosebi nimic si iesirea e NEMASURAT.
// node recompileaza-contract.mjs --solc <cale catre solc 0.8.23 nativ> iesire 0 IDENTIC, 1 DIFERIT, 2 NEMASURAT
// (solc 0.8.23: github.com/ethereum/solidity/releases/tag/v0.8.23, sau npx solc@0.8.23 cu --solcjs)
import fs from 'node:fs';
import path from 'node:path';
import { spawnSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';
import { incarcaArtefact } from './contract-2of2.mjs';
const a = process.argv.slice(2); const get = (f) => { const i = a.indexOf(f); return i >= 0 ? a[i + 1] : undefined; };
const solc = get('--solc'); const solcjs = a.includes('--solcjs');
if (!solc && !solcjs) { console.log('usage: node recompileaza-contract.mjs --solc <path to a native solc 0.8.23> | --solcjs (runs npx solc@0.8.23)'); process.exit(2); }
const art = incarcaArtefact();
const cheama = (args, intrare) => solcjs
? spawnSync(process.platform === 'win32' ? 'npx.cmd' : 'npx', ['--yes', 'solc@0.8.23', ...args], { input: intrare, encoding: 'utf8', maxBuffer: 1 << 28, shell: process.platform === 'win32', timeout: 600000 })
: spawnSync(solc, args, { input: intrare, encoding: 'utf8', maxBuffer: 1 << 28, timeout: 600000 });
const ver = cheama(['--version']);
const versiune = ((ver.stdout || '') + (ver.stderr || '')).match(/0\.8\.23\+commit\.[0-9a-f]+/);
if (!versiune) { console.log(`NEMASURAT: the compiler is not solc 0.8.23 (${String((ver.stdout || ver.stderr || (ver.error && ver.error.message) || '').trim()).slice(0, 100)})`); process.exit(2); }
const compileaza = (input) => {
const r = cheama(['--standard-json'], JSON.stringify(input));
let o = null; try { o = JSON.parse(r.stdout); } catch { return { eroare: `no JSON from the compiler (exit ${r.status})` }; }
const erori = (o.errors || []).filter((e) => e.severity === 'error');
if (erori.length) return { eroare: erori.map((e) => e.formattedMessage || e.message).join(' | ').slice(0, 200) };
const c = o.contracts && o.contracts[art.sourcePath] && o.contracts[art.sourcePath][art.contractName];
return c ? { bytecode: '0x' + c.evm.bytecode.object, deployedBytecode: '0x' + c.evm.deployedBytecode.object } : { eroare: 'the output has no such contract' };
};
const r = compileaza(art.standardJsonInput);
if (r.eroare) { console.log(`NEMASURAT: ${r.eroare}`); process.exit(2); }
// controlul metodei: un comentariu schimbat trebuie sa schimbe codul
const alt = JSON.parse(JSON.stringify(art.standardJsonInput));
alt.sources[art.sourcePath].content = alt.sources[art.sourcePath].content.replace('HONEST SCOPE', 'HONEST SCOPF');
const rc = compileaza(alt);
if (rc.eroare || alt.sources[art.sourcePath].content === art.standardJsonInput.sources[art.sourcePath].content || rc.deployedBytecode === r.deployedBytecode) {
console.log(`NEMASURAT: the method control failed (a changed comment did not change the code${rc.eroare ? ': ' + rc.eroare : ''})`); process.exit(2);
}
// copia de citit a sursei (contract/AereAgentWallet2of2.sol, in pachetul publicat) trebuie sa fie chiar sursa compilata
const citibil = path.join(path.dirname(fileURLToPath(import.meta.url)), 'contract', 'AereAgentWallet2of2.sol');
const copieBuna = !fs.existsSync(citibil) || fs.readFileSync(citibil, 'utf8').replace(/\r\n/g, '\n') === art.standardJsonInput.sources[art.sourcePath].content;
const bun = r.bytecode === art.bytecode && r.deployedBytecode === art.deployedBytecode && copieBuna;
console.log(` compiler: solc ${versiune[0]}${solcjs ? ' (solcjs)' : ''}`);
console.log(fs.existsSync(citibil) ? ` ${copieBuna ? 'OK ' : 'FAIL'} contract/AereAgentWallet2of2.sol is the compiled source` : ' -- no readable copy of the source next to this script (contract/AereAgentWallet2of2.sol)');
console.log(` ${r.bytecode === art.bytecode ? 'OK ' : 'FAIL'} creation code: ${(r.bytecode.length - 2) / 2} bytes`);
console.log(` ${r.deployedBytecode === art.deployedBytecode ? 'OK ' : 'FAIL'} runtime code: ${(r.deployedBytecode.length - 2) / 2} bytes`);
console.log(` OK method control: one changed character in a comment gives another runtime code`);
console.log(bun ? 'IDENTICAL: the artifact is the compilation of its source' : 'DIFFERENT: the artifact is not the compilation of its source, or the readable copy is not that source');
process.exitCode = bun ? 0 : 1;

View File

@ -6,13 +6,17 @@
// 2. fiecare plata numeste o intrare a registrului: plata permisa, din portofel, catre destinatar, cu suma si activul;
// 3. pe lant, tranzactia platii are status 1 si emite, din contractul activului, AuthorizationUsed(portofel, nonce) cu nonce =
// sha256(hash-ul intrarii) si Transfer(portofel, destinatar, suma) - deci plata de pe lant e chiar cea din registru;
// 4. cu --all-transfers: ORICE Transfer din portofel in intervalul de blocuri e una din platile de mai sus (nicio plata fara intrare).
// 4. cu --all-transfers: ORICE Transfer din portofel in intervalul de blocuri e una din platile de mai sus (nicio plata fara intrare);
// 5. cand dosarul numeste un portofel-contract 2-din-2 (walletContract: {agentSigner, policySigner}): codul de pe lant al portofelului e
// EXACT codul compilat din AereAgentWallet2of2.sol (artefactul de langa verificator) cu cei doi semnatari in locul imutabilelor, deci
// din portofel nu a putut plati nici agentul singur, nici serviciul de politica singur.
// Ce nu dovedeste: ca serviciul cumparat a fost livrat; ca portofelul nu a semnat si autorizari nedecontate (acelea nu misca bani).
// node verifica-plati.mjs <dovada.json> --rpc <url> [--all-transfers] iesire 0 VALID, 1 INVALID, 2 NEMASURAT
import fs from 'node:fs';
import { pathToFileURL } from 'node:url';
import { verifyLedger } from '../agent-ledger.mjs';
import { incarcaEthers, nonceForEntry, assetId } from './wallet.mjs';
import { incarcaArtefact, codulAsteptat, amprentaSursei } from './contract-2of2.mjs';
export async function verificaPlati(d, { rpc, allTransfers = false, fetchImpl = fetch } = {}) {
const ethers = incarcaEthers();
@ -53,6 +57,17 @@ export async function verificaPlati(d, { rpc, allTransfers = false, fetchImpl =
ok(`every Transfer out of the wallet since block ${d.fromBlock || 0} is a payment in the ledger (${logs.length} transfers)`, straine.length === 0, straine.map((l) => l.transactionHash.slice(0, 12)).join(', '));
} catch (x) { rez.push({ name: 'all transfers out of the wallet', pass: null, detail: x.message }); }
}
if (d.walletContract) {
const wc = d.walletContract; let art = null, asteptat = null;
try { art = incarcaArtefact(); asteptat = codulAsteptat(art, wc); } catch (x) { rez.push({ name: 'the 2-of-2 wallet artifact', pass: null, detail: x.message }); }
if (asteptat) {
try {
const cod = String(await apel('eth_getCode', [d.wallet, 'latest'])).toLowerCase();
ok(`the wallet is the 2-of-2 contract (AereAgentWallet2of2, source ${String(amprentaSursei(art)).slice(0, 12)}..): its code on chain is the compiled code with agent signer ${String(wc.agentSigner).slice(0, 10)}.. and policy signer ${String(wc.policySigner).slice(0, 10)}..`,
cod === asteptat, cod === asteptat ? '' : cod === '0x' ? 'no code at the wallet address' : `the code on chain differs: ${(cod.length - 2) / 2} bytes, expected ${(asteptat.length - 2) / 2}`);
} catch (x) { rez.push({ name: 'the wallet code on chain', pass: null, detail: x.message }); }
}
}
const verdict = rez.some((c) => c.pass === false) ? 'INVALID' : rez.some((c) => c.pass === null) ? 'UNMEASURED' : 'VALID';
return { verdict, checks: rez };
}

View File

@ -18,12 +18,17 @@
// de intrarea din registru, si aceeasi intrare nu poate plati de doua ori (nici la portofel, nici pe lant). Intoarce PaymentPayload-ul
// x402 v2, de pus de agent in antetul PAYMENT-SIGNATURE, si un plic AIP-23 cu decizia.
//
// DOUA MODURI. `eoa`: portofelul E adresa cheii pe care o tine; cine tine cheia (proprietarul) poate plati si fara agent. `cosign`
// (2026-09-29, `contractWallet`): banii stau intr-un portofel-contract 2-din-2 (contracts/contracts/x402/AereAgentWallet2of2.sol,
// ERC-1271) care cere semnatura agentului SI a acestui serviciu; serviciul semneaza aici numai jumatatea lui, peste acelasi digest
// EIP-712, iar agentul isi adauga jumatatea dupa ce isi recalculeaza singur digestul (client.mjs). Nici agentul, nici proprietarul
// nu pot plati singuri.
//
// CE NU FACE: nu trimite tranzactii (decontarea o face facilitatorul x402 al serverului de resurse); nu tine alt activ decat unul
// EIP-3009 configurat; nu e custodie fara incredere: cine tine cheia portofelului (proprietarul) poate plati fara agent. Un portofel-
// contract 2-din-2 (agentul + politica, ERC-1271) ar scoate si increderea asta; nu e facut.
// EIP-3009 configurat.
//
// node wallet.mjs serve --config wallet.json [--port 8793] serviciul HTTP (POST /authorize, POST /revocations, GET /status)
// wallet.json: { policy, policyHash, network, token:{address,name,version}, evmKeyFile, stateFile, toleranceSeconds?, maxValiditySeconds? }
// wallet.json: { policy, policyHash, network, token:{address,name,version}, evmKeyFile, stateFile, contractWallet?, toleranceSeconds?, maxValiditySeconds? }
import fs from 'node:fs';
import path from 'node:path';
import http from 'node:http';
@ -76,7 +81,11 @@ export function createWallet(c) {
const { policy, policyHash } = hashPolicy(c.policy);
if (c.policyHash != null && String(c.policyHash).toLowerCase() !== policyHash) throw new Error('agent-wallet: the policy does not hash to the pinned policyHash');
const cont = new ethers.Wallet(String(c.evmPrivateKey || '').trim());
const adresa = cont.address.toLowerCase();
// in modul cosign portofelul e contractul, iar cheia tinuta aici e numai a semnatarului de politica
if (c.contractWallet != null && !ADRESA.test(String(c.contractWallet))) throw new Error('agent-wallet: contractWallet must be an address');
const cosign = c.contractWallet != null;
const platitor = cosign ? ethers.getAddress(c.contractWallet) : cont.address;
const adresa = platitor.toLowerCase();
if (policy.wallet !== adresa) throw new Error('agent-wallet: the policy does not name this wallet (policy.wallet must be its address)');
if (!/^eip155:[0-9]+$/.test(String(c.network))) throw new Error('agent-wallet: network must be eip155:<chainId>');
if (!c.token || !ADRESA.test(String(c.token.address)) || !c.token.name || !c.token.version) throw new Error('agent-wallet: token needs address, name and version (its EIP-712 domain)');
@ -95,6 +104,7 @@ export function createWallet(c) {
const sesiune = sessionId(policy.agentId, policyHash);
const S = stare.sessions[sesiune] || (stare.sessions[sesiune] = { anchors: [], last: null, lastEntry: null });
const salveaza = () => { if (c.saveState) c.saveState(stare); };
const semneazaDigest = async (d) => cont.signingKey.sign(d).serialized;
const refuz = (error, extra = {}) => ({ ok: false, error, ...extra });
async function autorizeaza({ requirements: req, resource = null, ledger } = {}) {
@ -137,18 +147,22 @@ export function createWallet(c) {
const d = checkAction(policy, { ...a, at: Number(b.at) }, stare.signed.map((s) => ({ amount: s.amount, at: s.at })), { approvers: aprobatori });
if (!d.allowed) return refuz(`by what this wallet has signed: ${d.reason}`);
// semnatura EIP-3009
const authorization = { from: cont.address, to: ethers.getAddress(req.payTo), value: String(req.amount), validAfter: String(t - 5),
const authorization = { from: platitor, to: ethers.getAddress(req.payTo), value: String(req.amount), validAfter: String(t - 5),
validBefore: String(t + Math.min(timeout, VALID)), nonce: nonceForEntry(e.hash) };
const signature = await cont.signTypedData(domeniu, TIPURI, { ...authorization, value: BigInt(authorization.value),
const digest = ethers.TypedDataEncoder.hash(domeniu, TIPURI, { ...authorization, value: BigInt(authorization.value),
validAfter: BigInt(authorization.validAfter), validBefore: BigInt(authorization.validBefore) });
// aceeasi semnatura ECDSA peste digestul EIP-712 in ambele moduri (in eoa e chiar semnatura EIP-712 a platitorului). Semnarea e
// asincrona dinadins (un semnatar din afara procesului, KMS sau HSM, se leaga aici): de aceea cererile trec prin coada de mai jos.
const semnatura = await semneazaDigest(digest);
S.anchors.push({ seq: e.seq, hash: e.hash, at: t });
S.last = { seq: e.seq, hash: e.hash }; S.lastEntry = e;
stare.signed.push({ seq: e.seq, entryHash: e.hash, amount: String(req.amount), at: Number(b.at), payTo: authorization.to, nonce: authorization.nonce });
salveaza();
const paymentPayload = { x402Version: 2, ...(resource ? { resource } : {}), accepted: req, payload: { signature, authorization },
const paymentPayload = { x402Version: 2, ...(resource ? { resource } : {}), accepted: req, payload: { signature: cosign ? null : semnatura, authorization },
extensions: { 'aere-agent-ledger': { agentId: ledger.agentId, policyHash, session: ledger.session, entrySeq: e.seq, entryHash: e.hash } } };
return { ok: true, paymentPayload, header: Buffer.from(JSON.stringify(paymentPayload), 'utf8').toString('base64'),
receipt: { entrySeq: e.seq, entryHash: e.hash, nonce: authorization.nonce, from: cont.address, to: authorization.to, value: authorization.value },
return { ok: true, paymentPayload, header: cosign ? null : Buffer.from(JSON.stringify(paymentPayload), 'utf8').toString('base64'),
...(cosign ? { cosign: { digest, policySignature: semnatura, order: 'agent signature, then policy signature (65 + 65 bytes)' } } : {}),
receipt: { entrySeq: e.seq, entryHash: e.hash, nonce: authorization.nonce, from: platitor, to: authorization.to, value: authorization.value },
decision: decisionEnvelope({ policyHash, action: a, decision: d, createdAt: new Date(t * 1000).toISOString() }) };
}
// cererile se judeca UNA CATE UNA: verificarea si semnatura au un `await` intre ele, iar doua cereri deodata ar trece amandoua de
@ -161,11 +175,12 @@ export function createWallet(c) {
stare.revocations.push(rv); salveaza();
return { ok: true, revokedAt: r.revokedAt };
}
const status = () => ({ version: VERSION, address: cont.address, network, asset: token, agentId: policy.agentId, policyHash,
const status = () => ({ version: VERSION, mode: cosign ? 'cosign-2of2' : 'eoa', address: platitor, ...(cosign ? { policySigner: cont.address } : {}),
network, asset: token, tokenName: String(c.token.name), tokenVersion: String(c.token.version), agentId: policy.agentId, policyHash,
lastSeq: S.last ? S.last.seq : null, signed: stare.signed.length, revocations: stare.revocations.length });
// starea, ca sa fie pastrata peste o schimbare de politica (acelasi agent, acelasi portofel): o copie, nu referinta
const exportState = () => JSON.parse(JSON.stringify(stare));
return { address: cont.address, network, token, policyHash, authorize, addRevocation, status, exportState };
return { address: platitor, mode: cosign ? 'cosign-2of2' : 'eoa', network, token, policyHash, authorize, addRevocation, status, exportState };
}
// ---------------------------------------------------------------- serviciul HTTP (numai pe 127.0.0.1 implicit)