본문 바로가기
프로젝트/자바로 만드는 블록체인

[블록체인] 자바로 내 첫 번째 블록체인을 만들어보자 - Part 2. /Java Block Chain / 자바 블록체인

by 으노으뇨 2022. 12. 8.
728x90
반응형
SMALL

안녕하세요~~~!!! 

이번에 포스팅을 할 주제는! 자바로 블록체인을 구현해보고 공부해보는 시간 제 2탄입니다!!

본 포스팅은 블록체인 관련된 두번째 이므로 첫 번째 포스팅을 참고해주세요! https://uno-kim.tistory.com/252
 

[블록체인] 자바로 내 첫 번째 블록체인을 만들어보자 - Part 1. / Java BlockChain / 자바 블록체인

안녕하세요~!!! 이번 포스팅으로는 자바로 블록체인을 만들어보고 학습내용을 정리해 보겠습니다! 지금이 튜토리얼 시리즈는 블록체인 기술이라던가 블록체인 개발에 대해서 조금 도움과 이해

uno-kim.tistory.com

조금 더 설명을 드리자면 이번에 포스팅에서의 내용은

  • 간단한 지갑을 만들어보기
  • 디지털 서명된 거래 내역들을 우리가 만든 블록체인을 이용해서 보내보기
  • 미칠듯한 쾌감을 느껴보기ㅋㅋ

 

출처는 영어공부겸 자바공부겸 블록체인 겸 https://medium.com/programmers-blockchain/creating-your-first-blockchain-with-java-part-2-transactions-2cdac335e0ce

사이트를 참고하였습니다. 그리고 중간중간 해석이나 그런게 막히면 그냥 해석안하고 넘어가거나 대강 비슷한 단어로 의역한점은 죄송합니다 ~~ㅎㅎ
 

Creating Your First Blockchain with Java. Part 2 — Transactions.

The aim of this tutorial series, is to help you build a picture of how one could develop blockchain technology. You can find part 1 here.

medium.com

이제 우린 위의 기능들을 다 할 수 있게 됩니다!!!

걱정마세요 ㅋㅋ 이것은 그냥 보여주기식인데 지난 포스팅보다 조금 길수가 있겠네용


지난 포스팅에서 만든 체인은 필요없는 메세지만 남았었습니다. 
이번에는 이 데이터를 실제 거래에서 쓰일수 있게끔 작성해보겠습니다.
그리고 이를 통해서 우린 쉬운 코인을 만들어 보겠습니다. 
저는 제 이름을 따서 UnoCoin으로 이름을 짓겠습니다 ㅎㅎㅎ

1. 지갑을 준비한다.

코인의 소유구너은 블록체인 상에서의 거래로 이루어진다. 모든 참여자들은 이런 것들을 받거나 보낼 수 있는 주소를 가지고 있습니다. 이걸 지갑이라고 합니다.

지갑은 이런 주소들을 담을 수 있습니다. 대부분의 지갑은 블록체인의 새로운 거래를 만들 수 있는 소프트웨어 적인 기능도 있습니다.!

걱정마세요! 이 정보들은 트랜잭션에 대한거에요, 나중에 설명드릴게요!

이제 지갑에 대한 클래스를 작성해보곘습니다! 

지갑클래스

package Java.BlockChain;

import java.security.PrivateKey;
import java.security.PublicKey;

public class Wallet
{
	private PrivateKey privateKey;
	private PublicKey publicKey;
	
	public PrivateKey getPrivateKey()
	{
		return privateKey;
	}
	public void setPrivateKey(PrivateKey privateKey)
	{
		this.privateKey = privateKey;
	}
	public PublicKey getPublicKey()
	{
		return publicKey;
	}
	public void setPublicKey(PublicKey publicKey)
	{
		this.publicKey = publicKey;
	}
}

변수명을 보면 공개키, 암호키가 있습니다.

Q. 공개키, 암호키가 뭘 위한건가요?

우선 앞서 말한 UnoCoin의 공개키는 우리의 주소 역할을 합니다. 이것은 만천하에 알려져도 좋습니다.

왜냐면 다른사람들이 돈을 주고 받을 때 써야하니까요, 그러나 우리의 비공개키, 암호키는 거래에 서명할때 사용됩니다.

그러니 누구에게도 이게 알려져선 안됩니다!!

비공개키는 간섭당하기 싫은? 즉 변경되어 지기 싫은 데이터 싸인을 한다! 그리고 공개키는 서명을 확인하는데 사용된다.

그래서 우리는 공개키와 암호키를  KeyPair  라는 것 안에 생성시킬겁니다. 

그리고 우리는 Elliptic-curve cryptography 를 사용합니다.

https://en.wikipedia.org/wiki/Elliptic-curve_cryptography

여기서 잠깐!!!
ECC라고 부르는 타원곡선암호법이라고도 합니다.
타원 곡선 암호법은 유한한 필드에 걸친 타원곡선의 대수구조에 기초한 공개키 암호법입니다.
더이상 들어가면 저도 모르고... 모르는데 설명하기엔 그렇고.. 그냥 그렇구나하고 넘어가겠습니다 ㅈㅅㅈㅅ
 

Elliptic-curve cryptography - Wikipedia

From Wikipedia, the free encyclopedia Jump to navigation Jump to search Approach to public-key cryptography Elliptic-curve cryptography (ECC) is an approach to public-key cryptography based on the algebraic structure of elliptic curves over finite fields.

en.wikipedia.org

우선 해당 방법을 통해서 우리지갑에 해당 키들을 생성할 수 있게 해보겠습니다!!!

	public Wallet()
	{
		generateKeyPair();
	}
	
	public void generateKeyPair()
	{
		try
		{
			KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ECDSA", "BC");
			SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
			ECGenParameterSpec ecSpec = new ECGenParameterSpec("prime192v1");
			// 아래의 메서드로 초기화하고, 키 페어를 생성합니다!
			keyGen.initialize(ecSpec, random); // 256 바이트는 허용가능한 보안수준을 제공합니다!!!
			KeyPair keyPair = keyGen.generateKeyPair();
			// Set the public and private keys from the keyPair
			this.privateKey = keyPair.getPrivate();
			this.publicKey = keyPair.getPublic();
		}
		catch (Exception e)
		{
			throw new RuntimeException(e);
		}
	}

여기서 봐야할 것은 이 메소드가 자바 시큐리티에서 가져왔고, ECC 방법으로 KeyPair을 생성시킵니다 ㅎㅎ

이제 이 메소드를 통해서 공개키와 암호키들을 생성 및 설정 하게됩니다.

지갑 클래스에 대해서 틀을 잡았으니 이제 거래/트랜잭션에 대해서 알아보겠습니다. 

내 코인이 현금보다 값질까? ㅋㅋ

2. 트랜잭션 & 서명

각 고유 거래/ 트랜잭션에 는 

  • 돈을 보내는 사람의 공개키 주소
  • 돈을 받는 사람의 공개키 주소
  • 이체되는 금액과 계좌의 잔액
  • 보내는 사람이 실제 계좌를 가지고 있고, 그 돈이 있는지 증명할 이전 거래들의 참조값
  • 출력 -  해당 거래로 잔액이 나타납니다. 
  • 주소의 소유자가 이 트랜잭션 전송에 대한 사람이고, 데이터가 변경되지 않았음을 증명하는 암호화 서명이다.
    >> 제 3자가 보내는 금액을 수정하거나 그런걸 못하게 막음

*워 빡세다... 갓 파파고...

그럼 한번 새로운 거래에 대한 클래스를 만들어보겠습니다!!!

package Java.BlockChain;

import java.security.PublicKey;
import java.util.ArrayList;

public class Transaction
{
	public float value;
	public PublicKey sender; // 보낸사람의 공개키가 저장됨, 솔리디티 공부하면 대강 느껴짐
	public PublicKey reciepient; // 받는사람의 공개키
	public String transactionId; // 이것은 트랜잭션의 해시값이기도 합니다.
	public byte[] signature; // 다른 사람들로부터 이 지갑과 거래에 대하것을 방어하기 위함
	private int sequence = 0; // 얼마나 많은 트랜잭션이 생성되었는지 대략적으로 계산하기위한 변수
	public ArrayList<TransactionInput> inputs = new ArrayList<TransactionInput>();
	public ArrayList<TransactionOutput> outputs = new ArrayList<TransactionOutput>();
	
	public Transaction(PublicKey from, PublicKey to, float value, ArrayList<TransactionInput> inputs)
	{
		this.sender = from;
		this.reciepient = to;
		this.value = value;
		this.inputs = inputs;
	}
	// 트랜잭션 해시(ID로 사용)를 계산
	private String calulateHash()
	{
		this.sequence++; // 두 개의 동일한 트랜잭션을 방지하기 위해 시퀀스를 변경
		return BlockUtils.applySha256(BlockUtils.getStringFromKey(this.sender) + BlockUtils.getStringFromKey(this.reciepient) + Float.toString(this.value) + this.sequence);
	}
}

새로작성했지만 지금 정의하지않은 함수들과 클래스, 변수들로 오류가 펄펄 끓고 있을겁니다...

저두그래용...

나중에 추후에 만들어 보기로 하겠습니다.

트랜잭션 클래스는 시그니쳐를 만들고, 확인할 수 있는 적절한 메소드를 가지고있고, 또 거래에 대해 유효성검사를 해줍니다.

그러나...

Q. 그럼 시그니쳐의 목적은 뭐고 어케일해요?

시크쳐는 블록체인에서 2가지 역할을 한답니다. 

  1. 실제 코인의 오너만이  그 코인을 사용할 수 있게 하는 역할
  2. 새로운 블럭이 생성되기 전 이미 접수된 트랜잭션에 대해 다른 사람들이 수정하지 못하게 함
예를들어서 
김은호라는 사람이 2개의 으노코인을 영희에게 보내고 싶다고 하자, 그럼 그들의 소프트웨어는 거래를 생성하고 다음 블록에 그것들을 포함시키기위해 광부들(블록을 만드는 주체)에게 보냅니다. 그것을 받은 주체는 또다른 제 3자 존이라는 이름으로 슥삭 이름을 바꾸어 코인을 변경하려고 시도했습니다!!!! 헉 ㅋ
그런데 이미 암호키에 서명을 해서 큰일이 일어나지 않았습니다. 휴..

이전의 코드 블럭에서 우리의 서명은 바이트 묶음으로 되어있습니다.  그러니 서명을 생성하는 메소드를 만들어보겠습니다.

지난 포스팅에서 만들어놨던 BlockUtils 에 추가해보곘습니다! (원문에서는 StringUtil)

	/**
	 * @apiNote ECDSA 서명을 적용하고 결과를 반환.
	 * @param (PrivateKey,
	 *        String) 암호키, 문자열
	 * @return (byte[]) byte arr
	 */
	public static byte[] applyECDSASig(PrivateKey privateKey, String input)
	{
		Signature dsa;
		byte[] output = new byte[0];
		try
		{
			dsa = Signature.getInstance("ECDSA", "BC");
			dsa.initSign(privateKey);
			byte[] strByte = input.getBytes();
			dsa.update(strByte);
			byte[] realSig = dsa.sign();
			output = realSig;
		}
		catch (Exception e)
		{
			throw new RuntimeException(e);
		}
		return output;
	}
	
	/**
	 * @apiNote 서명확인
	 * @param (PublicKey,
	 *        String, byte[]) 공개키, 문자열, 서명들
	 * @return (boolean) 유효하면 true
	 */
	public static boolean verifyECDSASig(PublicKey publicKey, String data, byte[] signature)
	{
		try
		{
			Signature ecdsaVerify = Signature.getInstance("ECDSA", "BC");
			ecdsaVerify.initVerify(publicKey);
			ecdsaVerify.update(data.getBytes());
			return ecdsaVerify.verify(signature);
		}
		catch (Exception e)
		{
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * @apiNote 키를 받아 문자열로 리턴
	 * @param (Key)
	 *        키
	 * @return (String) 문자열
	 */
	public static String getStringFromKey(Key key)
	{
		return Base64.getEncoder().encodeToString(key.getEncoded());
	}

흐ㅜ이... 저란 사람은 짱친절하게 주석도 다달아 주었습니다 ㅎㅎㅎ

> 이 메소드를 이해 못했다고 해도 걱정마세요, 알아야할 것은 apllyECDSASig 가 암호키, 입력값을 받고 서명을 하고 , 바이트로된 배열을 반환하는것과 veifyECDSASig 는 서명공개키, 데이터를 받고 그 서명이 유효한지에 대해 판단하는것 그리고 getStringFromKey가 암호화된 문자열을 어떤 키로부터 반환하는 것이다~ 정도만 알면된답니다...<<원문에서..

그럼 이 메서드들을 활용해 보도록 하겠습니다.

그러기 위해서 Transaction클래스에 서명발생시키는 메소드와 서명검사하는 메소드를 추가해줍니다!

	// 서명을 하게한다.
	public void generateSignature(PrivateKey privateKey)
	{
		String data = BlockUtils.getStringFromKey(this.sender) + BlockUtils.getStringFromKey(this.reciepient) + Float.toString(this.value);
		this.signature = BlockUtils.applyECDSASig(privateKey, data);
	}
	// 서명한 데이터가 변조되진 않았는지 검사한다.
	public boolean verifiySignature()
	{
		String data = BlockUtils.getStringFromKey(this.sender) + BlockUtils.getStringFromKey(this.reciepient) + Float.toString(this.value);
		return BlockUtils.verifyECDSASig(this.sender, data, this.signature);
	}
이제 이 서명들은 블록을 만드는 주체로 부터 새로은 트랜잭션이 블록에 추가될 때마다 확인을 받게됩니다!

우린 또한 블록체인 유효성 검사할때 서명을 확인을 할 수 있다.

3. 지갑과 서명에 대한 테스트!

거의 절반정도 완료되었습니다 ㅠㅠ

이제 테스트도 한번 해보겠습니다. 

unoChain을 새로 만들어보고 작성해보겠습니다!!

package Java.BlockChain;

import java.security.Security;
import java.util.ArrayList;

public class UnoChain
{
	public static void main(String[] args)
	{
		Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
		// 새지갑 2개 정도 생성 하나는 보내는이, 받는이 
		Wallet walletA = new Wallet();
		Wallet walletB = new Wallet();
		// 공개키랑 암호키를 확인하기위해 출력
		System.out.println("Private and public keys:");
		System.out.println(BlockUtils.getStringFromKey(walletA.getPrivateKey()));
		System.out.println(BlockUtils.getStringFromKey(walletA.getPublicKey()));
		// A와 B간 거래를 시작한다.
		Transaction transaction = new Transaction(walletA.getPublicKey(), walletB.getPublicKey(), 5, null);
		transaction.generateSignature(walletA.getPrivateKey());
		//서명이 작동하는지 확인하고 공개키를 사용하여 확인
		System.out.println("Is signature verified");
		System.out.println(transaction.verifiySignature());
	}
}
꼭꼭 진행중 https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk16/1.46 에서 해당 의존성을 넣어주세요!!!

그리고TransactionOutput,  TransactionInput 클래스 2개도 생성해줍니다.

package Java.BlockChain;
public class TransactionInput
{
	public String transactionOutputId; // 트랜잭션 참조하기 위한 아이디
	public TransactionOutput UTXO; // 사용하지 않은 트랜잭션 출력
	
	public TransactionInput(String transactionOutputId)
	{
		this.transactionOutputId = transactionOutputId;
	}
}
package Java.BlockChain;

import java.security.PublicKey;

public class TransactionOutput
{
	public String id;
	public PublicKey reciepient; // 이 코인의 새 주인
	public float value; // 잔액
	public String parentTransactionId; // 생성된 트랜잭션의 ID
	// Constructor
	public TransactionOutput(PublicKey reciepient, float value, String parentTransactionId)
	{
		this.reciepient = reciepient;
		this.value = value;
		this.parentTransactionId = parentTransactionId;
		this.id = BlockUtils.applySha256(BlockUtils.getStringFromKey(reciepient) + Float.toString(value) + parentTransactionId);
	}
	// 동전이 내껀지 확인
	public boolean isMine(PublicKey publicKey)
	{
		return (publicKey == reciepient);
	}
}

과연 실행해본다면?!

오와!!! ㅎㅎㅎㅎ

결과가 이렇게 나온다면 이제 성공한겁니다. ㅎㅎㅎ

너무 길어서 중간에 한번 끊어야 겠군요 ㅠㅠ

우선 테스트 까지 모두 성공적으로 진행되었고, 해당 인풋,아웃풋 클래스에 대한 설명은 다음 포스팅에서 하겠습니다!!


긴글 읽어주셔서 감사합니다.!!!

 

728x90
반응형
LIST

댓글