Skip to content

predicatelabs/sol-contracts

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

31 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Predicate Registry Program

A comprehensive predicate registry program for managing attestors, policies, and task validation on Solana. This program provides a decentralized way to register attestors, set client policies, and validate tasks with cryptographic attestations.

πŸš€ Features

Core Functionality

  • Registry Management: Initialize and manage a decentralized predicate registry
  • Attestor Registration: Register and deregister trusted attestors
  • Policy Management: Set and update client validation policies
  • Task Validation: Validate tasks with cryptographic attestations
  • Authority Management: Secure ownership transfer capabilities

Advanced Features

  • Program Derived Addresses (PDAs): Deterministic account creation for all entities
  • Event Emission: Observable state changes for off-chain applications
  • Comprehensive Error Handling: Custom error types with descriptive messages
  • Authority Management: Secure access control with ownership transfer
  • Signature Verification: Ed25519 signature validation for attestations
  • Expiration Handling: Time-based validation for tasks and attestations

πŸ“‹ Prerequisites

πŸ› οΈ Installation

  1. Clone the repository:

    git clone git@github.com:predicatelabs/sol-contracts.git
    cd sol-contracts
  2. Install dependencies:

    yarn install
  3. Build the program:

    anchor build

βš™οΈ Configuration

Local Development

For local testing with solana-test-validator:

# Set cluster to localhost
solana config set --url localhost

# Start local validator (in separate terminal)
solana-test-validator

Devnet Deployment

For devnet deployment:

  1. Set Solana config to devnet:

    solana config set --url devnet
  2. Create/fund your wallet (if needed):

    solana-keygen new --outfile ~/.config/solana/id.json
    solana airdrop 2
  3. Deploy to devnet:

    ./scripts/deploy-devnet.sh

🎯 Usage

Running Tests

# Run all tests on localnet
anchor test

# Run tests with specific cluster
anchor test --provider.cluster devnet

# Run tests with verbose output
anchor test -- --nocapture

Program Instructions

The program uses Program Derived Addresses (PDAs) for deterministic account creation:

Initialize Registry

const [registryPda] = PublicKey.findProgramAddressSync(
  [Buffer.from("predicate_registry")],
  program.programId
);

await program.methods
  .initialize()
  .accounts({
    registry: registryPda,
    authority: authority.publicKey,
    systemProgram: SystemProgram.programId,
  })
  .rpc();

Register Attestor

const [attestorPda] = PublicKey.findProgramAddressSync(
  [Buffer.from("attestor"), attestorKey.toBuffer()],
  program.programId
);

await program.methods
  .registerAttestor(attestorKey)
  .accounts({
    registry: registryPda,
    attestorAccount: attestorPda,
    authority: authority.publicKey,
    systemProgram: SystemProgram.programId,
  })
  .rpc();

Set Policy

const [policyPda] = PublicKey.findProgramAddressSync(
  [Buffer.from("policy"), client.publicKey.toBuffer()],
  program.programId
);

// Policy as byte array (max 200 bytes)
const policyData = Buffer.from("your-policy-data", "utf8");

await program.methods
  .setPolicy(Array.from(policyData))
  .accounts({
    registry: registryPda,
    policyAccount: policyPda,
    client: client.publicKey,
    systemProgram: SystemProgram.programId,
  })
  .rpc();

Validate Attestation

await program.methods
  .validateAttestation(task, attestorKey, attestation)
  .accounts({
    registry: registryPda,
    attestorAccount: attestorPda,
    policyAccount: policyPda,
    validator: validator.publicKey,
  })
  .rpc();

πŸ“ Project Structure

sol-contracts/
β”œβ”€β”€ programs/predicate_registry/  # Rust program source
β”‚   β”œβ”€β”€ Cargo.toml
β”‚   └── src/
β”‚       β”œβ”€β”€ lib.rs               # Main program entry point
β”‚       β”œβ”€β”€ state.rs             # Account structures and state management
β”‚       β”œβ”€β”€ errors.rs            # Custom error definitions
β”‚       β”œβ”€β”€ events.rs            # Event definitions
β”‚       └── instructions/        # Instruction handlers
β”‚           β”œβ”€β”€ mod.rs           # Module definitions and contexts
β”‚           β”œβ”€β”€ initialize.rs    # Registry initialization
β”‚           β”œβ”€β”€ register_attestor.rs
β”‚           β”œβ”€β”€ deregister_attestor.rs
β”‚           β”œβ”€β”€ set_policy.rs
β”‚           β”œβ”€β”€ update_policy.rs
β”‚           β”œβ”€β”€ validate_attestation.rs
β”‚           └── transfer_authority.rs
β”œβ”€β”€ migrations/                  # Deployment scripts
β”‚   └── deploy.ts
β”œβ”€β”€ scripts/                     # Utility scripts
β”‚   └── deploy-devnet.sh
β”œβ”€β”€ Anchor.toml                  # Anchor configuration
β”œβ”€β”€ Cargo.toml                   # Workspace configuration
β”œβ”€β”€ package.json                 # Node.js dependencies
β”œβ”€β”€ tsconfig.json                # TypeScript configuration
β”œβ”€β”€ ARCHITECTURE.md              # Detailed architecture guide
└── README.md                    # This file

πŸ—οΈ Program Architecture

Registry Account Structure

pub struct PredicateRegistry {
    pub authority: Pubkey,        // 32 bytes - Owner of the registry
    pub created_at: i64,         // 8 bytes - Creation timestamp
    pub updated_at: i64,         // 8 bytes - Last update timestamp
    pub total_attestors: u64,    // 8 bytes - Total registered attestors
    pub total_policies: u64,     // 8 bytes - Total policies set
}

Attestor Account Structure

pub struct AttestorAccount {
    pub attestor: Pubkey,        // 32 bytes - Attestor's public key
    pub is_registered: bool,     // 1 byte - Registration status
    pub registered_at: i64,      // 8 bytes - Registration timestamp
}

Policy Account Structure

pub struct PolicyAccount {
    pub client: Pubkey,          // 32 bytes - Client's public key
    pub policy: [u8; 200],       // 200 bytes - Fixed-length policy data
    pub policy_len: u16,         // 2 bytes - Actual length of policy data
    pub set_at: i64,            // 8 bytes - Policy creation timestamp
    pub updated_at: i64,        // 8 bytes - Last update timestamp
}

Task Structure

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
}

Attestation Structure

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
}

Event Types

  • RegistryInitialized: Emitted when registry is created
  • AttestorRegistered: Emitted when attestor is registered
  • AttestorDeregistered: Emitted when attestor is deregistered
  • PolicySet: Emitted when policy is set
  • PolicyUpdated: Emitted when policy is updated
  • TaskValidated: Emitted when task validation succeeds
  • AuthorityTransferred: Emitted when ownership is transferred

Error Types

  • AttestorAlreadyRegistered: Attestor is already registered
  • AttestorNotRegistered: Attestor is not registered
  • AttestorNotRegisteredForValidation: Attestor not registered for validation
  • PolicyTooLong: Policy string exceeds 200 characters
  • InvalidPolicy: Policy string is empty
  • PolicyNotFound: No existing policy found for client
  • TaskExpired: Task has expired
  • AttestationExpired: Attestation has expired
  • InvalidSignature: Attestation signature is invalid
  • TaskIdMismatch: Task and attestation UUIDs don't match
  • ExpirationMismatch: Task and attestation expirations don't match
  • WrongAttestor: Signature doesn't match provided attestor
  • Unauthorized: Access control violations
  • ArithmeticError: Arithmetic operation overflow/underflow

πŸ›‘οΈ Security Features

  • Authority-based Access Control: Registry operations require proper authorization
  • Signature Verification: Ed25519 signature validation for attestations
  • Expiration Handling: Time-based validation prevents stale data
  • PDA-based Addressing: Deterministic and secure account creation
  • Input Validation: Parameter validation and sanitization
  • Event Auditing: Complete operation history through events

πŸ“œ Available Scripts

# Development
yarn build              # Build the program
yarn test               # Run tests
yarn test:devnet        # Run tests on devnet
yarn lint               # Check code formatting
yarn lint:fix           # Fix formatting issues

# Deployment
yarn deploy             # Deploy to configured cluster
yarn deploy:devnet      # Deploy to devnet

πŸš€ Deployment

Local Deployment

# Start local validator (in separate terminal)
solana-test-validator

# Build and deploy
anchor build
anchor deploy
anchor test --skip-deploy --skip-local-validator

Devnet Deployment

# Set cluster to devnet
solana config set --url devnet

# Deploy using script
./scripts/deploy-devnet.sh

# Or deploy manually
anchor deploy --provider.cluster devnet
anchor test --provider.cluster devnet

Mainnet Deployment

# Set cluster to mainnet
solana config set --url mainnet

# Deploy (ensure you have sufficient SOL)
anchor deploy --provider.cluster mainnet

πŸ†” Program ID

The program ID is declared in lib.rs:

declare_id!("PredicateRegistry11111111111111111111111111");

Important: After deployment, update this with your actual program ID and update Anchor.toml accordingly.

πŸ“š Documentation

🀝 Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes
  4. Add tests for new functionality
  5. Ensure all tests pass (anchor test)
  6. Run linting (yarn lint:fix)
  7. Commit your changes (git commit -m 'Add amazing feature')
  8. Push to the branch (git push origin feature/amazing-feature)
  9. Open a Pull Request

Development Guidelines

  • Follow Rust and TypeScript best practices
  • Add comprehensive tests for new features
  • Update documentation for API changes
  • Ensure security considerations are addressed
  • Use conventional commit messages

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ™ Acknowledgments

  • Anchor Framework for the excellent Solana development framework
  • Solana Labs for the high-performance blockchain platform
  • The Solana developer community for continuous support and resources

About

Predicate Contracts for Solana

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •