기사 메일전송
자바(Java)로 미니 블록체인 개발해봐요(5) - SECTION 04. 블록체인에서 트랜잭션의 처리과정을 알아보자 - 필자 나동빈은 '안경잡이' 개발자로 현재 프리랜서로 활약하고 있다.
  • 기사등록 2018-04-17 00:40:27
  • 수정 2018-04-19 18:13:46
기사수정



지난 시간에 우리는 하나의 블록(Block)에 대한 정보를 정의하는 Block 클래스를 만들었다. 나아가 여러 개의 블록이 연결된 형태인 블록체인(Block Chain)의 틀을 개발하는 시간을 가졌다. 이 때 블록체인에서 중간에 있는 하나의 블록 정보가 변경되면 이후의 모든 블록 데이터가 영향을 받는 것을 확인해보았고, 결과적으로 블록체인이 왜 보안상 안전한지에 대해서 이해할 수 있었다.


이번 시간에는 가장 먼저 트랜잭션(Transaction) 클래스를 생성해 한 번의 코인 거래에 대한 정보를 처리할 수 있도록 해보자. 블록체인에서 하나의 블록은 이전 블록에서부터 현재 블록까지 발생한 모든 코인 전송 기록을 담으며, 이 때 구체적으로 해당 시점까지 발생했던 모든 전송 기록이 리스트(List) 형태로 저장된다. 이번 시간에는 트랜잭션의 처리 과정에 대해 자세히 알아볼 것이다.



바로 이전 시간에 이어서 core 패키지 안에 새로운 클래스(Class)를 생성한다.



클래스의 이름은 Transaction(거래)로 A라는 사람이 B라는 사람에게 코인을 얼마나 전송했는지에 대한 하나의 코인 거래 정보를 저장하는 역할을 수행한다.


package core;


public class Transaction {


String sender;

String receiver;

double amount;

public String getSender() {

return sender;

}

public void setSender(String sender) {

this.sender = sender;

}

public String getReceiver() {

return receiver;

}

public void setReceiver(String receiver) {

this.receiver = receiver;

}

public double getAmount() {

return amount;

}

public void setAmount(double amount) {

this.amount = amount;

}

public Transaction(String sender, String receiver, double amount) {

this.sender = sender;

this.receiver = receiver;

this.amount = amount;

}

public String getInformation() {

return sender + "이(가) " + receiver + "에게 " + amount + "개의 코인을 보냈습니다.";

}

}


위와 같이 소스코드를 작성한다. 사용되는 변수는 총 세 개로 차례대로 ‘코인을 보낸 사람(Sender)’, ‘코인을 받는 사람(Receiver)’, ‘보낸 코인의 양(Amount)’이다. 또한 getInformation() 메소드는 하나의 트랜잭션 정보를 문자열 형태로 반환하는 역할을 수행한다. 이제 BlockChainStarter 클래스의 소스코드를 다음과 같이 변경하여 우리가 만든 트랜잭션 클래스를 테스트해보자.


package core;


public class BlockChainStarter {


public static void main(String[] args) {


Transaction transaction = new Transaction("나동빈", "박한울", 1.5);

System.out.println(transaction.getInformation());

}

}


위 소스코드를 실행하면 다음과 같이 하나의 트랜잭션 정보가 성공적으로 출력된다.



실제로 비트코인(Bit Coin)을 포함해 대부분의 블록체인에서 하나의 트랜잭션에서 제일 중요한 내용은 누가 누구에게 얼마의 코인을 보냈는지에 대한 정보다. 추가적으로 트랜잭션이 발생한 시간을 나타내는 타임스탬프 등의 정보가 사용될 수 있으나 현재 우리의 프로그램에서는 생략하도록 하자.


이제 우리가 기존에 작성했던 블록(Block) 클래스의 내용을 수정할 차례다. 기존에는 단순히 데이터(Data)라고 하여 하나의 블록마다 특정한 데이터를 담고 있도록 개발했다. 실제로 이 데이터란 해당 블록이 생성되기까지 발생한 신규 트랜잭션들의 정보를 의미한다. 따라서 Block 클래스의 소스코드를 다음과 같이 트랜잭션 리스트를 저장할 수 있도록 수정해보자.


package core;


import java.util.ArrayList;


import util.Util;


public class Block {


private int blockID;

private String previousBlockHash;

private int nonce;

private ArrayList transactionList;


public int getBlockID() {

return blockID;

}


public void setBlockID(int blockID) {

this.blockID = blockID;

}


public String getPreviousBlockHash() {

return previousBlockHash;

}


public void setPreviousBlockHash(String previousBlockHash) {

this.previousBlockHash = previousBlockHash;

}


public int getNonce() {

return nonce;

}


public void setNonce(int nonce) {

this.nonce = nonce;

}


public Block(int blockID, String previousBlockHash, int nonce, ArrayList transactionList) {

this.blockID = blockID;

this.previousBlockHash = previousBlockHash;

this.nonce = nonce;

this.transactionList = transactionList;

}


public void addTransaction(Transaction transaction) {

transactionList.add(transaction);

}

public void showInformation() {

System.out.println("--------------------------------------");

System.out.println("블록 번호: " + getBlockID());

System.out.println("이전 해시: " + getPreviousBlockHash());

System.out.println("채굴 변수 값: " + getNonce());

System.out.println("트랜잭션 개수: " + transactionList.size() + "개");

for(int i = 0; i < transactionList.size(); i++) {</p>

System.out.println(transactionList.get(i).getInformation());

}

System.out.println("블록 해시: " + getBlockHash());

System.out.println("--------------------------------------");

}

public String getBlockHash() {

String transactionInformations = "";

for(int i = 0; i < transactionList.size(); i++) {</p>

transactionInformations += transactionList.get(i).getInformation();

}

return Util.getHash(nonce + transactionInformations + previousBlockHash);

}


public void mine() {

while(true) {

if(getBlockHash().substring(0, 4).equals("0000")) {

System.out.println(blockID + "번째 블록의 채굴에 성공했습니다.");

break;

}

nonce++;

}

}

}


소스코드를 보면 기존에 존재하던 데이터(Data)라는 이름의 변수가 사라지고, 모든 트랜잭션을 담는 트랜잭션 리스트(Transaction List)라는 이름의 변수가 추가되었다. 그리고 addTransaction() 메소드는 새로운 트랜잭션이 발생했을 때 그 트랜잭션을 해당 블록에 추가하는 역할을 수행한다.


그리고 기존에 존재하던 getInformation() 메소드의 이름을 showInformation()으로 수정하고, 모든 트랜잭션 내용을 차례대로 출력하도록 해주었다. 무엇보다 가장 핵심적인 부분은 getBlockHash() 메소드로 특정한 블록에 저장된 모든 트랜잭션 정보가 블록의 해시 값에 반영되도록 수정했다. 만약 트랜잭션 정보가 조금이라도 변경되는 경우 해시 값 전체가 변경되어 쉽게 알아챌 수 있도록 한 것이다. 이제 다음과 같이 BlockChainStarter 클래스의 소스코드를 바꾸어 테스트해보자.


package core;


import java.util.ArrayList;


public class BlockChainStarter {


public static void main(String[] args) {


Block block1 = new Block(1, null, 0, new ArrayList());

block1.mine();

block1.showInformation();

Block block2 = new Block(2, block1.getBlockHash(), 0, new ArrayList());

block2.addTransaction(new Transaction("나동빈", "박한울", 1.5));

block2.addTransaction(new Transaction("이태일", "박한울", 0.7));

block2.mine();

block2.showInformation();

Block block3 = new Block(3, block2.getBlockHash(), 0, new ArrayList());

block3.addTransaction(new Transaction("강종구", "이상욱", 8.2));

block3.addTransaction(new Transaction("박한울", "나동빈", 0.4));

block3.mine();

block3.showInformation();


Block block4 = new Block(4, block3.getBlockHash(), 0, new ArrayList());

block4.addTransaction(new Transaction("이상욱", "강종구", 0.1));

block4.mine();

block4.showInformation();

}

}


우리가 새롭게 만든 예시 블록체인의 경우 총 4개의 블록으로 구성된다. 소스코드를 보면 첫 번째 블록이 생성되기까지는 어떠한 트랜잭션도 발생하지 않았고, 첫 번째 블록 생성 이후에 두 번째 블록이 생성되기까지는 2건의 트랜잭션이 발생했다. 이어서 세 번째 블록과 네 번째 블록은 각각 2개, 1개의 트랜잭션 정보를 담고 있다. 위 프로그램을 실행하면 결과는 다음과 같다.



각 블록은 이전 해시, 채굴 변수 값, 트랜잭션 정보를 이용해 현재 블록의 해시 값을 생성하고 있다. 위 결과를 그림으로 이해하기 쉽게 나타낸 것은 다음과 같다.



블록체인 내 블록들의 해시 값은 긴밀하게 연결되어 영향을 미치고 있다. 이 때 ‘나동빈’을 악성 해커라고 가정하고 세 번째 블록에서 ‘박한울’로부터 받은 코인의 개수가 9.9개라고 수정하려는 시도를 했다면 어떻게 될까? 즉 다음과 같이 BlockChainStarter 클래스의 소스코드를 수정해보자.


package core;


import java.util.ArrayList;


public class BlockChainStarter {


public static void main(String[] args) {


Block block1 = new Block(1, null, 0, new ArrayList());

block1.mine();

block1.showInformation();

Block block2 = new Block(2, block1.getBlockHash(), 0, new ArrayList());

block2.addTransaction(new Transaction("나동빈", "박한울", 1.5));

block2.addTransaction(new Transaction("이태일", "박한울", 0.7));

block2.mine();

block2.showInformation();

Block block3 = new Block(3, block2.getBlockHash(), 0, new ArrayList());

block3.addTransaction(new Transaction("강종구", "이상욱", 8.2));

block3.addTransaction(new Transaction("박한울", "나동빈", 9.9));

block3.mine();

block3.showInformation();


Block block4 = new Block(4, block3.getBlockHash(), 0, new ArrayList());

block4.addTransaction(new Transaction("이상욱", "강종구", 0.1));

block4.mine();

block4.showInformation();

}


}


단순하게 기존의 소스코드에서 세 번째 블록에서 ‘박한울’이 ‘나동빈’에게 보낸 코인의 양을 9.9로 바꾼 소스코드이며 실행 결과는 다음과 같다.



지난 시간에서 보았던 결과와 마찬가지로 수정된 이후의 모든 블록의 해시 값이 변경된 것을 확인할 수 있다. 이와 같이 악의적인 의도를 가진 해커가 트랜잭션 정보를 수정하려면 이후의 모든 블록에 대한 채굴(Mining)을 다시 수행해야 하므로 현실적으로 해킹은 불가능하다. 재미있는 점은, 이처럼 특정한 트랜잭션 데이터에서 보내는 코인의 양에 대한 정보를 아주 조금만 바꾸더라도 전체 블록체인이 변경되어야 한다는 점이다. 이러한 구조는 블록체인의 강력한 무결성을 보장할 수 있다.


또한 실제로 모든 블록체인에 대한 채굴을 다시 수행하여 이전에 발생한 트랜잭션 정보를 바꾸는 데에 성공했다고 하더라도 사실상 완전한 해킹은 어렵다. 왜냐하면 블록체인 시스템은 전체 네트워크에서 50% 이상의 컴퓨터에서 동일한 블록체인이 발견되는 경우, 해당 블록체인을 올바른 블록체인으로 인정하기 때문이다. 따라서 해커가 전체 네트워크의 50% 이상의 컴퓨팅 자원을 갖출 수 있어야 성공적인 해킹이 가능할 것이다. 이번 시간에는 이와 같이 실제 트랜잭션 정보를 블록체인에 추가하는 과정을 통해 보다 실제 블록체인과 흡사한 형태의 구조를 갖추어보는 시간을 가졌다.


다음주에 계속...

1
기사수정

다른 곳에 퍼가실 때는 아래 고유 링크 주소를 출처로 사용해주세요.

http://www.bitweb.co.kr/news/view.php?idx=738
기자프로필
프로필이미지
나도 한마디
※ 로그인 후 의견을 등록하시면, 자신의 의견을 관리하실 수 있습니다. 0/1000
현재의견(총 1 개)
  • kcjen2018-04-23 13:23:53

    좋은 강좌 감사드립니다. 연재 끝나면 출판도 해주세요^^

실시간 암호화폐 순위 확인하기
코인마켓캡
모바일 버전 바로가기