java coin #250101090934

  1. 프로젝트 구조 설정:
    • src 폴더에 main/java 폴더 구조가 있는지 확인합니다. Gradle 프로젝트의 표준 구조입니다.
    • 없다면 src 폴더 우클릭 → New → Directory 선택하여 main/java 경로를 생성합니다.
  2. 패키지 구조 생성:
    • src/main/java 폴더 우클릭 → New → Package
    • 패키지 이름으로 com.javachain 입력하고 생성
    • 같은 방식으로 com.javachain.corecom.javachain.network 패키지도 생성합니다.
  3. 클래스 파일 생성:
    • 각 패키지에 해당하는 클래스 파일을 생성합니다.
    • com.javachain.core 패키지에 Block.java, Transaction.java, Blockchain.java 생성
    • com.javachain.network 패키지에 Node.java, NodeConnection.java, Message.java, MessageType.java 생성
    • com.javachain 패키지에 Main.java 생성
  4. 모든 클래스 파일에 올바른 패키지 선언이 있는지 확인:
    • 예를 들어, Block.java의 첫 줄에 package com.javachain.core; 가 있어야 합니다.
  5. build.gradle 파일에서 애플리케이션 주 클래스 지정: application { mainClass = 'com.javachain.Main' } (mainClassName 대신 최신 Gradle에서는 mainClass 사용)
  6. Gradle 프로젝트 새로고침:
    • 인텔리제이 우측의 Gradle 탭 클릭
    • 새로고침 버튼 클릭하여 변경사항 적용
  7. 프로젝트 실행:
    • Main 클래스 우클릭 → Run ‘Main.main()’
    • 또는 Gradle 태스크 실행: Gradle 탭 → Tasks → application → run

파일 구조가 올바르게 설정되었는지 확인하는 것이 중요합니다. Gradle 프로젝트에서는 src/main/java/com/javachain/… 형식의 경로를 따라야 합니다.

인텔리제이에서 시작할 수 있는 간단한 P2P 연결 기능을 갖춘 블록체인 프로젝트를 만들어 드리겠습니다. 두 컴퓨터 간 기본적인 연결과 메시지 교환이 가능한 구조로 설계하겠습니다.

프로젝트 설정

인텔리제이에서 다음과 같이 프로젝트를 설정합니다:

  1. 빌드 시스템: Gradle 또는 Maven (여기서는 Gradle 사용)
  2. JDK 버전: Java 17 이상 권장
  3. 프로젝트 구조:
    • core (블록체인 핵심 기능)
    • network (P2P 네트워킹)
    • util (유틸리티 클래스)

build.gradle 파일

plugins {
    id 'java'
    id 'application'
}

group 'com.javachain'
version '1.0-SNAPSHOT'

sourceCompatibility = 17

repositories {
    mavenCentral()
}

dependencies {
    // 네트워킹을 위한 Netty
    implementation 'io.netty:netty-all:4.1.86.Final'
    
    // JSON 처리
    implementation 'com.google.code.gson:gson:2.10.1'
    
    // 암호화
    implementation 'org.bouncycastle:bcprov-jdk15on:1.70'
    
    // 로깅
    implementation 'org.slf4j:slf4j-api:2.0.7'
    implementation 'ch.qos.logback:logback-classic:1.4.6'
    
    // 테스트
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.2'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.2'
}

application {
    mainClassName = 'com.javachain.Main'
}

test {
    useJUnitPlatform()
}

핵심 클래스 구현

1. 블록체인 기본 구조

Block.java

package com.javachain.core;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;

public class Block {
    private int index;
    private long timestamp;
    private String previousHash;
    private String hash;
    private int nonce;
    private List<Transaction> transactions;

    public Block(int index, String previousHash) {
        this.index = index;
        this.timestamp = Instant.now().getEpochSecond();
        this.previousHash = previousHash;
        this.transactions = new ArrayList<>();
        this.nonce = 0;
        this.hash = calculateHash();
    }

    public String calculateHash() {
        try {
            String dataToHash = index + timestamp + previousHash + nonce + transactions.toString();
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] hash = digest.digest(dataToHash.getBytes(StandardCharsets.UTF_8));
            
            StringBuilder hexString = new StringBuilder();
            for (byte b : hash) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) hexString.append('0');
                hexString.append(hex);
            }
            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    public void mineBlock(int difficulty) {
        String target = new String(new char[difficulty]).replace('\0', '0');
        while (!hash.substring(0, difficulty).equals(target)) {
            nonce++;
            hash = calculateHash();
        }
        System.out.println("Block mined: " + hash);
    }

    public void addTransaction(Transaction transaction) {
        if (transaction != null) {
            transactions.add(transaction);
        }
    }

    // Getters
    public int getIndex() { return index; }
    public long getTimestamp() { return timestamp; }
    public String getPreviousHash() { return previousHash; }
    public String getHash() { return hash; }
    public List<Transaction> getTransactions() { return transactions; }
}

Transaction.java

package com.javachain.core;

import java.security.*;
import java.util.Base64;

public class Transaction {
    private String transactionId;
    private String sender;
    private String recipient;
    private double amount;
    private byte[] signature;

    public Transaction(String sender, String recipient, double amount) {
        this.sender = sender;
        this.recipient = recipient;
        this.amount = amount;
        this.transactionId = calculateHash();
    }

    public String calculateHash() {
        try {
            String data = sender + recipient + Double.toString(amount);
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] hash = digest.digest(data.getBytes());
            
            StringBuilder hexString = new StringBuilder();
            for (byte b : hash) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) hexString.append('0');
                hexString.append(hex);
            }
            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    public void generateSignature(PrivateKey privateKey) {
        try {
            String data = sender + recipient + Double.toString(amount);
            Signature dsa = Signature.getInstance("SHA256withECDSA");
            dsa.initSign(privateKey);
            dsa.update(data.getBytes());
            signature = dsa.sign();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public boolean verifySignature(PublicKey publicKey) {
        try {
            String data = sender + recipient + Double.toString(amount);
            Signature dsa = Signature.getInstance("SHA256withECDSA");
            dsa.initVerify(publicKey);
            dsa.update(data.getBytes());
            return dsa.verify(signature);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // Getters
    public String getTransactionId() { return transactionId; }
    public String getSender() { return sender; }
    public String getRecipient() { return recipient; }
    public double getAmount() { return amount; }
    public byte[] getSignature() { return signature; }

    @Override
    public String toString() {
        return "Transaction{" +
                "transactionId='" + transactionId + '\'' +
                ", sender='" + sender + '\'' +
                ", recipient='" + recipient + '\'' +
                ", amount=" + amount +
                '}';
    }
}

Blockchain.java

package com.javachain.core;

import java.util.ArrayList;
import java.util.List;

public class Blockchain {
    private List<Block> chain;
    private int difficulty;
    private List<Transaction> pendingTransactions;

    public Blockchain() {
        this.chain = new ArrayList<>();
        this.difficulty = 4;
        this.pendingTransactions = new ArrayList<>();
        
        // Genesis block
        createGenesisBlock();
    }

    private void createGenesisBlock() {
        Block genesisBlock = new Block(0, "0");
        genesisBlock.mineBlock(difficulty);
        chain.add(genesisBlock);
        System.out.println("Genesis block created: " + genesisBlock.getHash());
    }

    public Block getLatestBlock() {
        return chain.get(chain.size() - 1);
    }

    public void addBlock(Block newBlock) {
        newBlock.setPreviousHash(getLatestBlock().getHash());
        newBlock.mineBlock(difficulty);
        chain.add(newBlock);
    }

    public void addTransaction(Transaction transaction) {
        pendingTransactions.add(transaction);
    }

    public void minePendingTransactions(String minerRewardAddress) {
        Block block = new Block(chain.size(), getLatestBlock().getHash());
        
        for (Transaction transaction : pendingTransactions) {
            block.addTransaction(transaction);
        }
        
        block.mineBlock(difficulty);
        chain.add(block);
        
        // Clear pending transactions and reward the miner
        pendingTransactions.clear();
        addTransaction(new Transaction("System", minerRewardAddress, 1.0));
    }

    public boolean isChainValid() {
        for (int i = 1; i < chain.size(); i++) {
            Block currentBlock = chain.get(i);
            Block previousBlock = chain.get(i - 1);
            
            if (!currentBlock.getHash().equals(currentBlock.calculateHash())) {
                return false;
            }
            
            if (!currentBlock.getPreviousHash().equals(previousBlock.getHash())) {
                return false;
            }
        }
        return true;
    }

    // Getters
    public List<Block> getChain() { return chain; }
    public int getDifficulty() { return difficulty; }
    public List<Transaction> getPendingTransactions() { return pendingTransactions; }
}

2. P2P 네트워킹 클래스

Node.java

package com.javachain.network;

import com.google.gson.Gson;
import com.javachain.core.Block;
import com.javachain.core.Blockchain;
import com.javachain.core.Transaction;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Node {
    private List<NodeConnection> peers;
    private int port;
    private Blockchain blockchain;
    private ExecutorService executorService;
    private boolean running;
    private final Gson gson = new Gson();

    public Node(int port, Blockchain blockchain) {
        this.port = port;
        this.blockchain = blockchain;
        this.peers = new ArrayList<>();
        this.executorService = Executors.newCachedThreadPool();
        this.running = false;
    }

    public void start() {
        running = true;
        executorService.submit(this::runServer);
        System.out.println("Node started on port " + port);
    }

    public void stop() {
        running = false;
        executorService.shutdown();
        System.out.println("Node stopped");
    }

    private void runServer() {
        try (ServerSocket serverSocket = new ServerSocket(port)) {
            serverSocket.setReuseAddress(true);
            
            while (running) {
                Socket clientSocket = serverSocket.accept();
                NodeConnection connection = new NodeConnection(clientSocket);
                peers.add(connection);
                executorService.submit(() -> handleConnection(connection));
            }
        } catch (IOException e) {
            System.err.println("Server error: " + e.getMessage());
        }
    }

    private void handleConnection(NodeConnection connection) {
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getSocket().getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                Message message = gson.fromJson(line, Message.class);
                processMessage(message, connection);
            }
        } catch (IOException e) {
            System.err.println("Connection error: " + e.getMessage());
        } finally {
            peers.remove(connection);
            try {
                connection.getSocket().close();
            } catch (IOException e) {
                // Ignore
            }
        }
    }

    public void connectToPeer(String host, int port) {
        try {
            Socket socket = new Socket(host, port);
            NodeConnection connection = new NodeConnection(socket);
            peers.add(connection);
            executorService.submit(() -> handleConnection(connection));
            System.out.println("Connected to peer " + host + ":" + port);
            
            // Send current blockchain to new peer
            sendChain(connection);
        } catch (IOException e) {
            System.err.println("Failed to connect to peer: " + e.getMessage());
        }
    }

    private void sendChain(NodeConnection connection) {
        Message message = new Message(MessageType.CHAIN, gson.toJson(blockchain.getChain()));
        connection.send(gson.toJson(message));
    }

    private void processMessage(Message message, NodeConnection sender) {
        switch (message.getType()) {
            case TRANSACTION:
                Transaction transaction = gson.fromJson(message.getData(), Transaction.class);
                blockchain.addTransaction(transaction);
                broadcastMessage(message, sender);
                break;
                
            case BLOCK:
                Block block = gson.fromJson(message.getData(), Block.class);
                blockchain.addBlock(block);
                broadcastMessage(message, sender);
                break;
                
            case CHAIN:
                // Here you would implement chain validation and replacement if needed
                System.out.println("Received chain from peer");
                break;
                
            case CHAIN_REQUEST:
                sendChain(sender);
                break;
        }
    }

    public void broadcastTransaction(Transaction transaction) {
        Message message = new Message(MessageType.TRANSACTION, gson.toJson(transaction));
        broadcastMessage(message, null);
    }

    public void broadcastBlock(Block block) {
        Message message = new Message(MessageType.BLOCK, gson.toJson(block));
        broadcastMessage(message, null);
    }

    private void broadcastMessage(Message message, NodeConnection exclude) {
        String messageJson = gson.toJson(message);
        for (NodeConnection peer : peers) {
            if (peer != exclude) {
                peer.send(messageJson);
            }
        }
    }

    // Getter
    public Blockchain getBlockchain() { return blockchain; }
}

NodeConnection.java

package com.javachain.network;

import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;

public class NodeConnection {
    private Socket socket;
    private PrintWriter writer;

    public NodeConnection(Socket socket) {
        this.socket = socket;
        try {
            this.writer = new PrintWriter(socket.getOutputStream(), true);
        } catch (IOException e) {
            throw new RuntimeException("Error creating writer", e);
        }
    }

    public void send(String message) {
        writer.println(message);
    }

    public Socket getSocket() {
        return socket;
    }
}

Message.java

package com.javachain.network;

public class Message {
    private MessageType type;
    private String data;

    public Message(MessageType type, String data) {
        this.type = type;
        this.data = data;
    }

    public MessageType getType() {
        return type;
    }

    public String getData() {
        return data;
    }
}

MessageType.java

package com.javachain.network;

public enum MessageType {
    TRANSACTION,
    BLOCK,
    CHAIN,
    CHAIN_REQUEST
}

3. 메인 애플리케이션

Main.java

package com.javachain;

import com.javachain.core.Block;
import com.javachain.core.Blockchain;
import com.javachain.core.Transaction;
import com.javachain.network.Node;

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        System.out.println("=== Java Blockchain P2P Node ===");
        System.out.print("Enter port for this node: ");
        int port = scanner.nextInt();
        scanner.nextLine();  // Consume newline
        
        // Create blockchain
        Blockchain blockchain = new Blockchain();
        
        // Create and start node
        Node node = new Node(port, blockchain);
        node.start();
        
        boolean running = true;
        while (running) {
            System.out.println("\nCommands:");
            System.out.println("1. Connect to peer");
            System.out.println("2. Create transaction");
            System.out.println("3. Mine pending transactions");
            System.out.println("4. Show blockchain");
            System.out.println("5. Exit");
            System.out.print("Enter command: ");
            
            int command = scanner.nextInt();
            scanner.nextLine();  // Consume newline
            
            switch (command) {
                case 1:
                    System.out.print("Enter peer host: ");
                    String host = scanner.nextLine();
                    System.out.print("Enter peer port: ");
                    int peerPort = scanner.nextInt();
                    scanner.nextLine();  // Consume newline
                    node.connectToPeer(host, peerPort);
                    break;
                    
                case 2:
                    System.out.print("Enter sender: ");
                    String sender = scanner.nextLine();
                    System.out.print("Enter recipient: ");
                    String recipient = scanner.nextLine();
                    System.out.print("Enter amount: ");
                    double amount = scanner.nextDouble();
                    scanner.nextLine();  // Consume newline
                    
                    Transaction transaction = new Transaction(sender, recipient, amount);
                    blockchain.addTransaction(transaction);
                    node.broadcastTransaction(transaction);
                    System.out.println("Transaction created and broadcast");
                    break;
                    
                case 3:
                    System.out.print("Enter miner address: ");
                    String minerAddress = scanner.nextLine();
                    blockchain.minePendingTransactions(minerAddress);
                    Block latestBlock = blockchain.getLatestBlock();
                    node.broadcastBlock(latestBlock);
                    System.out.println("Block mined and broadcast");
                    break;
                    
                case 4:
                    System.out.println("\n=== Blockchain ===");
                    for (Block block : blockchain.getChain()) {
                        System.out.println("Index: " + block.getIndex());
                        System.out.println("Timestamp: " + block.getTimestamp());
                        System.out.println("Hash: " + block.getHash());
                        System.out.println("Previous hash: " + block.getPreviousHash());
                        System.out.println("Transactions: " + block.getTransactions().size());
                        System.out.println();
                    }
                    break;
                    
                case 5:
                    running = false;
                    node.stop();
                    break;
                    
                default:
                    System.out.println("Unknown command");
            }
        }
        
        scanner.close();
    }
}

프로젝트 실행 방법

  1. 인텔리제이에서 위의 클래스들을 적절한 패키지에 생성합니다.
  2. Main 클래스를 실행합니다.
  3. 프롬프트에 따라 노드 포트를 입력합니다(예: 9000).
  4. 두 번째 터미널 또는 컴퓨터에서 같은 애플리케이션을 실행하고 다른 포트를 선택합니다(예: 9001).
  5. 각 노드에서 “Connect to peer” 옵션을 선택하고 서로의 호스트와 포트를 입력하여 연결합니다.
  6. 트랜잭션을 생성하고 채굴하여 두 노드가 동기화되는 것을 확인합니다.

참고 사항

  1. 이 구현은 기본적인 P2P 연결 기능과 블록체인의 핵심 요소를 포함합니다.
  2. 실제 프로덕션 환경에서는 보안, 오류 처리, 동기화 문제 등을 더 강화해야 합니다.
  3. 네트워크 통신은 간단한 소켓 기반으로 구현했지만, 실제로는 Netty나 gRPC 같은 고급 프레임워크를 사용하는 것이 좋습니다.
  4. 사이드체인 기능은 기본 P2P 연결이 작동하는 것을 확인한 후 추가할 수 있습니다.

이 코드를 기반으로 하여 사이드체인 기능과 더 고급 기능을 점진적으로 추가해 나갈 수 있습니다. 기본적인 P2P 통신이 작동하면 그 위에 다양한 블록체인 기능을 구현하기가 훨씬 수월해집니다.

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다