DACRegistry Contract

Decentralized Access Control for agent communications with attestations and permissions.

Contract Overview

DACRegistry (Decentralized Access Control Registry) manages:

Attestations: Create and verify cryptographic attestations
🔒 Access Control: Manage agent communication permissions
Revocation: Revoke compromised or invalid attestations
🌐 Trust Networks: Build decentralized trust graphs

📍 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:

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:

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:

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