This Solana program implements a comprehensive predicate registry system for managing attestors, policies, and task validation on the Solana blockchain. It provides a decentralized infrastructure for attestation-based validation with cryptographic security guarantees.
programs/predicate_registry/src/
├── lib.rs # Main program entry point and instruction definitions
├── state.rs # Account structures and state management
├── errors.rs # Custom error definitions
├── events.rs # Event definitions for observability
└── instructions/ # Instruction handlers
├── mod.rs # Module definitions and account contexts
├── initialize.rs # Registry initialization logic
├── register_attestor.rs # Attestor registration
├── deregister_attestor.rs # Attestor deregistration
├── set_policy.rs # Policy creation
├── update_policy.rs # Policy updates
├── validate_attestation.rs # Task validation logic
└── transfer_authority.rs # Authority transfer
The program uses deterministic PDAs for all account types:
- Registry:
["predicate_registry"] - Attestor Accounts:
["attestor", attestor_pubkey] - Policy Accounts:
["policy", client_pubkey]
This ensures:
- Deterministic account addresses
- No need for manual keypair generation
- Secure account ownership verification
pub struct PredicateRegistry {
pub authority: Pubkey, // Registry owner
pub created_at: i64, // Creation timestamp
pub updated_at: i64, // Last update timestamp
pub total_attestors: u64, // Count of registered attestors
pub total_policies: u64, // Count of policies set
}pub struct AttestorAccount {
pub attestor: Pubkey, // Attestor's public key
pub is_registered: bool, // Registration status
pub registered_at: i64, // Registration timestamp
}pub struct PolicyAccount {
pub client: Pubkey, // Client's public key
pub policy: [u8; 200], // Fixed-length policy data (200 bytes)
pub policy_len: u16, // Actual length of policy data
pub set_at: i64, // Creation timestamp
pub updated_at: i64, // Last update timestamp
}pub struct Task {
pub uuid: [u8; 16], // Unique identifier
pub msg_sender: Pubkey, // Message sender
pub target: Pubkey, // Target address
pub msg_value: u64, // Message value (lamports)
pub encoded_sig_and_args: Vec<u8>, // Encoded signature and arguments
pub policy: [u8; 200], // Fixed-length policy data
pub expiration: i64, // Expiration timestamp
}pub struct Attestation {
pub uuid: [u8; 16], // UUID matching the task
pub attestor: Pubkey, // Attestor's public key
pub signature: [u8; 64], // Ed25519 signature
pub expiration: i64, // Expiration timestamp
}Custom error types provide clear feedback:
- Registration Errors:
AttestorAlreadyRegistered,AttestorNotRegistered - Policy Errors:
PolicyTooLong,InvalidPolicy,PolicyNotFound - Validation Errors:
TaskExpired,AttestationExpired,InvalidSignature - Matching Errors:
TaskIdMismatch,ExpirationMismatch,WrongAttestor - Access Control:
Unauthorized - Arithmetic:
ArithmeticError
Events are emitted for all operations to enable off-chain monitoring:
- Registry Events:
RegistryInitialized,AuthorityTransferred - Attestor Events:
AttestorRegistered,AttestorDeregistered - Policy Events:
PolicySet,PolicyUpdated - Validation Events:
TaskValidated
- Authority-based Access Control: Registry operations require proper authorization
- Signature Verification: Ed25519 signature validation for attestations
- Expiration Handling: Time-based validation prevents stale data
- Input Validation: Parameter validation and sanitization
- PDA-based Security: Deterministic account derivation
Creates the main registry account with the specified authority.
pub fn initialize(ctx: Context<Initialize>) -> Result<()>Accounts:
registry: PDA account to be createdauthority: Signer and payer for account creationsystem_program: Required for account creation
Allows the registry authority to register a new attestor.
pub fn register_attestor(ctx: Context<RegisterAttestor>, attestor: Pubkey) -> Result<()>Accounts:
registry: Existing registry accountattestor_account: PDA account to be created for the attestorauthority: Registry authority (signer)system_program: Required for account creation
Allows the registry authority to deregister an existing attestor.
pub fn deregister_attestor(ctx: Context<DeregisterAttestor>, attestor: Pubkey) -> Result<()>Accounts:
registry: Existing registry accountattestor_account: Existing attestor account to deregisterauthority: Registry authority (signer)
Allows a client to set their validation policy.
pub fn set_policy(ctx: Context<SetPolicy>, policy: Vec<u8>) -> Result<()>Accounts:
registry: Existing registry accountpolicy_account: PDA account to be created for the policyclient: Client setting the policy (signer)system_program: Required for account creation
Allows a client to update their existing policy.
pub fn update_policy(ctx: Context<UpdatePolicy>, policy: Vec<u8>) -> Result<()>Accounts:
registry: Existing registry accountpolicy_account: Existing policy account to updateclient: Client updating the policy (signer)
Validates an attestation for a given task.
pub fn validate_attestation(
ctx: Context<ValidateAttestation>,
task: Task,
attestor_key: Pubkey,
attestation: Attestation
) -> Result<()>Validation Steps:
- Check attestor is registered
- Verify task hasn't expired
- Verify attestation hasn't expired
- Validate UUID matching between task and attestation
- Verify expiration matching
- Validate Ed25519 signature
- Ensure signature matches provided attestor
Accounts:
registry: Existing registry accountattestor_account: Existing attestor accountpolicy_account: Existing policy account for the clientvalidator: Signer calling the validation
Transfers registry ownership to a new account.
pub fn transfer_authority(ctx: Context<TransferAuthority>, new_authority: Pubkey) -> Result<()>Accounts:
registry: Existing registry accountauthority: Current authority (signer)new_authority: Account info for new owner
All accounts use deterministic PDA derivation:
// Registry PDA
let (registry_pda, _) = Pubkey::find_program_address(
&[b"predicate_registry"],
&program_id
);
// Attestor PDA
let (attestor_pda, _) = Pubkey::find_program_address(
&[b"attestor", attestor_key.as_ref()],
&program_id
);
// Policy PDA
let (policy_pda, _) = Pubkey::find_program_address(
&[b"policy", client_key.as_ref()],
&program_id
);- Registry Authority: Can register/deregister attestors and transfer authority
- Clients: Can set and update their own policies
- Validators: Can validate attestations (read-only operation)
The program implements two hashing methods compatible with Solidity:
pub fn hash_task_safe(&self, validator: Pubkey) -> [u8; 32] {
// Hashes: uuid + msg_sender + validator + msg_value + encoded_sig_and_args + policy + expiration
}pub fn hash_task_with_expiry(&self) -> [u8; 32] {
// Hashes: uuid + msg_sender + target + msg_value + encoded_sig_and_args + policy + expiration
}Ed25519 signature verification ensures:
- Attestation authenticity
- Non-repudiation
- Integrity of task data
The test suite should cover:
- Initialization: Registry creation and authority setup
- Attestor Management: Registration, deregistration, and access control
- Policy Management: Setting, updating, and validation
- Task Validation: Complete attestation validation flow
- Error Handling: All error conditions and edge cases
- Authority Management: Transfer and access control
- Event Emission: Verification of emitted events
- Cryptographic Validation: Signature verification and hashing
anchor build
anchor testanchor build
anchor deploy --provider.cluster devnet
anchor test --provider.cluster devnetanchor build
anchor deploy --provider.cluster mainnet- Modular Architecture: Clear separation of concerns
- Comprehensive Documentation: Inline docs and architecture guide
- Error Handling: Custom error types with descriptive messages
- Event Emission: Observable program state changes
- Security: Access control, signature verification, and input validation
- PDA Usage: Deterministic account addresses
- Type Safety: Strong typing throughout the codebase
- Gas Optimization: Efficient account structures and operations