Skip to main content

quantrs2_ml/
blockchain.rs

1//! Quantum-secured blockchain primitives.
2//!
3//! Implements quantum-safe consensus mechanisms, quantum-signed transactions,
4//! and a simulated quantum blockchain ledger using post-quantum and
5//! QKD-based cryptographic primitives from the `crypto` module.
6
7use crate::crypto::{sha256, QuantumSignatureVerifyingKey};
8use crate::error::{MLError, Result};
9use std::collections::HashMap;
10use std::fmt;
11use std::time::{Duration, SystemTime, UNIX_EPOCH};
12
13/// Type of consensus algorithm for quantum blockchains
14#[derive(Debug, Clone, Copy, PartialEq)]
15pub enum ConsensusType {
16    /// Quantum-secured Proof of Work
17    QuantumProofOfWork,
18
19    /// Quantum-secured Proof of Stake
20    QuantumProofOfStake,
21
22    /// Quantum Byzantine Agreement
23    QuantumByzantineAgreement,
24
25    /// Quantum Federated Consensus
26    QuantumFederated,
27}
28
29/// Represents a transaction in a quantum blockchain
30#[derive(Debug, Clone)]
31pub struct Transaction {
32    /// Sender's serialized [`QuantumSignatureVerifyingKey`]
33    /// (see [`QuantumSignatureVerifyingKey::to_bytes`]), used to verify
34    /// `signature` against this transaction's signing message. Not merely a
35    /// hash of the key: signature verification needs the actual public key.
36    pub sender: Vec<u8>,
37
38    /// Recipient's public key bytes
39    pub recipient: Vec<u8>,
40
41    /// Amount to transfer
42    pub amount: f64,
43
44    /// Additional data (can be used for smart contracts)
45    pub data: Vec<u8>,
46
47    /// Transaction timestamp
48    timestamp: u64,
49
50    /// Transaction signature (a Lamport one-time signature produced by
51    /// [`crate::crypto::QuantumSignature::sign`] over `signing_message()`)
52    signature: Option<Vec<u8>>,
53}
54
55impl Transaction {
56    /// Creates a new transaction
57    pub fn new(sender: Vec<u8>, recipient: Vec<u8>, amount: f64, data: Vec<u8>) -> Self {
58        let timestamp = SystemTime::now()
59            .duration_since(UNIX_EPOCH)
60            .unwrap_or(Duration::from_secs(0))
61            .as_secs();
62
63        Transaction {
64            sender,
65            recipient,
66            amount,
67            data,
68            timestamp,
69            signature: None,
70        }
71    }
72
73    /// Signs the transaction by attaching a pre-computed signature (produced
74    /// by, e.g., `QuantumSignature::sign(&transaction.signing_message())`).
75    pub fn sign(&mut self, signature: Vec<u8>) -> Result<()> {
76        self.signature = Some(signature);
77        Ok(())
78    }
79
80    /// The canonical byte sequence this transaction's signature is computed
81    /// (and verified) over: every field except the signature itself.
82    fn signing_message(&self) -> Vec<u8> {
83        let mut message = Vec::new();
84        message.extend_from_slice(&self.sender);
85        message.extend_from_slice(&self.recipient);
86        message.extend_from_slice(&self.amount.to_be_bytes());
87        message.extend_from_slice(&self.timestamp.to_be_bytes());
88        message.extend_from_slice(&self.data);
89        message
90    }
91
92    /// Verifies the transaction signature against `sender`'s public key.
93    ///
94    /// Returns `Ok(false)` (rather than an error) both when there is no
95    /// signature to check and when `sender` cannot be parsed as a
96    /// [`QuantumSignatureVerifyingKey`] -- either way the transaction is not
97    /// validly signed.
98    pub fn verify(&self) -> Result<bool> {
99        let signature = match &self.signature {
100            Some(signature) => signature,
101            None => return Ok(false),
102        };
103        let verifying_key = match QuantumSignatureVerifyingKey::from_bytes(&self.sender) {
104            Ok(key) => key,
105            Err(_) => return Ok(false),
106        };
107        verifying_key.verify(&self.signing_message(), signature)
108    }
109
110    /// Gets the transaction hash: the real SHA-256 digest (see
111    /// [`crate::crypto::sha256`]) of every transaction field, replacing the
112    /// previous plain byte concatenation (which had no cryptographic
113    /// diffusion and was trivially invertible/collidable).
114    pub fn hash(&self) -> Vec<u8> {
115        let mut preimage = self.signing_message();
116        // The signing message intentionally excludes the signature (it is
117        // what gets signed), but the transaction *hash* should still be
118        // sensitive to it so that a re-signed/re-attached signature changes
119        // the hash.
120        if let Some(signature) = &self.signature {
121            preimage.extend_from_slice(signature);
122        }
123        sha256::digest(&preimage).to_vec()
124    }
125}
126
127/// Represents a block in a quantum blockchain
128#[derive(Debug, Clone)]
129pub struct Block {
130    /// Block index
131    pub index: usize,
132
133    /// Previous block hash
134    pub previous_hash: Vec<u8>,
135
136    /// Block timestamp
137    pub timestamp: u64,
138
139    /// Transactions in the block
140    pub transactions: Vec<Transaction>,
141
142    /// Nonce for proof of work
143    pub nonce: u64,
144
145    /// Block hash
146    pub hash: Vec<u8>,
147}
148
149impl Block {
150    /// Creates a new block
151    pub fn new(index: usize, previous_hash: Vec<u8>, transactions: Vec<Transaction>) -> Self {
152        let timestamp = SystemTime::now()
153            .duration_since(UNIX_EPOCH)
154            .unwrap_or(Duration::from_secs(0))
155            .as_secs();
156
157        let mut block = Block {
158            index,
159            previous_hash,
160            timestamp,
161            transactions,
162            nonce: 0,
163            hash: Vec::new(),
164        };
165
166        block.hash = block.calculate_hash();
167
168        block
169    }
170
171    /// Calculates the block hash: the real SHA-256 digest (see
172    /// [`crate::crypto::sha256`]) of the block's index, previous hash,
173    /// timestamp, transaction hashes, and nonce, replacing the previous
174    /// plain byte concatenation (which had no cryptographic diffusion, so
175    /// mining by nonce search or tampering with a transaction was not
176    /// meaningfully harder than editing the "hash" bytes directly).
177    pub fn calculate_hash(&self) -> Vec<u8> {
178        let mut preimage = Vec::new();
179
180        preimage.extend_from_slice(&(self.index as u64).to_be_bytes());
181        preimage.extend_from_slice(&self.previous_hash);
182        preimage.extend_from_slice(&self.timestamp.to_be_bytes());
183
184        for transaction in &self.transactions {
185            preimage.extend_from_slice(&transaction.hash());
186        }
187
188        preimage.extend_from_slice(&self.nonce.to_be_bytes());
189
190        sha256::digest(&preimage).to_vec()
191    }
192
193    /// Mines the block with proof of work
194    pub fn mine(&mut self, difficulty: usize) -> Result<()> {
195        // `hash` is now always a fixed 32-byte SHA-256 digest (previously it
196        // was a variable-length, unhashed byte concatenation whose length
197        // happened to grow with the number of fields/transactions); clamp
198        // the number of leading zero bytes we demand so an overly large
199        // `difficulty` cannot slice past the end of the digest.
200        let target_len = (difficulty / 8 + 1).min(self.hash.len());
201        let target = vec![0u8; target_len];
202
203        while self.hash[0..target_len] != target[..] {
204            self.nonce += 1;
205            self.hash = self.calculate_hash();
206
207            // Optional: add a check to prevent infinite loops
208            if self.nonce > 1_000_000 {
209                return Err(MLError::MLOperationError(
210                    "Mining took too long. Consider reducing difficulty.".to_string(),
211                ));
212            }
213        }
214
215        Ok(())
216    }
217
218    /// Verifies the block
219    pub fn verify(&self, previous_hash: &[u8]) -> Result<bool> {
220        // This is a dummy implementation
221        // In a real system, this would verify the block
222
223        if self.previous_hash != previous_hash {
224            return Ok(false);
225        }
226
227        let calculated_hash = self.calculate_hash();
228        if self.hash != calculated_hash {
229            return Ok(false);
230        }
231
232        for transaction in &self.transactions {
233            if !transaction.verify()? {
234                return Ok(false);
235            }
236        }
237
238        Ok(true)
239    }
240}
241
242/// Smart contract for quantum blockchains
243#[derive(Debug, Clone)]
244pub struct SmartContract {
245    /// Contract bytecode
246    pub bytecode: Vec<u8>,
247
248    /// Contract owner
249    pub owner: Vec<u8>,
250
251    /// Contract state
252    pub state: HashMap<Vec<u8>, Vec<u8>>,
253}
254
255impl SmartContract {
256    /// Creates a new smart contract
257    pub fn new(bytecode: Vec<u8>, owner: Vec<u8>) -> Self {
258        SmartContract {
259            bytecode,
260            owner,
261            state: HashMap::new(),
262        }
263    }
264
265    /// Executes the contract
266    pub fn execute(&mut self, input: &[u8]) -> Result<Vec<u8>> {
267        // This is a dummy implementation
268        // In a real system, this would execute the contract bytecode
269
270        if input.is_empty() {
271            return Err(MLError::InvalidParameter("Input is empty".to_string()));
272        }
273
274        let operation = input[0];
275
276        match operation {
277            0 => {
278                // Store operation
279                if input.len() < 3 {
280                    return Err(MLError::InvalidParameter("Invalid store input".to_string()));
281                }
282
283                let key = vec![input[1]];
284                let value = vec![input[2]];
285
286                self.state.insert(key, value.clone());
287
288                Ok(value)
289            }
290            1 => {
291                // Load operation
292                if input.len() < 2 {
293                    return Err(MLError::InvalidParameter("Invalid load input".to_string()));
294                }
295
296                let key = vec![input[1]];
297
298                let value = self.state.get(&key).ok_or_else(|| {
299                    MLError::MLOperationError(format!("Key not found: {:?}", key))
300                })?;
301
302                Ok(value.clone())
303            }
304            _ => Err(MLError::InvalidParameter(format!(
305                "Invalid operation: {}",
306                operation
307            ))),
308        }
309    }
310}
311
312/// Quantum token for digital assets
313#[derive(Debug, Clone)]
314pub struct QuantumToken {
315    /// Token name
316    pub name: String,
317
318    /// Token symbol
319    pub symbol: String,
320
321    /// Total supply
322    pub total_supply: u64,
323
324    /// Balances for addresses
325    pub balances: HashMap<Vec<u8>, u64>,
326}
327
328impl QuantumToken {
329    /// Creates a new quantum token
330    pub fn new(name: &str, symbol: &str, total_supply: u64, owner: Vec<u8>) -> Self {
331        let mut balances = HashMap::new();
332        balances.insert(owner, total_supply);
333
334        QuantumToken {
335            name: name.to_string(),
336            symbol: symbol.to_string(),
337            total_supply,
338            balances,
339        }
340    }
341
342    /// Transfers tokens from one address to another
343    pub fn transfer(&mut self, from: &[u8], to: &[u8], amount: u64) -> Result<()> {
344        // Get the from balance first and copy it
345        let from_balance = *self.balances.get(from).ok_or_else(|| {
346            MLError::MLOperationError(format!("From address not found: {:?}", from))
347        })?;
348
349        if from_balance < amount {
350            return Err(MLError::MLOperationError(format!(
351                "Insufficient balance: {} < {}",
352                from_balance, amount
353            )));
354        }
355
356        // Update from balance
357        self.balances.insert(from.to_vec(), from_balance - amount);
358
359        // Update to balance
360        let to_balance = self.balances.entry(to.to_vec()).or_insert(0);
361        *to_balance += amount;
362
363        Ok(())
364    }
365
366    /// Gets the balance for an address
367    pub fn balance_of(&self, address: &[u8]) -> u64 {
368        self.balances.get(address).cloned().unwrap_or(0)
369    }
370}
371
372/// Quantum blockchain with distributed ledger
373#[derive(Debug, Clone)]
374pub struct QuantumBlockchain {
375    /// Chain of blocks
376    pub chain: Vec<Block>,
377
378    /// Pending transactions
379    pub pending_transactions: Vec<Transaction>,
380
381    /// Mining difficulty
382    pub difficulty: usize,
383
384    /// Consensus algorithm
385    pub consensus: ConsensusType,
386
387    /// Network nodes
388    pub nodes: Vec<String>,
389}
390
391impl QuantumBlockchain {
392    /// Creates a new quantum blockchain
393    pub fn new(consensus: ConsensusType, difficulty: usize) -> Self {
394        // Create genesis block
395        let genesis_block = Block::new(0, vec![0u8; 32], Vec::new());
396
397        QuantumBlockchain {
398            chain: vec![genesis_block],
399            pending_transactions: Vec::new(),
400            difficulty,
401            consensus,
402            nodes: Vec::new(),
403        }
404    }
405
406    /// Adds a transaction to the pending transactions
407    pub fn add_transaction(&mut self, transaction: Transaction) -> Result<()> {
408        // Verify transaction
409        if !transaction.verify()? {
410            return Err(MLError::MLOperationError(
411                "Transaction verification failed".to_string(),
412            ));
413        }
414
415        self.pending_transactions.push(transaction);
416
417        Ok(())
418    }
419
420    /// Mines a new block
421    pub fn mine_block(&mut self) -> Result<Block> {
422        if self.pending_transactions.is_empty() {
423            return Err(MLError::MLOperationError(
424                "No pending transactions to mine".to_string(),
425            ));
426        }
427
428        let transactions = self.pending_transactions.clone();
429        self.pending_transactions.clear();
430
431        let previous_block = self
432            .chain
433            .last()
434            .ok_or_else(|| MLError::MLOperationError("Blockchain is empty".to_string()))?;
435
436        let mut block = Block::new(self.chain.len(), previous_block.hash.clone(), transactions);
437
438        // Mine the block based on consensus algorithm
439        match self.consensus {
440            ConsensusType::QuantumProofOfWork => {
441                block.mine(self.difficulty)?;
442            }
443            _ => {
444                // Other consensus algorithms (simplified for example)
445                block.hash = block.calculate_hash();
446            }
447        }
448
449        self.chain.push(block.clone());
450
451        Ok(block)
452    }
453
454    /// Verifies the blockchain
455    pub fn verify(&self) -> Result<bool> {
456        for i in 1..self.chain.len() {
457            let current_block = &self.chain[i];
458            let previous_block = &self.chain[i - 1];
459
460            if !current_block.verify(&previous_block.hash)? {
461                return Ok(false);
462            }
463        }
464
465        Ok(true)
466    }
467
468    /// Alias for verify() - to match the example call
469    pub fn verify_chain(&self) -> Result<bool> {
470        self.verify()
471    }
472
473    /// Gets a blockchain with a tampered block for testing
474    pub fn tamper_with_block(
475        &self,
476        block_index: usize,
477        sender: &str,
478        amount: f64,
479    ) -> Result<QuantumBlockchain> {
480        if block_index >= self.chain.len() {
481            return Err(MLError::MLOperationError(format!(
482                "Block index out of range: {}",
483                block_index
484            )));
485        }
486
487        let mut tampered = self.clone();
488
489        // Create a tampered transaction
490        let tampered_transaction = Transaction::new(
491            sender.as_bytes().to_vec(),
492            vec![1, 2, 3, 4],
493            amount,
494            Vec::new(),
495        );
496
497        // Replace the first transaction in the block
498        if !tampered.chain[block_index].transactions.is_empty() {
499            tampered.chain[block_index].transactions[0] = tampered_transaction;
500        } else {
501            tampered.chain[block_index]
502                .transactions
503                .push(tampered_transaction);
504        }
505
506        // Recalculate the hash (but don't fix it)
507        let hash = tampered.chain[block_index].calculate_hash();
508        tampered.chain[block_index].hash = hash;
509
510        Ok(tampered)
511    }
512}
513
514impl fmt::Display for ConsensusType {
515    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
516        match self {
517            ConsensusType::QuantumProofOfWork => write!(f, "Quantum Proof of Work"),
518            ConsensusType::QuantumProofOfStake => write!(f, "Quantum Proof of Stake"),
519            ConsensusType::QuantumByzantineAgreement => write!(f, "Quantum Byzantine Agreement"),
520            ConsensusType::QuantumFederated => write!(f, "Quantum Federated Consensus"),
521        }
522    }
523}
524
525#[cfg(test)]
526mod integrity_regression_tests {
527    use super::*;
528    use crate::crypto::QuantumSignature;
529
530    fn signed_transaction(
531        signer: &QuantumSignature,
532        recipient: Vec<u8>,
533        amount: f64,
534    ) -> Transaction {
535        let mut tx = Transaction::new(signer.public_key_bytes(), recipient, amount, Vec::new());
536        let signature = signer.sign(&tx.signing_message()).expect("sign");
537        tx.sign(signature).expect("attach signature");
538        tx
539    }
540
541    /// Regression test for the "verify() only checks Option::is_some()" bug:
542    /// a properly signed transaction must verify true, and tampering with
543    /// any signed field afterward must make it verify false.
544    #[test]
545    fn transaction_verify_checks_the_real_signature() {
546        let signer = QuantumSignature::new(64, "lamport-test").expect("key generation");
547        let mut tx = signed_transaction(&signer, vec![9, 9, 9], 42.0);
548        assert!(tx.verify().expect("verify should not error"));
549
550        // Tampering with the amount after signing must invalidate it.
551        tx.amount = 999.0;
552        assert!(!tx.verify().expect("verify should not error"));
553    }
554
555    #[test]
556    fn transaction_without_signature_never_verifies() {
557        let tx = Transaction::new(vec![1, 2, 3], vec![4, 5, 6], 1.0, Vec::new());
558        assert!(!tx.verify().expect("verify should not error"));
559    }
560
561    /// Regression test for the "hash is plain byte concatenation" bug: the
562    /// transaction/block hash must behave like a real cryptographic hash --
563    /// fixed-size and radically different for even a tiny input change
564    /// (avalanche effect) -- rather than an easily-inspected concatenation.
565    #[test]
566    fn transaction_hash_is_fixed_size_and_diffuses_small_changes() {
567        let signer = QuantumSignature::new(64, "lamport-test").expect("key generation");
568        let tx_a = signed_transaction(&signer, vec![9, 9, 9], 42.0);
569        let mut tx_b = tx_a.clone();
570        tx_b.amount = 42.000001;
571
572        let hash_a = tx_a.hash();
573        let hash_b = tx_b.hash();
574        assert_eq!(hash_a.len(), 32, "expected a 32-byte SHA-256 digest");
575        assert_eq!(hash_b.len(), 32, "expected a 32-byte SHA-256 digest");
576        assert_ne!(hash_a, hash_b);
577
578        // Avalanche effect: roughly half the bits should differ for a
579        // one-bit-ish change in the input, not just a few trailing bytes as
580        // plain concatenation would produce.
581        let differing_bits: u32 = hash_a
582            .iter()
583            .zip(hash_b.iter())
584            .map(|(a, b)| (a ^ b).count_ones())
585            .sum();
586        assert!(
587            differing_bits > 32,
588            "expected substantial bit diffusion, got {differing_bits} differing bits"
589        );
590    }
591
592    /// End-to-end regression test: mining and verifying a small chain with
593    /// genuinely signed transactions.
594    #[test]
595    fn mined_chain_with_signed_transactions_verifies() {
596        let signer = QuantumSignature::new(48, "lamport-test").expect("key generation");
597        let mut blockchain = QuantumBlockchain::new(ConsensusType::QuantumProofOfWork, 8);
598
599        blockchain
600            .add_transaction(signed_transaction(&signer, vec![1, 2, 3], 5.0))
601            .expect("adding a validly signed transaction should succeed");
602        blockchain.mine_block().expect("mining should succeed");
603
604        assert!(blockchain.verify().expect("chain should verify"));
605    }
606
607    /// Adding a transaction with an invalid/missing signature must be
608    /// rejected up front, not silently accepted.
609    #[test]
610    fn add_transaction_rejects_unsigned_transaction() {
611        let mut blockchain = QuantumBlockchain::new(ConsensusType::QuantumProofOfWork, 8);
612        let unsigned = Transaction::new(vec![1, 2, 3], vec![4, 5, 6], 1.0, Vec::new());
613        assert!(blockchain.add_transaction(unsigned).is_err());
614    }
615}