feat(runtime): implement payment receipt signing#11
Conversation
Add cryptographic payment receipts for Phase 4 (Economics). Nodes sign Ed25519 receipts after each checkpoint epoch attesting execution cost. Agents introspect receipts via wallet hostcalls. Receipts travel with agents during migration for audit trail continuity. New packages: - pkg/receipt: Receipt type, binary serialization, Ed25519 sign/verify - internal/hostcall/wallet.go: WalletState interface + 3 wallet hostcalls Key changes: - wallet_balance, wallet_receipt_count, wallet_receipt hostcalls (CM-4 observation) - Receipt creation per checkpoint epoch with cost tracking - Receipt persistence (SaveReceipts/LoadReceipts/DeleteReceipts) - Wallet replay support for single-tick and chain replay - Receipts included in AgentPackage for migration - SDK wrappers, mocks, and simulator support - "wallet" added to NodeCapabilities Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fbba3902b3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if err != nil { | ||
| r.logger.Warn("wallet_receipt: invalid index", "index", index, "error", err) | ||
| return -1 |
There was a problem hiding this comment.
Record failed wallet_receipt calls for replay
wallet_receipt returns -1 on invalid index without writing any event-log entry, but replay always consumes a WalletReceipt entry for each call; this causes deterministic replay to fail for agents that probe unavailable receipts (or any path where the runtime returns an error instead of payload bytes). The hostcall should still record an entry that encodes the failure outcome so replay can reproduce the same return value.
Useful? React with 👍 / 👎.
| if len(receipts) > 0 { | ||
| i.lastReceiptTick = receipts[len(receipts)-1].EpochEnd | ||
| } |
There was a problem hiding this comment.
Seed receipt epoch start when no receipts are restored
lastReceiptTick is only set from the restored receipt chain; when an agent resumes from an existing checkpoint that has no receipts (the documented backward-compatible path), TickNumber is restored but lastReceiptTick stays 0, so the next signed receipt claims an epoch starting at tick 1 even though epochCost only includes post-resume work. That produces incorrect signed accounting ranges after upgrade/migration from pre-receipt checkpoints.
Useful? React with 👍 / 👎.
| func (w *walletStateRef) GetBudget() int64 { return w.instance.Budget } | ||
| func (w *walletStateRef) GetReceiptCount() int { | ||
| return len(w.instance.Receipts) |
There was a problem hiding this comment.
Guard wallet state access before instance wiring
walletStateRef dereferences w.instance unconditionally, but the reference is wired after module instantiation and _initialize execution; if agent initialization code invokes wallet_* during that phase, this path panics with a nil dereference. Add a nil guard (or wire state before any initialization callbacks) so wallet-enabled agents cannot crash during load.
Useful? React with 👍 / 👎.
Overview
Implements cryptographic payment receipts (Task 10, Phase 4: Economics). Nodes now sign and persist receipts attesting to agent execution costs, enabling auditable payment trails.
Key Changes
Receipt Infrastructure
pkg/receipt/— Payment receipt data structure with Ed25519 signing and binary serializationinternal/storage/— Receipt persistence (SaveReceipts/LoadReceipts/DeleteReceipts)internal/hostcall/wallet.go— Wallet hostcall implementations:wallet_balance()— Returns current budget in microcents (observation)wallet_receipt_count()— Returns number of accumulated receipts (observation)wallet_receipt(index, buf, len)— Copies receipt at index into buffer (observation)Agent Lifecycle Integration
internal/agent/instance.go— Receipt creation per checkpoint epoch with cost tracking; receipts loaded/saved alongside checkpointsinternal/migration/service.go— Receipts travel with agents during migration; signing key extracted from libp2p hostcmd/igord/main.go— Node signing key passed to agent loaderReplay & Verification
internal/replay/engine.go— Wallet hostcall replay support for deterministic verification (CE-3)internal/eventlog/eventlog.go— Added wallet hostcall event typesSDK & Testing
sdk/igor/— Hostcall wrappers and mocks for agent developmentinternal/simulator/hostcalls.go— Simulator wallet support (balance only, no receipts)Documentation
docs/runtime/HOSTCALL_ABI.md— Wallet function signatures and replay behaviordocs/governance/ROADMAP.md— Task 10 marked complete; Phase 4 in progressCLAUDE.md— Updated architecture overview with receipt moduleDesign Highlights
Testing