diff --git a/SwiftChain/Models/Block.swift b/SwiftChain/Models/Block.swift index fd4c083..53abb02 100644 --- a/SwiftChain/Models/Block.swift +++ b/SwiftChain/Models/Block.swift @@ -9,7 +9,7 @@ import Foundation import CryptoSwift -class Block { +class Block: Codable { var hash: String var transactions: [Transaction] var previousHash: String @@ -19,6 +19,10 @@ class Block { var difficulty: Int var miner: String? // Adresse qui recevra la récompense + enum CodingKeys: String, CodingKey { + case hash, transactions, previousHash, index, nonce, timestamp, difficulty, miner + } + init(hash: String = "", transactions: [Transaction] = [], previousHash: String = "", index: Int = 0, nonce: Int = 0, timestamp: Int = 0, difficulty: Int = 4) { self.hash = hash @@ -30,6 +34,30 @@ class Block { self.difficulty = difficulty } + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + hash = try container.decode(String.self, forKey: .hash) + transactions = try container.decode([Transaction].self, forKey: .transactions) + previousHash = try container.decode(String.self, forKey: .previousHash) + index = try container.decode(Int.self, forKey: .index) + nonce = try container.decode(Int.self, forKey: .nonce) + timestamp = try container.decode(Int.self, forKey: .timestamp) + difficulty = try container.decode(Int.self, forKey: .difficulty) + miner = try container.decodeIfPresent(String.self, forKey: .miner) + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(hash, forKey: .hash) + try container.encode(transactions, forKey: .transactions) + try container.encode(previousHash, forKey: .previousHash) + try container.encode(index, forKey: .index) + try container.encode(nonce, forKey: .nonce) + try container.encode(timestamp, forKey: .timestamp) + try container.encode(difficulty, forKey: .difficulty) + try container.encodeIfPresent(miner, forKey: .miner) + } + func mineBlock() -> Double { let startTime = Date() let target = String(repeating: "0", count: difficulty) diff --git a/SwiftChain/Models/Blockchain.swift b/SwiftChain/Models/Blockchain.swift index 0df73f0..a48f6a3 100644 --- a/SwiftChain/Models/Blockchain.swift +++ b/SwiftChain/Models/Blockchain.swift @@ -116,4 +116,62 @@ class Blockchain { } return true } + + func validateChain(_ newChain: [Block]) -> Bool { + // Vérifier que la chaîne commence par notre genesis block + guard newChain.first?.hash == chain.first?.hash else { + print("Genesis block mismatch") + return false + } + + // Vérifier chaque bloc + for i in 1.. Bool { + // Vérifier que la nouvelle chaîne est plus longue + guard newChain.count > chain.count else { + print("Received chain is not longer than current chain") + return false + } + + // Vérifier la validité de la nouvelle chaîne + guard validateChain(newChain) else { + print("Received chain is invalid") + return false + } + + // Sauvegarder l'ancienne chaîne au cas où + let oldChain = chain + + // Essayer de traiter tous les blocs + for block in newChain { + if !accountManager.processBlock(block) { + print("Failed to process transactions in block \(block.index)") + chain = oldChain + return false + } + } + + chain = newChain + print("Chain replaced successfully") + return true + } } diff --git a/SwiftChain/Models/MemPool.swift b/SwiftChain/Models/MemPool.swift index 1e4edee..ea92ccc 100644 --- a/SwiftChain/Models/MemPool.swift +++ b/SwiftChain/Models/MemPool.swift @@ -94,4 +94,37 @@ class MemPool { func getAllPendingTransactions() -> [Transaction] { return pendingTransactions } + + /** + * Nettoie le mempool en retirant les transactions qui sont dans la chaîne + */ + func cleanupMempool(chain: [Block]) { + var remainingTransactions: [Transaction] = [] + + for pendingTx in pendingTransactions { + // On vérifie si la transaction est dans un des blocs + var isInChain = false + + for block in chain { + if block.transactions.contains(where: { chainTx in + chainTx.sender == pendingTx.sender && + chainTx.receiver == pendingTx.receiver && + chainTx.amount == pendingTx.amount && + chainTx.type == pendingTx.type + }) { + isInChain = true + break + } + } + + // Si la transaction n'est pas dans la chaîne, on la garde + if !isInChain { + remainingTransactions.append(pendingTx) + } + } + + // Mettre à jour les transactions en attente + print("Mempool cleanup: removed \(pendingTransactions.count - remainingTransactions.count) transactions") + pendingTransactions = remainingTransactions + } } diff --git a/SwiftChain/Models/Node.swift b/SwiftChain/Models/Node.swift index ac4ce1d..a8759ca 100644 --- a/SwiftChain/Models/Node.swift +++ b/SwiftChain/Models/Node.swift @@ -24,6 +24,9 @@ class Node { enum MessageType: String, Codable { case transaction = "TX" + case blockchainRequest = "BC_REQ" + case blockchainResponse = "BC_RES" + case newBlock = "NEW_BLOCK" } struct NetworkMessage: Codable { @@ -152,21 +155,146 @@ class Node { case .transaction: let transaction = try JSONDecoder().decode(Transaction.self, from: message.data) print("Transaction decoded: \(transaction.sender) -> \(transaction.receiver): \(transaction.amount)") - if blockchain.submitTransaction(transaction) { print("Transaction added to mempool successfully") } else { print("Failed to add transaction to mempool") } + + case .blockchainRequest: + // Envoyer notre blockchain au pair + sendBlockchain(to: connection) + + case .blockchainResponse: + // Recevoir et traiter la blockchain + let receivedChain = try JSONDecoder().decode([Block].self, from: message.data) + handleReceivedBlockchain(receivedChain) + + case .newBlock: + // Recevoir et traiter un nouveau bloc + let block = try JSONDecoder().decode(Block.self, from: message.data) + handleReceivedBlock(block) } } catch { print("Failed to decode received data: \(error)") } } + // Quand un pair se connecte, demander sa blockchain private func handleNewConnection(_ connection: NWConnection) { connection.start(queue: queue) peers.append(connection) startReceiving(connection) + + // Demander la blockchain + requestBlockchain(from: connection) + } + + private func requestBlockchain(from peer: NWConnection) { + do { + let message = NetworkMessage(type: .blockchainRequest, data: Data()) + let messageData = try JSONEncoder().encode(message) + + peer.send(content: messageData, completion: .contentProcessed { error in + if let error = error { + print("Failed to request blockchain: \(error)") + } else { + print("Blockchain request sent to: \(peer.endpoint)") + } + }) + } catch { + print("Failed to encode blockchain request: \(error)") + } + } + + private func sendBlockchain(to peer: NWConnection) { + do { + let chainData = try JSONEncoder().encode(blockchain.chain) + let message = NetworkMessage(type: .blockchainResponse, data: chainData) + let messageData = try JSONEncoder().encode(message) + + peer.send(content: messageData, completion: .contentProcessed { error in + if let error = error { + print("Failed to send blockchain: \(error)") + } else { + print("Blockchain sent to: \(peer.endpoint)") + } + }) + } catch { + print("Failed to encode blockchain: \(error)") + } + } + + private func handleReceivedBlockchain(_ receivedChain: [Block]) { + print("Received blockchain with \(receivedChain.count) blocks") + + // Vérifier si la chaîne reçue est plus longue et valide + if receivedChain.count > blockchain.chain.count { + print("Received chain is longer, validating...") + if blockchain.replaceChain(receivedChain) { + print("Blockchain updated successfully") + // Nettoyer le mempool des transactions qui sont maintenant dans la chaîne + blockchain.memPool.cleanupMempool(chain: receivedChain) + } else { + print("Failed to update blockchain") + } + } else { + print("Current chain is longer or equal, keeping it") + } + } + + private func handleReceivedBlock(_ block: Block) { + print("Received new block: \(block.hash)") + + // Vérifier que le bloc suit bien notre dernier bloc + guard let lastBlock = blockchain.chain.last else { + print("No existing chain") + return + } + + if block.previousHash != lastBlock.hash { + print("Block does not connect to our chain") + return + } + + if !block.isValid() { + print("Block is not valid") + return + } + + // Traiter les transactions du bloc + if !blockchain.accountManager.processBlock(block) { + print("Failed to process block transactions") + return + } + + // Ajouter le bloc à la chaîne + blockchain.chain.append(block) + print("New block added to chain") + + // Propager le bloc aux autres pairs (sauf celui qui nous l'a envoyé) + broadcastBlock(block) + } + + private func broadcastBlock(_ block: Block) { + do { + let blockData = try JSONEncoder().encode(block) + let message = NetworkMessage(type: .newBlock, data: blockData) + let messageData = try JSONEncoder().encode(message) + + print("Broadcasting block to peers") + + for peer in peers { + peer.send(content: messageData, completion: .contentProcessed { error in + if let error = error { + print("Failed to send block: \(error)") + } else { + print("Block sent successfully to: \(peer.endpoint)") + } + }) + } + } catch { + print("Failed to encode block: \(error)") + } } }