Skip to main content

Blockchain Integration

This document describes how Oneliac integrates with the Solana blockchain for proof verification, transaction recording, and oracle event management.

Why Blockchain?

Blockchain provides Oneliac with:

  • Immutability: Patient transactions cannot be altered
  • Transparency: Audit trail of all operations
  • Decentralization: No single point of failure
  • Smart Contracts: Automated proof verification
  • Oracles: Cross-chain pharmacy data verification

Solana Integration

Oneliac uses Solana and the Anchor framework for smart contracts because of:

  • High throughput (65,000+ TPS)
  • Low transaction costs
  • Fast finality (~400ms)
  • Anchor provides safety guarantees

Solana Program Architecture

┌─────────────────────────────────────────────────────────────┐
│ Solana Blockchain (Devnet/Mainnet) │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Healthcare Proof Verifier Program (Anchor) │ │
│ │ │ │
│ │ Instructions: │ │
│ │ - verify_eligibility_proof() │ │
│ │ - verify_prescription_proof() │ │
│ │ - record_transaction() │ │
│ │ - register_ipfs_hash() │ │
│ │ │ │
│ └──────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ On-Chain State Accounts │ │
│ │ - VerificationRegistry (proof records) │ │
│ │ - IPFSHashRegistry (patient data references) │ │
│ │ - OracleResultCache (pharmacy confirmations) │ │
│ │ - TransactionLog (audit trail) │ │
│ └──────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Events (Emitted after verification) │ │
│ │ - ProofVerified(proof_id, eligible) │ │
│ │ - TransactionRecorded(tx_id, timestamp) │ │
│ │ - IPFSHashRegistered(cid) │ │
│ └──────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘

Smart Contract Components

Proof Verifier Account

#[account]
pub struct ProofVerifier {
/// Verification key for Groth16 proofs
pub verification_key: Vec<u8>,

/// Authority who can update the verifier
pub authority: Pubkey,

/// Version of the verifier (for updates)
pub version: u32,
}

Verification Registry Account

#[account]
pub struct VerificationRegistry {
/// Proof ID (hash of proof content)
pub proof_id: [u8; 32],

/// Timestamp of verification
pub verified_at: i64,

/// Eligibility result
pub eligible: bool,

/// Coverage percentage
pub coverage_pct: u32,

/// Patient commitment hash (for privacy)
pub patient_commitment: [u8; 32],

/// Bump seed for PDA derivation
pub bump: u8,
}

IPFS Hash Registry Account

#[account]
pub struct IPFSHashRegistry {
/// IPFS Content Identifier
pub ipfs_cid: String,

/// SHA256 hash of patient data
pub data_hash: [u8; 32],

/// Timestamp of registration
pub registered_at: i64,

/// Patient commitment (privacy-preserving reference)
pub patient_commitment: [u8; 32],

/// Bump seed
pub bump: u8,
}

Instruction Handlers

verify_eligibility_proof

pub fn verify_eligibility_proof(
ctx: Context<VerifyProof>,
proof: Vec<u8>,
public_inputs: Vec<[u8; 32]>,
ipfs_cid: String,
) -> Result<()> {
// Load verifier
let verifier = &ctx.accounts.proof_verifier;

// Verify proof using Groth16
let proof_valid = verify_groth16_proof(
&proof,
&public_inputs,
&verifier.verification_key
)?;

if !proof_valid {
return Err(ProgramError::InvalidArgument.into());
}

// Create or update registry
let registry = &mut ctx.accounts.verification_registry;
registry.proof_id = hash_proof(&proof);
registry.verified_at = Clock::get()?.unix_timestamp;
registry.eligible = true; // Result from proof
registry.ipfs_cid = ipfs_cid.clone();

// Emit event
emit!(ProofVerifiedEvent {
proof_id: registry.proof_id,
eligible: true,
timestamp: registry.verified_at,
});

Ok(())
}

register_ipfs_hash

pub fn register_ipfs_hash(
ctx: Context<RegisterIPFSHash>,
ipfs_cid: String,
data_hash: [u8; 32],
) -> Result<()> {
let registry = &mut ctx.accounts.ipfs_registry;

registry.ipfs_cid = ipfs_cid.clone();
registry.data_hash = data_hash;
registry.registered_at = Clock::get()?.unix_timestamp;

emit!(IPFSHashRegisteredEvent {
ipfs_cid,
data_hash,
timestamp: registry.registered_at,
});

Ok(())
}

Transaction Flow

Eligibility Verification on-chain

1. Client submits request to Oneliac API
2. API generates ZK proof off-chain
3. API builds Solana transaction:
- Program: Healthcare Proof Verifier
- Instruction: verify_eligibility_proof
- Data: proof, public_inputs, ipfs_cid
4. Transaction signed and submitted to Solana
5. RPC endpoint broadcasts to network
6. Validators execute program
- Load proof verifier account
- Verify Groth16 proof
- Create verification registry account
- Emit ProofVerifiedEvent
7. Transaction finalized (~400ms on Solana)
8. API listens for events or polls account state
9. Returns result to client:
{
"eligible": true,
"coverage_pct": 80,
"zk_proof_verified": true,
"transaction_signature": "..."
}

State Account Derivation (PDAs)

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

// Eligibility verification registry PDA
let (registry_pda, bump) = Pubkey::find_program_address(
&[
b"eligibility_registry",
patient_commitment.as_ref(),
proof_id.as_ref(),
],
&PROGRAM_ID,
);

// IPFS hash registry PDA
let (ipfs_pda, bump) = Pubkey::find_program_address(
&[
b"ipfs_registry",
ipfs_cid.as_bytes(),
],
&PROGRAM_ID,
);

Benefits:

  • Accounts are deterministic
  • No separate account creation needed
  • Reduces transaction overhead
  • Predictable address for lookups

Events and Indexing

Emitted Events

#[event]
pub struct ProofVerifiedEvent {
pub proof_id: [u8; 32],
pub eligible: bool,
pub timestamp: i64,
}

#[event]
pub struct TransactionRecordedEvent {
pub tx_id: [u8; 32],
pub operation: String, // "eligibility", "prescription"
pub status: String, // "success", "failed"
pub timestamp: i64,
}

#[event]
pub struct IPFSHashRegisteredEvent {
pub ipfs_cid: String,
pub data_hash: [u8; 32],
pub timestamp: i64,
}

Listening to Events (Client Side)

# Using Solana Web3.py
from solders.rpc.responses import DataSlice

async def listen_for_proof_events():
# Subscribe to program logs
with ws_client.subscription_ctx(
await ws_client.logs_subscribe(
logs_subscribe_filter=RpcTransactionLogsFilter(
mention=PROGRAM_ID
)
)
) as logs:
async for log in logs:
# Parse log to extract ProofVerifiedEvent
if "ProofVerified" in log.value.logs:
# Extract event data
proof_id = parse_event_log(log)
print(f"Proof verified: {proof_id}")

Data Integrity Checks

Verification Sequence

  1. Proof Verification: Groth16 verifier confirms proof cryptography
  2. Public Input Validation: Confirm procedure code, insurance ID valid
  3. IPFS Hash Check: Verify SHA256 hash matches registered hash
  4. Patient Commitment: Ensure proof matches patient identity (privacy-preserving)
  5. Timestamp Validation: Reject old proofs (prevent replay)

On-Chain Verification Code

// Validate IPFS hash against registered data
pub fn verify_data_integrity(
ipfs_cid: &str,
data_hash: &[u8; 32],
ipfs_registry: &IPFSHashRegistry,
) -> Result<()> {
if ipfs_registry.ipfs_cid != ipfs_cid {
return Err(ProgramError::InvalidArgument.into());
}

if ipfs_registry.data_hash != *data_hash {
return Err(ProgramError::InvalidArgument.into());
}

Ok(())
}

Fee Structure

Solana transaction costs are paid in Lamports (1 SOL = 1,000,000,000 lamports):

OperationLamportsCost (approx)
Proof verification5,000$0.0015
IPFS hash registration2,500$0.0008
Account creation (registry)890,880$0.26

These are typical estimates; actual costs vary with network congestion.

Cost Optimization

  1. PDA Accounts: Created only once per patient-procedure pair
  2. Batching: Multiple operations in single transaction
  3. Devnet Testing: Free on testnet before mainnet deployment
  4. Account Reuse: Update existing accounts instead of creating new ones

Network Selection

Development

Network: Solana Devnet
Endpoint: https://api.devnet.solana.com
RPC Speed: ~400ms finality
Cost: Free (airdrop SOL)

Production

Network: Solana Mainnet-Beta
Endpoint: https://api.mainnet-beta.solana.com
RPC Speed: ~400ms finality
Cost: ~$0.00025-0.001 per transaction

Best Practices

  1. Key Management: Use hardware wallets for production keys
  2. Error Handling: Always verify transaction success before returning to client
  3. Monitoring: Track transaction success rates and fees
  4. Event Indexing: Use Solana indexers (Marinade, Magic Eden API) for event querying
  5. Idempotency: Design transactions to be idempotent (safe to retry)

Troubleshooting

Common Issues

IssueCauseSolution
Proof verification failsInvalid proof or wrong verifier keyRegenerate proof, verify circuit
Account too smallInsufficient space allocatedIncrease account rent
Rate limited by RPCToo many requestsUse private RPC endpoint or batch requests
Transaction expiredBlockhash too oldUse fresh blockhash (< 2 min old)

Next Steps