~ knotless Search/
↑↓ move↵ open⇧↵ all results

johnandersen777.bsky.social/atlibs-socialweb-computer-requester

socialweb.computer market requester and policy modules for atlibs, with its web pages and lexicons

0
0
main
257 lines · 14.0 KB Raw History
// Test vectors for the policy modules (crates/policy), ported from
// socialweb-computer-kcp atlibs-bidder-fixes-rc159: internal/policyengine/policytest/cases.go
// (TrustCases: the expected Vouch / OnlyMe / Mutuals verdict per world) and records.go (how each
// world's records look). The kcp spec tests (market/bidder|requester/policy_spec_test.go) run every
// case through the scope and full runs of each policy; trustSuite does the same against the wasm,
// serving the world's records to the module's getRecord reads.
import assert from "node:assert/strict";
import { createHash } from "node:crypto";
import { test } from "node:test";

import { load, now } from "../../../atlibs/rust/src/harness.mjs";

const T = "com.publicdomainrelay.temp";
export const NSID = {
	vouch: "sh.tangled.graph.vouch",
	badge: `${T}.badgeBlueKeys`,
	association: `${T}.market.association`,
	bid: `${T}.market.bid`,
	accept: `${T}.market.accept`,
	receipt: `${T}.market.receipt`,
	rfp: `${T}.market.rfp`,
	vm: `${T}.compute.vm`,
	free: `${T}.market.bids.free`,
	x402: `${T}.market.bids.x402`,
	vmDelete: `${T}.compute.events.vm.delete`,
	vmTerminated: `${T}.compute.events.vm.terminated`,
};
export const GET_RECORD = "com.atproto.repo.getRecord";

export const Bidder = "did:plc:policytestbidder";
export const BidderOperator = "did:plc:policytestbidop";
export const Requester = "did:plc:policytestrequester";
export const ReqOperator = "did:plc:policytestreqop";
export const Stranger = "did:plc:policyteststranger";
export const OtherBidder = "did:plc:policytestbidder2";
const createdAt = "2026-10-30T00:00:00Z";
const BIDDER_ASSOCIATE = "bidder_associate";
const REQUESTER_ASSOCIATE = "requester_associate";

const sha256 = (...parts) => createHash("sha256").update(Buffer.concat(parts.map((p) => Buffer.from(p)))).digest();
export const claimRKey = (service, subject) => `${service}-${subject.replaceAll(":", "-")}`;
const receiptRKey = (uri, cid) => `receipt-${sha256(`${uri.trim()}\n${cid.trim()}`).subarray(0, 16).toString("hex")}`;
const releaseRKey = (receipt) => {
	const alphabet = "234567abcdefghijklmnopqrstuvwxyz";
	return [...sha256("delete", [0], receipt).subarray(0, 13)].map((b) => alphabet[b % alphabet.length]).join("");
};
const terminationRKey = (receipt) => `termination-payload-${sha256(`terminated:${receipt}`).subarray(0, 16).toString("hex")}`;
const plus = (ms) => new Date(Date.parse(now) + ms).toISOString();

export function partiesFor(perspective) {
	return perspective === "requester"
		? { Self: Requester, SelfOp: ReqOperator, SelfSvc: REQUESTER_ASSOCIATE, CP: Bidder, CPOp: BidderOperator, CPSvc: BIDDER_ASSOCIATE }
		: { Self: Bidder, SelfOp: BidderOperator, SelfSvc: BIDDER_ASSOCIATE, CP: Requester, CPOp: ReqOperator, CPSvc: REQUESTER_ASSOCIATE };
}

// policytest.World: records by at-uri, and the getRecord answers a host would give for them.
export class World {
	records = new Map();
	reads = [];
	put(repo, collection, rkey, value) {
		const uri = `at://${repo}/${collection}/${rkey}`;
		const cid = `bafy${sha256(JSON.stringify(value)).toString("hex").slice(0, 20)}`;
		this.records.set(uri, { uri, cid, value });
		return { uri, cid };
	}
	vouch(voucher, subject) {
		return this.put(voucher, NSID.vouch, subject, { $type: NSID.vouch, subject, createdAt });
	}
	denounce(voucher, subject) {
		return this.put(voucher, NSID.vouch, subject, { $type: NSID.vouch, subject, kind: "denounce", createdAt });
	}
	mirror(subject, operator, service) {
		return this.put(subject, NSID.badge, "association", { $type: NSID.badge, service, keyId: subject, challenge: operator, createdAt });
	}
	claim(operator, subject, service) {
		return this.put(operator, NSID.badge, claimRKey(service, subject), { $type: NSID.badge, service, keyId: subject, challenge: operator, createdAt });
	}
	associate(subject, operator, service) {
		this.mirror(subject, operator, service);
		this.claim(operator, subject, service);
	}
	heldContract(operator, bidder) {
		const bid = this.put(bidder, NSID.bid, "policytest-bid", { $type: NSID.bid, createdAt });
		const accept = this.put(operator, NSID.accept, "policytest-accept", { $type: NSID.accept, bid, createdAt });
		const receipt = this.put(bidder, NSID.receipt, receiptRKey(accept.uri, accept.cid), { $type: NSID.receipt, accept, createdAt });
		return { accept, receipt, bidder };
	}
	contractAssociation(subject, operator, service, contract, expiresAt) {
		this.mirror(subject, operator, service);
		return this.put(operator, NSID.association, claimRKey(service, subject), {
			$type: NSID.association, subject, service, contract: contract.accept, expiresAt, createdAt,
		});
	}
	release(operator, contract) {
		this.put(operator, NSID.vmDelete, releaseRKey(contract.receipt.uri), { $type: NSID.vmDelete, createdAt });
	}
	terminate(contract) {
		this.put(contract.bidder, NSID.vmTerminated, terminationRKey(contract.receipt.uri), { $type: NSID.vmTerminated, createdAt });
	}
	bid(bidder, rkey, free, cost = 0) {
		const nsid = free ? NSID.free : NSID.x402;
		const payload = this.put(bidder, nsid, `${rkey}-payload`, free ? { $type: nsid, reason: "free", createdAt } : { $type: nsid, cost, createdAt });
		return this.put(bidder, NSID.bid, rkey, { $type: NSID.bid, payload, createdAt });
	}
	// The run-module call contract for a policy module: public getRecord on the repo's PDS.
	respond = (call) => {
		assert.equal(call.nsid, GET_RECORD, `unexpected call ${call.nsid}`);
		const { repo, collection, rkey } = call.params;
		assert.equal(call.service, repo, "a read goes to the repo's own PDS");
		const uri = `at://${repo}/${collection}/${rkey}`;
		this.reads.push(uri);
		const record = this.records.get(uri);
		return record ? { ok: true, output: record } : { ok: false, error: `${GET_RECORD} failed with RecordNotFound: Could not locate record: ${uri}` };
	};
}

// cases.go TrustCases, verbatim.
export const TrustCases = [
	{ name: "unvouched", build: () => {} },
	{ name: "vouched-by-self", Vouch: true, build: (w, p) => w.vouch(p.Self, p.CP) },
	{ name: "vouched-by-verified-self-operator", Vouch: true, build: (w, p) => (w.associate(p.Self, p.SelfOp, p.SelfSvc), w.vouch(p.SelfOp, p.CP)) },
	{ name: "vouched-by-self-claimed-self-operator", build: (w, p) => (w.mirror(p.Self, p.SelfOp, p.SelfSvc), w.vouch(p.SelfOp, p.CP)) },
	{ name: "verified-counterparty-operator-vouched-by-self", Vouch: true, build: (w, p) => (w.associate(p.CP, p.CPOp, p.CPSvc), w.vouch(p.Self, p.CPOp)) },
	{ name: "verified-counterparty-operator-vouched-by-self-operator", Vouch: true, build: (w, p) => {
		w.associate(p.Self, p.SelfOp, p.SelfSvc);
		w.associate(p.CP, p.CPOp, p.CPSvc);
		w.vouch(p.SelfOp, p.CPOp);
	} },
	{ name: "forged-counterparty-operator-claim", build: (w, p) => (w.vouch(p.Self, p.CPOp), w.mirror(p.CP, p.CPOp, p.CPSvc)) },
	{ name: "claim-for-the-other-role", build: (w, p) => (w.vouch(p.Self, p.CPOp), w.associate(p.CP, p.CPOp, p.SelfSvc)) },
	{ name: "vouched-directly-with-verified-operator", Vouch: true, build: (w, p) => (w.associate(p.CP, p.CPOp, p.CPSvc), w.vouch(p.Self, p.CP)) },
	{ name: "counterparty-operator-vouches-self-only", build: (w, p) => (w.associate(p.CP, p.CPOp, p.CPSvc), w.vouch(p.CPOp, p.Self)) },
	{ name: "mutual-operator-vouches", Vouch: true, Mutuals: true, build: (w, p) => {
		w.associate(p.CP, p.CPOp, p.CPSvc);
		w.vouch(p.Self, p.CPOp);
		w.vouch(p.CPOp, p.Self);
	} },
	{ name: "denounced", build: (w, p) => w.denounce(p.Self, p.CP) },
	{ name: "stranger-vouches", build: (w, p) => (w.vouch(Stranger, p.CP), w.vouch(p.CP, p.Self)) },
	{ name: "same-verified-operator", OnlyMe: true, Vouch: true, Mutuals: true, build: (w, p) => (w.associate(p.Self, p.SelfOp, p.SelfSvc), w.associate(p.CP, p.SelfOp, p.CPSvc)) },
	{ name: "counterparty-operated-by-self", OnlyMe: true, Vouch: true, Mutuals: true, build: (w, p) => w.associate(p.CP, p.Self, p.CPSvc) },
	{ name: "forged-same-operator", build: (w, p) => (w.associate(p.Self, p.SelfOp, p.SelfSvc), w.mirror(p.CP, p.SelfOp, p.CPSvc)) },
	{ name: "different-verified-operators", build: (w, p) => (w.associate(p.Self, p.SelfOp, p.SelfSvc), w.associate(p.CP, p.CPOp, p.CPSvc)) },
	{ name: "contract-scoped-association-held", OnlyMe: true, Vouch: true, Mutuals: true, build: (w, p) => {
		w.associate(p.Self, p.SelfOp, p.SelfSvc);
		w.contractAssociation(p.CP, p.SelfOp, p.CPSvc, w.heldContract(p.SelfOp, OtherBidder), plus(3600e3));
	} },
	{ name: "contract-scoped-association-expired", build: (w, p) => {
		w.associate(p.Self, p.SelfOp, p.SelfSvc);
		w.contractAssociation(p.CP, p.SelfOp, p.CPSvc, w.heldContract(p.SelfOp, OtherBidder), plus(-60e3));
	} },
	{ name: "contract-scoped-association-released", build: (w, p) => {
		w.associate(p.Self, p.SelfOp, p.SelfSvc);
		const contract = w.heldContract(p.SelfOp, OtherBidder);
		w.contractAssociation(p.CP, p.SelfOp, p.CPSvc, contract, plus(3600e3));
		w.release(p.SelfOp, contract);
	} },
	{ name: "contract-scoped-association-terminated", build: (w, p) => {
		w.associate(p.Self, p.SelfOp, p.SelfSvc);
		const contract = w.heldContract(p.SelfOp, OtherBidder);
		w.contractAssociation(p.CP, p.SelfOp, p.CPSvc, contract, plus(3600e3));
		w.terminate(contract);
	} },
];

const FAMILY = {
	"tangled-vouch": { want: (c) => !!c.Vouch, msg: "not vouched" },
	"only-me": { want: (c) => !!c.OnlyMe, msg: "operator mismatch" },
	mutuals: { want: (c) => !!c.Mutuals, msg: "not mutual follows" },
	open: { want: () => true },
};

export const wasm = (crate) => new URL(`../../target/wasm32-unknown-unknown/release/${crate.replaceAll("-", "_")}.wasm`, import.meta.url);

export async function loadPolicy(crate) {
	const mod = await load(wasm(crate));
	// One run of `evaluate` under the real runModule host loop, the world answering its reads.
	mod.evaluate = async (input, world = new World()) => {
		const ran = await mod.handle("evaluate", input, { respond: world.respond });
		return { ...ran, verdict: ran.result, reads: world.reads };
	};
	return mod;
}

export function abiSuite(mod) {
	test("module has no imports and exports evaluate", () => {
		assert.equal(WebAssembly.Module.imports(mod.module).length, 0);
		const names = WebAssembly.Module.exports(mod.module).map((e) => e.name);
		for (const name of ["memory", "alloc", "evaluate"]) assert.ok(names.includes(name), name);
	});
}

// Every TrustCase, scope and full, from the policy's own perspective, read from the records.
export function trustSuite(crate, name, family) {
	const perspective = name.startsWith("bidder-") ? "bidder" : "requester";
	const { want, msg } = FAMILY[family];
	return async () => {
		const mod = await loadPolicy(crate);
		abiSuite(mod);
		for (const tc of TrustCases) {
			for (const mode of ["scope", "full"]) {
				test(`${name} ${mode} ${tc.name}: allow=${want(tc)}`, async () => {
					const world = new World();
					const p = partiesFor(perspective);
					tc.build(world, p);
					const input = mode === "scope"
						? { selfDid: p.Self, counterpartyDid: p.CP, mode }
						: { selfDid: p.Self, counterpartyDid: p.CP, subjectDid: p.CP, mode };
					const { verdict, error } = await mod.evaluate(input, world);
					assert.equal(error, undefined);
					const deny = mode === "scope" ? `scope denied by ${name}` : msg;
					assert.deepEqual(verdict, want(tc) ? { allow: true, violations: [] } : { allow: false, violations: [{ msg: deny, policyId: name }] });
				});
			}
		}
		if (family === "open") return;
		test(`${name}: a seeded snapshot is used as is, no reads`, async () => {
			const p = partiesFor(perspective);
			const vouched = { operatorOf: { [p.Self]: p.Self }, vouchedBy: { [p.Self]: [p.CP] } };
			const verdict = mod.run("evaluate", { selfDid: p.Self, counterpartyDid: p.CP, snapshot: vouched, now, state: null, event: { type: "start" } });
			assert.deepEqual(verdict.calls, undefined);
			assert.equal(verdict.done, true);
			assert.equal(verdict.result.allow, family === "tangled-vouch");
			const operated = { operatorOf: { [p.Self]: p.SelfOp, [p.CP]: p.SelfOp }, vouchedBy: {} };
			assert.equal(mod.run("evaluate", { selfDid: p.Self, counterpartyDid: p.CP, snapshot: operated, now, event: { type: "start" } }).result.allow, true);
		});
		test(`${name}: a self-vouch is ignored (BuildSnapshotWith skips voucher == subject)`, async () => {
			// The counterparty is the evaluator's own (verified) operator, and only vouches for itself.
			const p = partiesFor(perspective);
			const world = new World();
			world.associate(p.Self, p.SelfOp, p.SelfSvc);
			world.vouch(p.SelfOp, p.SelfOp);
			const { verdict, reads } = await mod.evaluate({ selfDid: p.Self, counterpartyDid: p.SelfOp, mode: "scope" }, world);
			assert.equal(verdict.allow, false);
			assert.ok(!reads.includes(`at://${p.SelfOp}/${NSID.vouch}/${p.SelfOp}`), "the self pair is never read");
			const seeded = { operatorOf: { [p.Self]: p.SelfOp }, vouchedBy: { [p.Self]: [], [p.SelfOp]: [p.SelfOp] } };
			assert.equal(mod.run("evaluate", { selfDid: p.Self, counterpartyDid: p.SelfOp, snapshot: seeded, now, event: { type: "start" } }).result.allow, false);
		});
		test(`${name}: bad input is an error (kcp exits 1), as data`, async () => {
			assert.equal((await mod.evaluate({ counterpartyDid: Bidder })).error, "selfDid is required (the evaluator's own DID)");
			assert.equal((await mod.evaluate({ selfDid: Bidder, mode: "scope" })).error, "scope mode requires counterpartyDid");
			assert.deepEqual((await mod.evaluate({ selfDid: Bidder })).verdict, { allow: false, violations: [{ msg: "no counterparty to evaluate", policyId: name }] });
			assert.deepEqual((await mod.evaluate({ selfDid: Bidder, subjectDid: Requester, counterpartyDid: Stranger })).verdict, {
				allow: false, violations: [{ msg: "the subject and the counterparty differ", policyId: name }],
			});
		});
		test(`${name}: full mode derives the counterparty from the records`, async () => {
			const p = partiesFor(perspective);
			const world = new World();
			world.vouch(p.Self, p.CP);
			const record = (repo, collection) => ({ uri: `at://${repo}/${collection}/r`, cid: "bafyx" });
			const input = perspective === "requester" ? { bid: record(p.CP, NSID.bid) } : { rfp: record(p.CP, NSID.rfp) };
			const { verdict } = await mod.evaluate({ selfDid: p.Self, ...input }, world);
			assert.equal(verdict.allow, family === "tangled-vouch");
		});
	};
}