C++ 으로 블록체인 구현해보기
- -
구현을 따라해보기 위해서, 외국 유튜버분이 구현해보신 영상을 보고 코드를 작성하고 리뷰를 하려고합니다.
TransactionData.h
#pragma once
#ifndef TransactionData_h
#define TransactionData_h
#include <string>
// Transaction Data
struct TransactionData
{
double amount;
std::string senderKey;
std::string receiverKey;
time_t timestamp;
TransactionData() {};
TransactionData(double amt, std::string sender, std::string reciver, time_t time) {
amount = amt;
senderKey = sender;
receiverKey = reciver;
timestamp = time;
};
};
#endif // !TransactionData_h
- TransactionData : 거래내역을 나타내는 구조체입니다
- amount : 코인 거래 양
- senderKey : 보낸 사람의 지갑 주소
- receiverKey : 받은 사람의 지갑 주소
- timestamp : 거래가 이루어진 시간
Block.h
#pragma once
#ifndef Block_h
#define Block_h
#include "TransactionData.h"
// Block Class
class Block {
private:
int index;
size_t blockHash;
size_t previousHash;
size_t generateHash();
public:
// Constructor
Block(int idx, TransactionData d, size_t prevHash);
// Get Index
size_t getIndex();
// Get Original Hash
size_t getHash();
// Get Previous Hash
size_t getPreviousHash();
// Transaction Data
// Would ordinarily be a private member with a "getter" : getData()
TransactionData data;
// Validate Hash
bool isHashValid();
};
#endif // !Block_h
- Block : 블록의 정보를 가지고 있는 헤더 파일입니다.
private:
- index : 블록의 인덱스
- blockHash : 현재 블록의 해쉬값
- previousHash : 이전 블록의 해쉬값
- generateHash() : 현재 들어온 값들을 가지고 해시값을 생성해줍니다.
transaction data는 원래는 private 멤버이지만, 블록체인이 어떻게 해킹에서 안전한지를 보여주기 위해서 public으로 설정하였습니다.
public:
- getIndex() : 현재 블록의 인덱스를 리턴합니다.
- getHash() : 현재 블록의 해쉬값을 리턴합니다.
- getPreviousHash() : 이전 블록의 해쉬값을 리턴합니다.
- isHashValid() : 생성한 해쉬값이, 현재 블록의 해쉬와 맞는지 참/거짓 유무를 리턴합니다.
transaction data가 private 멤버로 들어갈 때에는 추가적으로 getData() 함수를 생성하여
transaction 데이터를 가져올 수 있어야합니다.
Block.cpp
#include <iostream>
#include <string>
#include "Block.h"
#include "TransactionData.h"
// Constructor with params
Block::Block(int idx, TransactionData d, size_t prevHash) {
index = idx;
data = d;
previousHash = prevHash;
blockHash = generateHash();
}
// private Functions
size_t Block::generateHash() {
// creating string of transcation data
std::string toHashS = std::to_string(data.amount) + data.senderKey + data.receiverKey + std::to_string(data.timestamp);
// 2 hashed to combine
std::hash<std::string> tDataHash; // transaction data string
std::hash<std::string> prevHash; // re-hashed previous hash (for combination)
// combine hashes and get size_t for block hash
return tDataHash(toHashS) ^ (prevHash(std::to_string(previousHash)) << 1);
}
// public Functions
size_t Block::getHash() {
return blockHash;
}
size_t Block::getPreviousHash() {
return previousHash;
}
size_t Block::getIndex() {
return index;
}
bool Block::isHashValid() {
return generateHash() == getHash();
}
헤더파일에 대한 클래스의 기능 함수입니다.
눈여겨 볼 점은
generateHash() 함수 가 data에 관한 모든 정보를 해쉬와 이전해쉬의 결합값을 리턴해주는 것입니다.
Blockchain.h
#pragma once
#ifndef Blockchain_h
#define Blockchain_h
#include "Block.h"
#include <vector>
// Blockchain Class
class Blockchain {
private:
Block createGenesisBlock();
std::vector<Block> chain;
public:
// Constuctor
Blockchain();
// Public Functions
std::vector<Block> getChain();
Block* getLatestBlock();
void addBlock(TransactionData data);
bool isChainValid();
void printChain();
};
#endif // !Blockchain_h
- Blockchain : 블록체인에 대한 정보를 가지고 있는 헤더 파일 입니다.
private:
- createGenesisBlock() : 맨 초기의 블록을 생성해주는 함수입니다.
- vector chain : 블록체인의 기반이 되는 체인역할의 벡터입니다.
public:
- getChain() : 블록체인을 가져오기 위한 함수인듯 하지만, 여기서 구현을 되지 않았습니다.
- getLatestBlock() : 원래는 없어야하는 포인터이지만, 우리는 어떻게 작동하는지 보기 위하여, 블록의 메모리를 증명하기 위한 함수입니다.
- addBlock() : 새로운 블럭을 만들어, 블록체인에 추가해주는 함수입니다.
- isChainValid() : 처음부터 현재블럭까지 블럭값이 연결이 된건지 확인해주는 함수입니다.
- printChain() : 블록체인의 정보를 출력해주는 함수입니다.
Blockchain.cpp
#include <iostream>
#include <string>
#include "Blockchain.h"
// Blockchain Constructor
Blockchain::Blockchain() {
Block genesis = createGenesisBlock();
chain.push_back(genesis);
}
// Create Genesis Block
Block Blockchain::createGenesisBlock() {
// Get Current Time
time_t current;
// Setup Initial Transaction Data
TransactionData d(0, "Genesis", "Genesis", time(¤t));
// creating string of transaction data
std::string toHashS = std::to_string(d.amount) + d.senderKey + d.receiverKey + std::to_string(d.timestamp);
// 2 hashed to combine
std::hash<std::string> tDataHash; // transaction data string
std::hash<std::string> prevHash; // re-hashed previous hash (for combination)
// combine hashes and get size_t for block hash
size_t hash = tDataHash(toHashS) ^ (prevHash(std::to_string(0)) << 1);
Block genesis(0, d, hash);
return genesis;
}
// We only need pointer here
// to demonstrate manipulation of transaction data
Block* Blockchain::getLatestBlock() {
return &chain.back();
}
void Blockchain::addBlock(TransactionData d) {
int index = (int)chain.size();
std::size_t lastHash = (int)chain.size() > 0 ? getLatestBlock()->getHash() : 0;
Block newBlock(index, d, lastHash);
chain.push_back(newBlock);
}
bool Blockchain::isChainValid() {
std::vector<Block>::iterator it;
for (it = chain.begin(); it != chain.end(); ++it) {
Block currentBlock = *it;
if (!currentBlock.isHashValid()) {
// INVALID!!
return false;
}
// Don't forget to check if this is the first item
if (it != chain.begin()) {
Block previousBlock = *(it - 1);
if (currentBlock.getPreviousHash() != previousBlock.getHash()) {
// INVALID!!
return false;
}
}
}
return true;
}
void Blockchain::printChain() {
std::vector<Block>::iterator it;
for (it = chain.begin(); it != chain.end(); ++it) {
Block curruntBlock = *it;
std::cout << '\n' << '\n' << "Block ===============================";
std::cout << '\n' << "Index: " << curruntBlock.getIndex();
std::cout << '\n' << "Amount: " << curruntBlock.data.amount;
std::cout << '\n' << "SenderKey: " << curruntBlock.data.senderKey.c_str();
std::cout << '\n' << "ReceiverKey: " << curruntBlock.data.receiverKey.c_str();
std::cout << '\n' << "Timestamp: " << curruntBlock.data.timestamp;
std::cout << '\n' << "Hash: " << curruntBlock.getHash();
std::cout << '\n' << "Previous Hash: " << curruntBlock.getPreviousHash();
std::cout << '\n' << "Is Block Valid?: " << curruntBlock.isHashValid();
}
}
구현에서 눈여겨 볼 코드는 isChainValid()를 통하여 블록의 해쉬값이 처음부터 지금까지 Valid? 한지를 결정하는 코드로 대충 이런식으로 작동이 되구나를 알 수 있게 되었습니다.
실제 작동확인
#include <iostream>
#include <string>
#include <vector>
#include <ctime>
#include "Block.h"
#include "Blockchain.h"
#include "TransactionData.h"
using namespace std;
int main() {
// Start Blockchain
Blockchain AwesomeCoin;
// Data for first added block
time_t data1Time;
TransactionData data1(1.5, "Jun", "Uram", time(&data1Time));
AwesomeCoin.addBlock(data1);
time_t data2Time;
TransactionData data2(1.0, "Uram", "Jun", time(&data2Time));
AwesomeCoin.addBlock(data2);
AwesomeCoin.printChain();
cout << '\n' << "is chain valid? " << AwesomeCoin.isChainValid();
// Somone's getting sneaky
Block* hackBlock = AwesomeCoin.getLatestBlock();
hackBlock->data.amount = 10000;
hackBlock->data.receiverKey = "Hacker!!"; // hahahaha
AwesomeCoin.printChain();
cout << '\n' << "is chain valid? " << AwesomeCoin.isChainValid();
}
정상 작동
해커 개입
제대로 작동이 잘되는 블록체인이라면, 이전의 해쉬값에 상관없이 계속 Valid를 유지하는 모습을 볼 수 있지만
해커가 개입해서, 말도 안되는 정보를 바꿨다고 해도, 이전 해쉬의 값이 달라져가기 때문에 맞지 허락하지 않는 모습을 볼 수 있습니다.
마치며
실제로 비트코인이나, 이더리움, 리플같은 대형 코인들은 훨씬 많은 기능들이 있을 듯 하고, 채굴에 관련된 점도 포함되어 있을것입니다.
하지만 이 코드는 실제로 블록체인이 어떻게 운용되는지를 대충 간편하게 알아 볼 수 있는 코드이고, 이런식으로 흘러간다는 점을 알 수 있다는 점에서 저에게 꽤나 도움이 되었습니다.
출처:
https://www.youtube.com/watch?v=2VDQeQfh4Hs&t=315s
'💰 Crypto's (가상화폐, 코인) > Tech (기술설명, 용어)' 카테고리의 다른 글
밈코인(Meme Coin) 왜 돈이 될까? (0) | 2024.03.17 |
---|---|
스테이블 코인 (Stable coin) 같이 알아봅시다 (1) | 2022.04.18 |
간단한 Bitcoin - PoW 채굴 원리 (0) | 2022.04.14 |
코인 채굴의 간단한 순서 (0) | 2022.04.13 |
디앱 (DApp) 이란? (0) | 2022.04.09 |
소중한 공감 감사합니다