Cryptographic Foundations

Deep dive into the cryptographic primitives underlying Zeris: hash functions, timestamp proofs, Merkle trees, and deterministic sequencing.

Hash Functions

Zeris relies on SHA256 as its primary cryptographic hash function. SHA256 provides:

  • Pre-image resistance - Computationally infeasible to find input given output
  • Second pre-image resistance - Cannot find different input with same hash
  • Collision resistance - Extremely difficult to find two inputs with identical hash

SHA256 Properties

  • Output size: 256 bits (32 bytes)
  • Block size: 512 bits
  • Security level: 2^128 operations for collision
  • Typical performance: ~300 MB/s on modern CPUs
rust
use sha2::{Sha256, Digest};

fn compute_hash(data: &[u8]) -> [u8; 32] {
    let mut hasher = Sha256::new();
    hasher.update(data);
    hasher.finalize().into()
}

// Sequential hashing for PoH
fn poh_chain(genesis: [u8; 32], ticks: u64) -> [u8; 32] {
    let mut current = genesis;
    for _ in 0..ticks {
        current = compute_hash(&current);
    }
    current
}

Timestamp Proofs

Zeris constructs cryptographic timestamp proofs using sequential hashing. The key insight is that computing n sequential hashes requires at minimum n × t_hash time.

Verifiable Delay Function (VDF)

A VDF is a function that requires a specified number of sequential steps to compute but can be efficiently verified:

VDF(input, difficulty) → (output, proof)
Evaluation time: O(difficulty)
Verification time: O(log difficulty)
rust
struct TimestampProof {
    input: [u8; 32],
    output: [u8; 32],
    num_iterations: u64,
    checkpoints: Vec<[u8; 32]>,
}

fn generate_timestamp_proof(
    input: [u8; 32],
    num_iterations: u64
) -> TimestampProof {
    let checkpoint_interval = num_iterations / 8;
    let mut checkpoints = Vec::new();
    let mut current = input;
    
    for i in 0..num_iterations {
        current = compute_hash(&current);
        if i % checkpoint_interval == 0 {
            checkpoints.push(current);
        }
    }
    
    TimestampProof {
        input,
        output: current,
        num_iterations,
        checkpoints,
    }
}

fn verify_timestamp_proof(proof: &TimestampProof) -> bool {
    let checkpoint_interval = proof.num_iterations / 8;
    let mut current = proof.input;
    
    for (i, checkpoint) in proof.checkpoints.iter().enumerate() {
        // Compute hashes until checkpoint
        for _ in 0..checkpoint_interval {
            current = compute_hash(&current);
        }
        // Verify checkpoint matches
        if current != *checkpoint {
            return false;
        }
    }
    
    current == proof.output
}

Merkle Branching

Merkle trees enable efficient proof of inclusion for transactions. A Merkle tree is a binary tree where each leaf is a transaction hash and each internal node is the hash of its children.

Merkle Tree Construction

typescript
class MerkleTree {
  private leaves: Buffer[];
  private layers: Buffer[][];
  
  constructor(leaves: Buffer[]) {
    this.leaves = leaves;
    this.layers = this.buildLayers(leaves);
  }
  
  private buildLayers(leaves: Buffer[]): Buffer[][] {
    const layers = [leaves];
    
    while (layers[layers.length - 1].length > 1) {
      const currentLayer = layers[layers.length - 1];
      const nextLayer: Buffer[] = [];
      
      for (let i = 0; i < currentLayer.length; i += 2) {
        const left = currentLayer[i];
        const right = i + 1 < currentLayer.length 
          ? currentLayer[i + 1] 
          : left; // Duplicate if odd number
        
        const combined = Buffer.concat([left, right]);
        nextLayer.push(sha256(combined));
      }
      
      layers.push(nextLayer);
    }
    
    return layers;
  }
  
  getRoot(): Buffer {
    return this.layers[this.layers.length - 1][0];
  }
  
  getProof(index: number): Buffer[] {
    const proof: Buffer[] = [];
    
    for (let i = 0; i < this.layers.length - 1; i++) {
      const layer = this.layers[i];
      const isRightNode = index % 2 === 1;
      const siblingIndex = isRightNode ? index - 1 : index + 1;
      
      if (siblingIndex < layer.length) {
        proof.push(layer[siblingIndex]);
      }
      
      index = Math.floor(index / 2);
    }
    
    return proof;
  }
}

Merkle Proof Verification

typescript
function verifyMerkleProof(
  leaf: Buffer,
  proof: Buffer[],
  root: Buffer,
  index: number
): boolean {
  let computedHash = leaf;
  
  for (const proofElement of proof) {
    const isRightNode = index % 2 === 1;
    
    if (isRightNode) {
      computedHash = sha256(
        Buffer.concat([proofElement, computedHash])
      );
    } else {
      computedHash = sha256(
        Buffer.concat([computedHash, proofElement])
      );
    }
    
    index = Math.floor(index / 2);
  }
  
  return computedHash.equals(root);
}

Merkle proofs require O(log n) space and verification time, making them highly efficient for large transaction sets.

Deterministic Sequencing

Zeris ensures deterministic ordering through a combination of PoH timestamps and content-addressed hashing.

Transaction Ordering Algorithm

rust
#[derive(Debug, Clone)]
struct OrderedTransaction {
    tx_hash: [u8; 32],
    poh_tick: u64,
    pow_nonce: u64,
    timestamp: i64,
}

impl PartialOrd for OrderedTransaction {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for OrderedTransaction {
    fn cmp(&self, other: &Self) -> Ordering {
        // Primary: PoH tick (deterministic time)
        match self.poh_tick.cmp(&other.poh_tick) {
            Ordering::Equal => {
                // Secondary: Transaction hash (deterministic tiebreaker)
                self.tx_hash.cmp(&other.tx_hash)
            }
            other => other,
        }
    }
}

fn order_transactions(txs: &mut [OrderedTransaction]) {
    txs.sort(); // Uses Ord implementation above
    
    // Verify ordering is deterministic
    assert!(is_strictly_ordered(txs));
}

fn is_strictly_ordered(txs: &[OrderedTransaction]) -> bool {
    txs.windows(2).all(|w| w[0] < w[1])
}

Sybil Cost Model

The PoW layer imposes quantifiable costs on creating multiple identities:

Economic Attack Analysis

Given:

  • C = Cost per identity (PoW computation)
  • N = Number of sybil identities
  • B = Benefit from attack

An attack is profitable only if:

B > N × C

By tuning PoW difficulty, Zeris ensures N × C exceeds any realistic attack benefit.

Cryptographic Security Parameters

ParameterValueSecurity Level
Hash FunctionSHA256128-bit collision resistance
Min PoW Difficulty16 bits~65k average attempts
Max PoW Difficulty32 bits~4B average attempts
PoH Tick Rate1000/sec1ms timestamp resolution
PoS Threshold67%Byzantine fault tolerant

Next Sections