Add p2p

This commit is contained in:
Victor Bodinaud
2024-11-27 20:02:35 +01:00
parent 9905bf2964
commit 212b175b6b
6 changed files with 345 additions and 36 deletions

View File

@@ -32,7 +32,7 @@ class Blockchain {
genesisBlock.previousHash = "0000"
genesisBlock.index = 0
genesisBlock.timestamp = Int(Date().timeIntervalSince1970)
genesisBlock.mineBlock()
let _ = genesisBlock.mineBlock()
chain.append(genesisBlock)
print("Genesis block created -- hash: \(genesisBlock.hash)")
}

View File

@@ -29,17 +29,22 @@ class MemPool {
Valide une transaction avant de l'ajouter au pool
*/
private func validateTransaction(_ transaction: Transaction) -> Bool {
// Basic validations
// Vérifications basiques
if transaction.amount <= 0 {
return false
}
// Skip balance check for mining rewards
// Pas besoin de vérifier la signature pour les récompenses de minage
if transaction.type == "MINING_REWARD" {
return true
}
// Check if sender has enough balance
// Vérifier la signature
if !transaction.isSignatureValid() {
return false
}
// Vérifier le solde
return accountManager.canProcessTransaction(transaction)
}

View File

@@ -0,0 +1,172 @@
//
// Node.swift
// SwiftChain
//
// Created by Victor on 27/11/2024.
//
import Foundation
import Network
class Node {
private var peers: [NWConnection] = []
private let port: NWEndpoint.Port = 8333 // Port standard
private var listener: NWListener?
private let blockchain: Blockchain
// Queue pour gérer les connexions de manière asynchrone
private let queue = DispatchQueue(label: "com.blockchain.node")
init(blockchain: Blockchain) {
self.blockchain = blockchain
setupListener()
}
enum MessageType: String, Codable {
case transaction = "TX"
}
struct NetworkMessage: Codable {
let type: MessageType
let data: Data
}
// Configuration du listener
private func setupListener() {
let parameters = NWParameters.tcp
do {
listener = try NWListener(using: parameters, on: port)
listener?.stateUpdateHandler = { [weak self] state in
switch state {
case .ready:
print("Node is listening on port \(self?.port.rawValue ?? 0)")
case .failed(let error):
print("Listener failed with error: \(error)")
default:
break
}
}
listener?.newConnectionHandler = { [weak self] connection in
self?.handleNewConnection(connection)
print("New incoming connection from: \(connection.endpoint)")
}
listener?.start(queue: queue)
} catch {
print("Failed to create listener: \(error)")
}
}
// Connexion à un pair
func connectToPeer(host: String) {
let endpoint = NWEndpoint.hostPort(host: NWEndpoint.Host(host), port: port)
let connection = NWConnection(to: endpoint, using: .tcp)
connection.stateUpdateHandler = { [weak self] state in
switch state {
case .ready:
print("Connected to peer: \(host)")
self?.peers.append(connection)
self?.startReceiving(connection)
case .failed(let error):
print("Connection failed: \(error)")
case .cancelled:
print("Connection cancelled to: \(host)")
default:
break
}
}
connection.start(queue: queue)
}
// Méthode pour lister les pairs connectés
func listPeers() {
if peers.isEmpty {
print("Aucun pair connecté")
return
}
print("\nPairs connectés (\(peers.count)):")
for (index, peer) in peers.enumerated() {
print("\(index + 1). \(peer.endpoint)")
}
}
// Propagation d'une transaction
func broadcastTransaction(_ transaction: Transaction) {
do {
let transactionData = try JSONEncoder().encode(transaction)
let message = NetworkMessage(type: .transaction, data: transactionData)
let messageData = try JSONEncoder().encode(message)
print("Broadcasting transaction to \(peers.count) peers")
for peer in peers {
peer.send(content: messageData, completion: .contentProcessed { error in
if let error = error {
print("Failed to send transaction: \(error)")
} else {
print("Transaction sent successfully to: \(peer.endpoint)")
}
})
}
} catch {
print("Failed to encode transaction: \(error)")
}
}
// Réception des messages
private func startReceiving(_ connection: NWConnection) {
connection.receive(minimumIncompleteLength: 1, maximumLength: 65536) { [weak self] content, _, isComplete, error in
if let data = content {
self?.handleReceivedData(data, from: connection)
}
if let error = error {
print("Receive error: \(error)")
}
if !isComplete && error == nil {
self?.startReceiving(connection)
}
}
}
// Traitement d'une transaction reçue
private func handleReceivedTransaction(_ transaction: Transaction) {
if blockchain.submitTransaction(transaction) {
// Propager aux autres pairs si la transaction est valide
broadcastTransaction(transaction)
}
}
private func handleReceivedData(_ data: Data, from connection: NWConnection) {
do {
print("Received data of size: \(data.count) bytes")
let message = try JSONDecoder().decode(NetworkMessage.self, from: data)
print("Message decoded successfully, type: \(message.type)")
switch message.type {
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")
}
}
} catch {
print("Failed to decode received data: \(error)")
}
}
private func handleNewConnection(_ connection: NWConnection) {
connection.start(queue: queue)
peers.append(connection)
startReceiving(connection)
}
}

View File

@@ -7,11 +7,13 @@
import Foundation
class Transaction {
var sender: String
var receiver: String
var amount: Int
var type: String
class Transaction: Codable {
let sender: String
let receiver: String
let amount: Int
let type: String
var signature: Data?
var senderPublicKey: Data?
init(sender: String, receiver: String, amount: Int, type: String) {
self.sender = sender
@@ -19,4 +21,53 @@ class Transaction {
self.amount = amount
self.type = type
}
// Pour encoder/décoder les Data optionnels
enum CodingKeys: String, CodingKey {
case sender, receiver, amount, type, signature, senderPublicKey
}
// Données à signer
func messageToSign() -> Data {
return "\(sender)\(receiver)\(amount)\(type)".data(using: .utf8)!
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
sender = try container.decode(String.self, forKey: .sender)
receiver = try container.decode(String.self, forKey: .receiver)
amount = try container.decode(Int.self, forKey: .amount)
type = try container.decode(String.self, forKey: .type)
signature = try container.decodeIfPresent(Data.self, forKey: .signature)
senderPublicKey = try container.decodeIfPresent(Data.self, forKey: .senderPublicKey)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(sender, forKey: .sender)
try container.encode(receiver, forKey: .receiver)
try container.encode(amount, forKey: .amount)
try container.encode(type, forKey: .type)
try container.encodeIfPresent(signature, forKey: .signature)
try container.encodeIfPresent(senderPublicKey, forKey: .senderPublicKey)
}
// Vérifier la validité de la signature
func isSignatureValid() -> Bool {
guard let signature = signature,
let publicKeyData = senderPublicKey else {
return false
}
// Pas besoin de vérifier la signature pour les récompenses de minage
if type == "MINING_REWARD" {
return true
}
return Wallet.verifySignature(
for: self,
signature: signature,
publicKeyData: publicKeyData
)
}
}

View File

@@ -0,0 +1,47 @@
//
// Wallet.swift
// SwiftChain
//
// Created by Victor on 27/11/2024.
//
import Foundation
import CryptoKit
class Wallet {
private let privateKey: Curve25519.Signing.PrivateKey
let publicKey: Curve25519.Signing.PublicKey
let address: String
init() {
// Générer une nouvelle paire de clés
privateKey = Curve25519.Signing.PrivateKey()
publicKey = privateKey.publicKey
// Créer une adresse au format swift_(hash)
let pubKeyData = publicKey.rawRepresentation
let hash = SHA256.hash(data: pubKeyData)
let hashString = hash.compactMap { String(format: "%02x", $0) }.joined()
address = "swift_" + hashString.prefix(40)
}
// Signer une transaction
func signTransaction(_ transaction: Transaction) -> Data? {
let messageData = transaction.messageToSign()
return try? privateKey.signature(for: messageData)
}
// Vérifier une signature
static func verifySignature(for transaction: Transaction, signature: Data, publicKeyData: Data) -> Bool {
guard let publicKey = try? Curve25519.Signing.PublicKey(rawRepresentation: publicKeyData) else {
return false
}
return (publicKey.isValidSignature(signature, for: transaction.messageToSign()))
}
// Obtenir la clé publique en format Data
func getPublicKeyData() -> Data {
return publicKey.rawRepresentation
}
}

View File

@@ -9,8 +9,10 @@
import Foundation
let blockchain = Blockchain()
let node = Node(blockchain: blockchain)
var command: String?
var currentMinerAddress = "MINER1" // Adresse par défaut du mineur
var wallets: [String: Wallet] = [:]
print("""
Blockchain CLI - Commandes disponibles:
@@ -20,6 +22,8 @@ Blockchain CLI - Commandes disponibles:
- pending : Voir les transactions en attente
- validity : Vérifier la validité de la chaîne
- setminer : Changer l'adresse du mineur
- connect <host> : Se connecter à un pair
- peers : Liste des pairs connectés
- exit : Quitter
""")
@@ -30,6 +34,15 @@ repeat {
command = readLine()?.lowercased()
switch command {
case "connect":
print("Entrez l'adresse du pair (ex: 192.168.1.100):")
if let host = readLine() {
node.connectToPeer(host: host)
}
case "peers":
node.listPeers()
case "mine":
if let block = blockchain.createBlock(minerAddress: currentMinerAddress) {
print("Bloc miné avec succès. Récompense envoyée à \(currentMinerAddress)")
@@ -38,27 +51,6 @@ repeat {
print("Échec du minage")
}
case "send":
print("Adresse de l'expéditeur:")
let sender = readLine() ?? ""
print("Adresse du destinataire:")
let receiver = readLine() ?? ""
print("Montant:")
if let amountStr = readLine(), let amount = Int(amountStr) {
let transaction = Transaction(
sender: sender,
receiver: receiver,
amount: amount,
type: "TRANSFER"
)
if blockchain.submitTransaction(transaction) {
print("Transaction ajoutée au mempool")
} else {
print("Transaction invalide")
}
}
case "balance":
print("Entrer l'adresse:")
if let address = readLine() {
@@ -78,11 +70,11 @@ repeat {
print("Transactions en attente: \(transactions.count)")
for (index, tx) in transactions.enumerated() {
print("""
\(index + 1). De: \(tx.sender)
À: \(tx.receiver)
Montant: \(tx.amount)
Type: \(tx.type)
""")
\(index + 1). De: \(tx.sender)
À: \(tx.receiver)
Montant: \(tx.amount)
Type: \(tx.type)
""")
}
case "validity":
@@ -92,6 +84,48 @@ repeat {
case "exit":
print("Au revoir!")
// Ajoutons une nouvelle commande pour créer un wallet
case "createwallet":
let wallet = Wallet()
wallets[wallet.address] = wallet
print("Nouveau wallet créé!")
print("Adresse: \(wallet.address)")
// Modifions la commande send
case "send":
print("Votre adresse (wallet):")
guard let senderAddress = readLine(),
let wallet = wallets[senderAddress]
else {
print("Wallet non trouvé")
break
}
print("Adresse du destinataire:")
guard let receiverAddress = readLine() else { break }
print("Montant:")
guard let amountStr = readLine(),
let amount = Int(amountStr) else { break }
let transaction = Transaction(
sender: senderAddress,
receiver: receiverAddress,
amount: amount,
type: "TRANSFER"
)
// Signer la transaction
transaction.senderPublicKey = wallet.getPublicKeyData()
transaction.signature = wallet.signTransaction(transaction)
if blockchain.submitTransaction(transaction) {
node.broadcastTransaction(transaction)
print("Transaction propagée au réseau!")
} else {
print("Erreur lors de l'envoi de la transaction")
}
default:
print("Commande inconnue")
}