Aere Network public source. Everything here can be checked against the live chain (chain id 2800, https://rpc.aere.network). Scope note, stated up front rather than buried: consensus on chain 2800 is classical secp256k1 ECDSA QBFT. The post-quantum work in this repository is at the signature, precompile, account and transport layers. Nothing here makes the consensus post-quantum, and no document in it should be read as claiming so.
201 lines
11 KiB
Solidity
201 lines
11 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity 0.8.23;
|
|
|
|
import "./IERC8004.sol";
|
|
import "@openzeppelin/contracts/access/Ownable.sol";
|
|
|
|
/// @dev The subset of the LIVE, deployed AereAIReputation (chain 2800, 0x781e…feBF) this adapter
|
|
/// uses. Reputation there is keyed by (operator address, bytes32 agentId): `attest` is gated
|
|
/// to a registered attestor set fixed at deploy, and `scoreOf` / `statsOf` are public reads.
|
|
interface IAereAIReputationLive {
|
|
function attest(address operator, bytes32 agentId, int8 delta, bytes32 evidenceHash, string calldata evidenceUri)
|
|
external;
|
|
|
|
function scoreOf(address operator, bytes32 agentId) external view returns (uint256);
|
|
|
|
function statsOf(address operator, bytes32 agentId)
|
|
external
|
|
view
|
|
returns (uint256 positiveCount, uint256 disputeCount, uint256 lifetimeSlashed);
|
|
|
|
function isAttestor(address who) external view returns (bool);
|
|
}
|
|
|
|
/// @dev The one Identity-adapter read used to resolve an ERC-8004 agentId to its Aere operator
|
|
/// (the agent's controller), which is the operator address the live reputation is keyed by.
|
|
interface IAereIdentity8004 {
|
|
function isRegistered(uint256 agentId) external view returns (bool);
|
|
function getAgent(uint256 agentId)
|
|
external
|
|
view
|
|
returns (IERC8004Identity.AgentInfo memory);
|
|
}
|
|
|
|
/**
|
|
* @title AereReputationRegistry8004, an ERC-8004 Reputation adapter over the deployed AereAIReputation
|
|
*
|
|
* @notice Exposes the ERC-8004 Reputation surface (attest / read feedback) over Aere Network's
|
|
* ALREADY-DEPLOYED composable reputation primitive, AereAIReputation. It does NOT redeploy
|
|
* or modify the live reputation contract.
|
|
*
|
|
* READ (adapts the deployed contract directly). `getScore` and `getStats` map an ERC-8004
|
|
* agentId to the live reputation's (operator, agentId) key and read `scoreOf` / `statsOf`
|
|
* straight from the deployed AereAIReputation. This works against the currently-live
|
|
* instance as-is, with no change to it.
|
|
*
|
|
* WRITE (forwards feedback to the live contract). `giveFeedback` maps an ERC-8004 feedback
|
|
* signal to the live `attest(operator, agentId, delta, evidenceHash, uri)` and forwards it.
|
|
*
|
|
* @dev KEY MAPPING. The live reputation is keyed by (operator address, bytes32 agentId). This
|
|
* adapter derives:
|
|
* operator = the agent's current controller, resolved from the Identity adapter.
|
|
* agentKey = bytes32(agentId) (the ERC-8004 / DID agent id, left-padded).
|
|
* So an ERC-8004 read/write addresses exactly one live reputation slot, deterministically.
|
|
*
|
|
* @dev HONEST WRITE CONSTRAINT. AereAIReputation's attestor set is fixed at its deploy and has
|
|
* no add path. For `giveFeedback` to land in the live score, THIS adapter must be a
|
|
* registered attestor on the reputation instance it is constructed with. The currently-live
|
|
* AereAIReputation lists the Foundation attestor only (immutable), so pairing this adapter
|
|
* for WRITE-THROUGH is a Foundation governance decision, external to this contract and
|
|
* founder-gated. [VERIFY: on mainnet, register this adapter as an attestor before relying on
|
|
* giveFeedback, or have the Foundation attestor call the live attest directly.] The READ
|
|
* path needs no such authorization and works against the live instance today.
|
|
*
|
|
* @dev [VERIFY: confirm against the finalized ERC-8004 interface] ERC-8004 reputation feedback in
|
|
* later drafts carries a richer payload (score, tag, response URI). Here the feedback signal
|
|
* is normalized to the live model's delta in {-1, 0, +1} (+1 positive, -1 dispute, 0
|
|
* neutral) so no information is invented beyond what the deployed contract records.
|
|
*
|
|
* HONEST SCOPE. Application-layer reputation only. Does NOT change AERE consensus, holds no
|
|
* funds.
|
|
*
|
|
* @dev CALLER GATE (feedback authorization). Because every caller collapses into ONE on-chain
|
|
* attestor identity (this adapter) on the live AereAIReputation, an ungated write path would
|
|
* let ANY address inflate or tank any agent's reputation, defeating the live contract's
|
|
* curated-attestor model and its (attestor, evidenceHash) dedup. To mirror that curated model,
|
|
* `giveFeedback` is gated to an owner-managed allowlist of authorized feedback authors
|
|
* (`isAuthorizedFeedbackAuthor`). The owner curates WHO may submit feedback; it CANNOT set a
|
|
* reputation value directly, cannot bypass the {-1, 0, +1} delta clamp every author is held to,
|
|
* and holds no funds. This is the least-surprising design that closes the "anyone can write"
|
|
* hole while keeping the score honest and the read path fully permissionless.
|
|
*/
|
|
contract AereReputationRegistry8004 is IERC8004Reputation, Ownable {
|
|
/// @notice The LIVE, deployed AereAIReputation this adapter presents an ERC-8004 face over.
|
|
IAereAIReputationLive public immutable REPUTATION;
|
|
|
|
/// @notice The Aere ERC-8004 Identity adapter, used to resolve agentId -> operator (controller).
|
|
IAereIdentity8004 public immutable IDENTITY;
|
|
|
|
/// @notice Owner-curated set of addresses allowed to submit feedback through this adapter. Mirrors
|
|
/// the live AereAIReputation curated-attestor model at the adapter boundary: only an
|
|
/// authorized author may drive an agent's live reputation via `giveFeedback`.
|
|
mapping(address => bool) public isAuthorizedFeedbackAuthor;
|
|
|
|
error ZeroAddress();
|
|
error AgentNotRegistered(uint256 agentId);
|
|
error InvalidDelta(int8 delta);
|
|
error AdapterNotAttestor();
|
|
error NotAuthorizedFeedbackAuthor(address caller);
|
|
|
|
/// @notice Emitted when the owner adds or removes an authorized feedback author.
|
|
event FeedbackAuthorSet(address indexed author, bool authorized);
|
|
|
|
constructor(address reputation, address identity) {
|
|
if (reputation == address(0) || identity == address(0)) revert ZeroAddress();
|
|
REPUTATION = IAereAIReputationLive(reputation);
|
|
IDENTITY = IAereIdentity8004(identity);
|
|
}
|
|
|
|
// ==========================================================================
|
|
// Feedback-author administration
|
|
// ==========================================================================
|
|
|
|
/// @notice Authorize `author` to submit feedback through this adapter. Owner-only. This power is
|
|
/// scoped to feedback AUTHORIZATION only: it cannot set a reputation value, cannot exceed
|
|
/// the {-1, 0, +1} delta clamp, and moves no funds.
|
|
function addFeedbackAuthor(address author) external onlyOwner {
|
|
if (author == address(0)) revert ZeroAddress();
|
|
isAuthorizedFeedbackAuthor[author] = true;
|
|
emit FeedbackAuthorSet(author, true);
|
|
}
|
|
|
|
/// @notice Revoke `author`'s authorization to submit feedback through this adapter. Owner-only.
|
|
function removeFeedbackAuthor(address author) external onlyOwner {
|
|
isAuthorizedFeedbackAuthor[author] = false;
|
|
emit FeedbackAuthorSet(author, false);
|
|
}
|
|
|
|
// ==========================================================================
|
|
// Write
|
|
// ==========================================================================
|
|
|
|
/**
|
|
* @notice Record ERC-8004 feedback about an agent, forwarding it to the live AereAIReputation.
|
|
* The feedback signal is `delta` in {-1, 0, +1}. Resolves the live operator from the
|
|
* Identity adapter and calls the deployed `attest`. Reverts fail-closed if this adapter
|
|
* is not an authorized attestor on the live reputation (see contract NatSpec).
|
|
* @dev Gated to the owner-curated `isAuthorizedFeedbackAuthor` set: an unauthorized caller
|
|
* reverts NotAuthorizedFeedbackAuthor, so this adapter cannot act as an open reputation
|
|
* firehose once it is a live attestor. Every authorized author is still bound by the
|
|
* {-1, 0, +1} delta clamp, so authorization cannot forge an arbitrary score.
|
|
* @param agentId the ERC-8004 / DID agent id.
|
|
* @param delta +1 positive, -1 dispute, 0 neutral (audit-trail only).
|
|
* @param evidenceHash a 32-byte commitment to the off-chain evidence (non-zero on the live path).
|
|
* @param evidenceURI an optional public URI for the evidence.
|
|
*/
|
|
function giveFeedback(uint256 agentId, int8 delta, bytes32 evidenceHash, string calldata evidenceURI) external {
|
|
if (!isAuthorizedFeedbackAuthor[msg.sender]) revert NotAuthorizedFeedbackAuthor(msg.sender);
|
|
if (!IDENTITY.isRegistered(agentId)) revert AgentNotRegistered(agentId);
|
|
if (delta < -1 || delta > 1) revert InvalidDelta(delta);
|
|
// Surface the missing-authorization case with a clear adapter-level error rather than an
|
|
// opaque revert bubbling up from the live contract.
|
|
if (!REPUTATION.isAttestor(address(this))) revert AdapterNotAttestor();
|
|
|
|
address operator = _operatorOf(agentId);
|
|
REPUTATION.attest(operator, _agentKey(agentId), delta, evidenceHash, evidenceURI);
|
|
|
|
emit NewFeedback(agentId, msg.sender, delta, evidenceHash, evidenceURI);
|
|
}
|
|
|
|
// ==========================================================================
|
|
// Read
|
|
// ==========================================================================
|
|
|
|
/// @inheritdoc IERC8004Reputation
|
|
/// @notice The agent's live reputation score (0..10000), read straight from AereAIReputation.
|
|
function getScore(uint256 agentId) external view returns (uint256) {
|
|
return REPUTATION.scoreOf(_operatorOf(agentId), _agentKey(agentId));
|
|
}
|
|
|
|
/// @notice The raw live reputation counters for an agent (positive / dispute / lifetime slashed).
|
|
function getStats(uint256 agentId)
|
|
external
|
|
view
|
|
returns (uint256 positiveCount, uint256 disputeCount, uint256 lifetimeSlashed)
|
|
{
|
|
return REPUTATION.statsOf(_operatorOf(agentId), _agentKey(agentId));
|
|
}
|
|
|
|
/// @notice True iff this adapter is authorized to write feedback into the live reputation.
|
|
function canWriteThrough() external view returns (bool) {
|
|
return REPUTATION.isAttestor(address(this));
|
|
}
|
|
|
|
/// @notice The (operator, agentKey) pair this adapter maps an ERC-8004 agentId to on the live contract.
|
|
function liveKeyOf(uint256 agentId) external view returns (address operator, bytes32 agentKey) {
|
|
return (_operatorOf(agentId), _agentKey(agentId));
|
|
}
|
|
|
|
// ==========================================================================
|
|
// Internal
|
|
// ==========================================================================
|
|
|
|
function _operatorOf(uint256 agentId) internal view returns (address) {
|
|
return IDENTITY.getAgent(agentId).agentAddress; // current controller
|
|
}
|
|
|
|
function _agentKey(uint256 agentId) internal pure returns (bytes32) {
|
|
return bytes32(agentId);
|
|
}
|
|
}
|