[카테고리:] 미분류

  • 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 통신이 작동하면 그 위에 다양한 블록체인 기능을 구현하기가 훨씬 수월해집니다.