Referenced by a Savjee
https://www.youtube.com/watch?v=zVqczFZr124&t=767s
실제 블록체인의 모습이 어떤지, 거래가 실제로는 내부에서 어떻게 이루어지는지,
그래서 블록체인은 어떻게 구현해야 하는지
지갑, 트렌젝션 등에 있는 블랙박스를 파해치고자 합니다.
완성된 블록체인 - github.com/znxkznxk1030/yscoin
블록체인이란 무엇인가요?
사전적인 정의는 누구나 읽을 수 있는 공개된 블록으로 이루어진 퍼블릭 데이터베이스입니다.
그 각각의 블록들은 절대 변하지 않는 정보, 속성를 가지고 있죠.
그렇기에 한 블록이 한번 체인에 추가되면 절대 변할수 없게 되어,
누구나 열람은 가능하지만 위/변조가 불가능한 특징을 가진 데이터베이스입니다.
왜 블록체인이 필요할까요?
앞서 말했듯, 블록체인은 누구나 읽을 수 있는 블록들이 곳곳에 퍼진 퍼블릭 데이터베이스입니다.
이러한 특징은 현재 우리가 사용하는 단일 서버시스템의 최대 약점인 단일 장애점 (Single Point Of Failure, SPOF)를 보안해줄 수 있습니다.
쉽게 설명하자면, 한개의 서버에서 관리하는 은행, 공인인증서, 계약서 등은 해당 서버의 장애가 났을 때, 또는 백업하는 시간에는 이용 할수 없는 것과 반대의 장점이 있습니다.
오늘 우리가 배울 것들
변조가 불가능한 블록을 어떻게 만드는지 확인해 봅시다.
- 블록의 구성요소
- 블록체인의 구성
- 블록체인의 유효성 판별 방법
코딩 할 준비를 해봅시다 (in Terminal)
노드 패키지 매니저 (npm)와 파일은 하나 생성해봅시다.
그리고 블록내에서 필요한 암호화를 위해 crpyto-js를 설치해주세요.
npm init
npm install --save crypto-js
touch block.js
touch block-chain.js
실행시키기 (in Terminal)
준비가 되었으면 실행을 해봐요
아무것도 뜨지 않으면 정상입니다.
node block-chain.js
Block
블록체인의 블록은 크게 두가지로 이루어져 있습니다.
- 자기의 속성들
- 그리고 그 속성들로 비벼진 hash 값
속성는 자유롭게 추가 가능하지만 몇가지 필수 속성들이 있습니다.
- timestamp
- transactions (거래를 위한 속성이지만, 오늘은 거래는 생략할 예정입니다.)
- hash
- previousHash
- nonce (채굴을 위한 속성이지만, 오늘은 채굴도 생략할 예정입니다)
오늘 우리가 구성할 속성(Transaction, Nonce 생략)
- index
- * timestamp
- data
- * previousHash
- *\ hash
**
const SHA256 = require('crypto-js/sha256');
사용할 해시알고리즘인 sha256을 crypto-js에서 가져옵니다.
class Block() {
constructor(index, timeStamp, context, previousHash) {
this.index = index;
this.previousHash = previousHash;
this.timeStamp = timeStamp;
this.context = context;
this.hash = this.calculateHash();
}
블록은 오늘 우리가 구성할 속성으로 채워 넣습니다.
이때 hash를 뺀 모든 속성들은 constructor 생성자의 파라미터로 받아왔어요.
그리고 마지막 속성인 hash는 나머지 속성들을 해시알고리즘으로 비벼넣습니다.
calculateHash() {
return SHA256(
this.index
+ this.previousHash
+ this.timeStamp
+ JSON.stringify(this.context))
.toString;
}
그리고 마지막으로 블록을 밖에서 호출할 수 있도록 Export 시켜줘요.
export default Block;
짝짝짝 그럼 지금까지 우리는 block을 완성했어요!
BlockChain
지금까진 블록체인에서의 블록을 만들었어요.
아지만 블록만으론 블록체인이이 될 수 없어요.
블록들이 모여 블록체인이 되기 위해서는 4가지의 행동이 필수적으로 필요합니다.
- GenesisBlock 생성하기
- 가장 최근 생성된 Block 가져오기
- Block 추가하기
- Chain이 유효한지 검사하기
최초의 Block, GenesisBlock 생성하기
먼저 앞서 만들 블록을 사용하기 위해 import 시켜줘요.
import Block from "./block"
최초의 블록, GenesisBlock을 생성해 봐요.
이때 중요한점은 GenesisBlock은 첫번째 블록이기 때문에 이전 블록이 필요하지 않기 때문에,
previousHash값에 0을 넣었어요.
class BlockChain {
constructor(){
this.chain = [this.createGenesisBlock];
}
createGenesisBlock() {
return new Block(0, "01/01/2018", "Genesis Block", "0");
}
}
가장 최근에 생성된 Block 가져오기
getLastestBlock() {
return this.chain[this.chain.length - 1];
}
Block 추가하기
새로운 블록을 생성해 붙여봅시다.
이때 새로운 블록에는 중요한점 2가지가 있습니다.
1. 이전 블록의 해쉬값을 자지고 있어야 합니다. (블록체인의 getLastestBlock().hash 이용)
2. 새로생긴 블록의 해쉬값은 이전 블록의 해쉬 값을 포함하여 새로 계산해 줍니다. (현재 블록의 calculateHash 이용)
addBlock(newBlock) {
newBlock.previousHash = this.getLastestBlock().hash;
newBlock.hash = newBlock.calculateHash();
this.chain.push(newBlock);
}
유효성 검사하기
isChainVaild() {
let genesisBlock = this.chain[0];
if (genesisBlock !== genesisBlock.calculateHash()) {
return false;
}
for (let i = 1; i < this.chain.length - 1; i++) {
let previousBlock = this.chain[i - 1];
let currentBlock = this.chain[i];
if (currentBlock.hash !== currentBlock.calculateHash()) {
return false;
}
if(currentBlock.previousHash !== previousBlock.hash) {
return false;
}
}
return true;
}
Genesis Block은 이전 해쉬값이 없기 때문에 시작은 0이 아닌 1 입니다.
자기 properties를 이용한 해쉬값과 자기 해쉬값이 같은지 확인해줍니다.
아래와 같이, 내용을 위조할 경우 이 로직에서 유효하지 않은 블록임을 알 수 있어요
blockChain[3].money = 100;
현재 블록의 previousHash과 이전 블록의 hash값이 같은지 확인해줍니다.
아래와 같이, 조작된 블록을 추가할 경우 이로직에서 유효하지 않을 블록임을 알 수 있어요
blockChain[4] = new Block(조작된 생성자 파라미터)
완성된 Block Chain
class BlockChain {
constructor(){
this.chain = [this.createGenesisBlock];
}
createGenesisBlock(){
return new Block(0, "01/01/2018", "Genesis Block", "0");
}
getLastestBlock() {
return this.chain[this.chain.length - 1];
}
addBlock(newBlock) {
newBlock.previousHash = this.getLastestBlock().hash;
newBlock.hash = newBlock.calculateHash();
this.chain.push(newBlock);
}
isChainVaild() {
let genesisBlock = this.chain[0];
if (genesisBlock !== genesisBlock.calculateHash()) {
return false;
}
for (let i = 1; i < this.chain.length - 1; i++) {
let previousBlock = this.chain[i - 1];
let currentBlock = this.chain[i];
if (currentBlock.hash !== currentBlock.calculateHash()) {
return false;
}
if(currentBlock.previousHash !== previousBlock.hash) {
return false;
}
}
return true;
}
}
실제로 블록체인을 테스트해 봅시다
import Block from "./block";
import BlockChain from "./block-chain";
let arthurCoin = new BlockChain();
ysCoin.addBlock(new Block(1, "20/07/2017", { amount: 4 }));
ysCoin.addBlock(new Block(2, "20/07/2017", { amount: 8 }));
console.log('Blockchain valid? ' + arthurCoin.isChainValid()); // => true
console.log('Changing a block...');
ysCoin.chain[1].data = { amount: 100 }; // => 위조
console.log("Blockchain valid? " + arthurCoin.isChainValid()); // => false