DACRegistry Contract
Decentralized Access Control for agent communications with attestations and permissions.
Contract Overview
DACRegistry (Decentralized Access Control Registry) manages:
📍 Contract Address
0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9
Network: 0G Chain Testnet
Attestation Structure
struct Attestation {
bytes32 id; // Unique attestation ID
bytes32 issuerId; // Issuer agent ID
bytes32 subjectId; // Subject agent ID
bytes32 claim; // Claim hash (capability/permission)
bytes signature; // Ed25519 signature
uint256 issuedAt; // Issuance timestamp
uint256 expiresAt; // Expiration timestamp (0 = never)
bool revoked; // Is revoked
uint256 revokedAt; // Revocation timestamp
string metadata; // JSON metadata
}
Key Functions
1. Create Attestation
function createAttestation(
bytes32 subjectId,
bytes32 claim,
bytes calldata signature,
uint256 expiresAt,
string calldata metadata
) external returns (bytes32 attestationId)
Description: Creates a new attestation for an agent.
Parameters:
subjectId- Agent being attested toclaim- Keccak256 hash of the claim (e.g., "can-access-weather-api")signature- Ed25519 signature over (subjectId + claim + expiresAt)expiresAt- Unix timestamp when attestation expires (0 = never)metadata- Additional JSON metadata
Returns: Unique attestation ID
Gas Cost: ~80,000 gas (~0.00008 0G)
2. Verify Attestation
function verifyAttestation(
bytes32 attestationId
) external view returns (bool valid, string memory reason)
Description: Checks if an attestation is valid.
Returns:
valid- True if attestation is validreason- Reason if invalid ("expired", "revoked", "not-found")
3. Revoke Attestation
function revokeAttestation(
bytes32 attestationId
) external onlyIssuer(attestationId)
Description: Revokes an attestation (only by original issuer).
Gas Cost: ~30,000 gas
4. Get Attestations for Agent
function getAttestationsForAgent(
bytes32 agentId
) external view returns (bytes32[] memory)
Description: Returns all attestation IDs for an agent.
5. Check Access Permission
function hasPermission(
bytes32 agentId,
bytes32 claim
) external view returns (bool)
Description: Checks if an agent has a valid attestation for a specific claim.
6. Get Trust Score
function getTrustScore(
bytes32 agentId
) external view returns (uint256)
Description: Calculates aggregate trust score based on valid attestations.
Events
event AttestationCreated(
bytes32 indexed attestationId,
bytes32 indexed issuerId,
bytes32 indexed subjectId,
bytes32 claim,
uint256 expiresAt
);
event AttestationRevoked(
bytes32 indexed attestationId,
bytes32 indexed issuerId,
uint256 timestamp
);
event TrustUpdated(
bytes32 indexed agentId,
uint256 newTrustScore
);
Complete Usage Example
TypeScript: Create and Verify Attestation
import { ethers } from 'ethers';
import { ed25519 } from '@noble/curves/ed25519';
// Setup
const provider = new ethers.JsonRpcProvider('https://evmrpc-testnet.0g.ai');
const wallet = new ethers.Wallet(PRIVATE_KEY, provider);
const dacRegistry = new ethers.Contract(
DAC_REGISTRY_ADDRESS,
DAC_REGISTRY_ABI,
wallet
);
// Define claim
const claim = 'can-access-weather-api';
const claimHash = ethers.keccak256(ethers.toUtf8Bytes(claim));
// Set expiration (30 days from now)
const expiresAt = Math.floor(Date.now() / 1000) + 30 * 24 * 60 * 60;
// Create signature message
const message = ethers.solidityPacked(
['bytes32', 'bytes32', 'uint256'],
[subjectAgentId, claimHash, expiresAt]
);
const messageHash = ethers.keccak256(message);
// Sign with Ed25519
const signature = ed25519.sign(
Buffer.from(messageHash.slice(2), 'hex'),
issuerPrivateKey
);
// Create attestation
const metadata = JSON.stringify({
type: 'capability',
scope: 'weather-data',
level: 'read-only'
});
console.log('Creating attestation...');
const tx = await dacRegistry.createAttestation(
subjectAgentId,
claimHash,
'0x' + Buffer.from(signature).toString('hex'),
expiresAt,
metadata
);
const receipt = await tx.wait();
const attestationId = receipt.events[0].args.attestationId;
console.log('✅ Attestation created:', attestationId);
// Verify attestation
const [valid, reason] = await dacRegistry.verifyAttestation(attestationId);
console.log('Attestation valid:', valid);
// Check permission
const hasPermission = await dacRegistry.hasPermission(
subjectAgentId,
claimHash
);
console.log('Has permission:', hasPermission);
Rust: Query Attestations
use ethers::prelude::*;
// Setup
let provider = Provider::::try_from("https://evmrpc-testnet.0g.ai")?;
let dac_registry = DACRegistry::new(DAC_REGISTRY_ADDRESS, Arc::new(provider));
// Get all attestations for agent
let attestation_ids = dac_registry
.get_attestations_for_agent(agent_id)
.call()
.await?;
println!("Found {} attestations", attestation_ids.len());
// Check each attestation
for id in attestation_ids {
let (valid, reason) = dac_registry
.verify_attestation(id)
.call()
.await?;
if valid {
println!("✓ Attestation {} is valid", hex::encode(id));
} else {
println!("✗ Attestation {} is invalid: {}", hex::encode(id), reason);
}
}
// Check specific permission
let claim = "can-access-weather-api";
let claim_hash = keccak256(claim.as_bytes());
let has_permission = dac_registry
.has_permission(agent_id, claim_hash)
.call()
.await?;
println!("Has weather API permission: {}", has_permission);
Trust Score Calculation
Trust scores are calculated based on:
- Valid Attestations: +10 points per valid attestation
- Issuer Reputation: Weighted by issuer's trust score
- Time Decay: Older attestations have less weight
- Revocations: -50 points per revoked attestation
function calculateTrustScore(bytes32 agentId) internal view returns (uint256) {
uint256 score = 0;
bytes32[] memory attestations = agentAttestations[agentId];
for (uint256 i = 0; i < attestations.length; i++) {
Attestation memory att = attestationRegistry[attestations[i]];
if (att.revoked) {
score = score > 50 ? score - 50 : 0;
continue;
}
if (att.expiresAt > 0 && att.expiresAt < block.timestamp) {
continue; // Expired
}
// Base score
uint256 attScore = 10;
// Time decay
uint256 age = block.timestamp - att.issuedAt;
if (age > 90 days) {
attScore = attScore / 2;
}
// Issuer weight
uint256 issuerScore = getTrustScore(att.issuerId);
attScore = attScore * (100 + issuerScore) / 100;
score += attScore;
}
return score;
}
Access Control Patterns
1. Capability-Based Access
// Grant specific capability
const capability = 'can-read-medical-records';
const claimHash = ethers.keccak256(ethers.toUtf8Bytes(capability));
await dacRegistry.createAttestation(
doctorAgentId,
claimHash,
signature,
expiresAt,
JSON.stringify({ scope: 'patient-123' })
);
// Check before allowing access
const canAccess = await dacRegistry.hasPermission(
doctorAgentId,
claimHash
);
if (!canAccess) {
throw new Error('Access denied');
}
2. Time-Limited Access
// Grant access for 1 hour
const oneHour = 60 * 60;
const expiresAt = Math.floor(Date.now() / 1000) + oneHour;
await dacRegistry.createAttestation(
temporaryAgentId,
claimHash,
signature,
expiresAt,
JSON.stringify({ temporary: true })
);
3. Hierarchical Trust
// Admin attests to manager
await dacRegistry.createAttestation(
managerAgentId,
keccak256('manager-role'),
adminSignature,
0, // Never expires
JSON.stringify({ role: 'manager' })
);
// Manager attests to employee
await dacRegistry.createAttestation(
employeeAgentId,
keccak256('employee-role'),
managerSignature,
0,
JSON.stringify({ role: 'employee', manager: managerAgentId })
);
Revocation Handling
// Revoke attestation (only issuer can revoke)
await dacRegistry.revokeAttestation(attestationId);
// Listen for revocation events
dacRegistry.on('AttestationRevoked', (attestationId, issuerId, timestamp) => {
console.log(`⚠️ Attestation ${attestationId} revoked by ${issuerId}`);
// Update local cache
cache.invalidate(attestationId);
// Notify affected agents
notifyRevocation(attestationId);
});
Gas Optimization
Create Attestation
~80,000 gas
~0.00008 0G
One-time cost
Verify
Free (view)
No transaction
Instant
Revoke
~30,000 gas
~0.00003 0G
Rare operation
Query
Free (view)
Batch queries
Off-chain
Security Considerations
⚠️ Best Practices
- Signature Verification: Always verify Ed25519 signatures on-chain
- Expiration: Set reasonable expiration times for attestations
- Revocation Checks: Always check revocation status before trusting
- Issuer Trust: Verify issuer's reputation and authority
- Claim Specificity: Use specific, scoped claims instead of broad permissions
Integration with AgentRegistry
DACRegistry works closely with AgentRegistry:
// When attestation is created, update agent reputation
function _updateAgentReputation(bytes32 agentId, int256 delta) internal {
IAgentRegistry(agentRegistryAddress).updateReputation(agentId, delta);
emit TrustUpdated(agentId, getTrustScore(agentId));
}
// When attestation is revoked, decrease reputation
function revokeAttestation(bytes32 attestationId) external {
Attestation storage att = attestationRegistry[attestationId];
require(att.issuerId == msg.sender, "Not issuer");
att.revoked = true;
att.revokedAt = block.timestamp;
// Decrease subject's reputation
_updateAgentReputation(att.subjectId, -50);
emit AttestationRevoked(attestationId, att.issuerId, block.timestamp);
}
Related Documentation
- Verification Guide - How to create and verify attestations
- AgentRegistry - Agent identity management
- Security Model - Security architecture