183 lines
5.5 KiB
Swift
183 lines
5.5 KiB
Swift
//
|
|
// Blockchain.swift
|
|
// Blockchain
|
|
//
|
|
// Created by Victor BODINAUD on 27/02/2020.
|
|
// Copyright © 2020 Victor BODINAUD. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
class Blockchain {
|
|
var chain = [Block]()
|
|
let memPool: MemPool
|
|
let accountManager: AccountManager
|
|
let minDifficulty = 2
|
|
let maxDifficulty = 6
|
|
let targetBlockTime = 10.0 // en secondes
|
|
|
|
static let genesisBlock: Block = {
|
|
let block = Block()
|
|
block.previousHash = "0000genesis0000"
|
|
block.index = 0
|
|
block.timestamp = 1701388800 // 1er Décembre 2023 00:00:00 GMT
|
|
block.difficulty = 4
|
|
block.nonce = 12345 // Nonce précalculé qui donne un hash valide
|
|
block.hash = "000088c1731bed4996680d2c50ea3d9b573c1507d2d61866c0deff33a7f8cf5e"
|
|
return block
|
|
}()
|
|
|
|
init() {
|
|
self.accountManager = AccountManager()
|
|
self.memPool = MemPool(accountManager: accountManager)
|
|
}
|
|
|
|
/**
|
|
Initialize the first block of the blockchain.
|
|
|
|
- Parameters:
|
|
- data: The datas of the block
|
|
*/
|
|
func createGenesisBlock() {
|
|
chain.append(Blockchain.genesisBlock)
|
|
print("Genesis block initialized -- hash: \(Blockchain.genesisBlock.hash)")
|
|
}
|
|
|
|
/**
|
|
Initialize a new block of the blockchain.
|
|
|
|
- Parameters:
|
|
- data: The datas of the block
|
|
*/
|
|
func createBlock(minerAddress: String) -> Block? {
|
|
guard let lastBlock = chain.last else { return nil }
|
|
|
|
// Récupérer les transactions en attente
|
|
var transactions = memPool.getTransactionsForBlock()
|
|
|
|
// Ajouter la récompense de minage
|
|
let miningReward = Transaction(sender: "SYSTEM", receiver: minerAddress, amount: 50, type: "MINING_REWARD")
|
|
transactions.append(miningReward)
|
|
|
|
let newBlock = Block(
|
|
transactions: transactions,
|
|
previousHash: lastBlock.hash,
|
|
index: chain.count,
|
|
difficulty: calculateNewDifficulty()
|
|
)
|
|
|
|
newBlock.miner = minerAddress
|
|
newBlock.timestamp = Int(Date().timeIntervalSince1970)
|
|
let miningTime = newBlock.mineBlock()
|
|
|
|
// Valider et traiter les transactions
|
|
if accountManager.processBlock(newBlock) {
|
|
chain.append(newBlock)
|
|
print("""
|
|
Block \(newBlock.index) created:
|
|
Hash: \(newBlock.hash)
|
|
Previous hash: \(newBlock.previousHash)
|
|
Transactions: \(newBlock.transactions.count)
|
|
Mining time: \(String(format: "%.2f", miningTime)) seconds
|
|
""")
|
|
return newBlock
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
private func calculateNewDifficulty() -> Int {
|
|
guard chain.count >= 2 else { return minDifficulty }
|
|
|
|
let lastBlock = chain.last!
|
|
let previousBlock = chain[chain.count - 2]
|
|
let timeSpent = Double(lastBlock.timestamp - previousBlock.timestamp)
|
|
|
|
if timeSpent < targetBlockTime / 2 {
|
|
return min(lastBlock.difficulty + 1, maxDifficulty)
|
|
} else if timeSpent > targetBlockTime * 2 {
|
|
return max(lastBlock.difficulty - 1, minDifficulty)
|
|
}
|
|
|
|
return lastBlock.difficulty
|
|
}
|
|
|
|
func submitTransaction(_ transaction: Transaction) -> Bool {
|
|
return memPool.addTransaction(transaction)
|
|
}
|
|
|
|
func getBalance(address: String) -> Int {
|
|
return accountManager.getBalance(address)
|
|
}
|
|
|
|
func chainValidity() -> Bool {
|
|
for i in 1..<chain.count {
|
|
let currentBlock = chain[i]
|
|
let previousBlock = chain[i-1]
|
|
|
|
if !currentBlock.isValid() || currentBlock.previousHash != previousBlock.hash {
|
|
print("Chain invalid at block \(i)")
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func validateChain(_ newChain: [Block]) -> Bool {
|
|
// Vérifier que la chaîne commence par notre bloc genesis codé en dur
|
|
guard let firstBlock = newChain.first,
|
|
firstBlock.hash == Blockchain.genesisBlock.hash else {
|
|
print("Genesis block mismatch")
|
|
return false
|
|
}
|
|
|
|
// Vérifier les blocs suivants
|
|
for i in 1..<newChain.count {
|
|
let block = newChain[i]
|
|
let previousBlock = newChain[i-1]
|
|
|
|
if block.previousHash != previousBlock.hash {
|
|
print("Invalid chain at block \(i): incorrect previous hash")
|
|
return false
|
|
}
|
|
|
|
if !block.isValid() {
|
|
print("Invalid chain at block \(i): invalid block hash")
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func replaceChain(_ newChain: [Block]) -> 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
|
|
}
|
|
}
|