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가지 역할을 한답니다.
실제 코인의 오너만이 그 코인을 사용할 수 있게 하는 역할
새로운 블럭이 생성되기 전 이미 접수된 트랜잭션에 대해 다른 사람들이 수정하지 못하게 함
예를들어서 김은호라는 사람이 2개의 으노코인을 영희에게 보내고 싶다고 하자, 그럼 그들의 소프트웨어는 거래를 생성하고 다음 블록에 그것들을 포함시키기위해 광부들(블록을 만드는 주체)에게 보냅니다. 그것을 받은 주체는 또다른 제 3자 존이라는 이름으로 슥삭 이름을 바꾸어 코인을 변경하려고 시도했습니다!!!! 헉 ㅋ 그런데 이미 암호키에 서명을 해서 큰일이 일어나지 않았습니다. 휴..
이전의 코드 블럭에서 우리의 서명은 바이트 묶음으로 되어있습니다. 그러니 서명을 생성하는 메소드를 만들어보겠습니다.
지난 포스팅에서 만들어놨던 BlockUtils 에 추가해보곘습니다! (원문에서는 StringUtil)
> 이 메소드를 이해 못했다고 해도 걱정마세요, 알아야할 것은 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);
}
}
과연 실행해본다면?!
오와!!! ㅎㅎㅎㅎ
결과가 이렇게 나온다면 이제 성공한겁니다. ㅎㅎㅎ
너무 길어서 중간에 한번 끊어야 겠군요 ㅠㅠ
우선 테스트 까지 모두 성공적으로 진행되었고, 해당 인풋,아웃풋 클래스에 대한 설명은 다음 포스팅에서 하겠습니다!!
댓글