block chain 개념 및 코드

YU YU·2021년 9월 1일
4

경일_BlockChain

목록 보기
1/24

01.block chain 개념


01-1)사전 개념

  • 네트워크(http, socket)
  • 분산원장
    c.f)socket(p2p 연결형식)

01-2)Hash

  • sha256
    단방향 암호화(복호화할 수 없는 암호시스템)
    자리수 고정됨(a를 넣어도 64글자 bbbbdcaadsf를 넣어도 64자리...)

01-3)merkle Tree

  • 만들어진 목적
    1. 해당 거래를 효율적으로 찾아가도록 하기 위해서 만들어짐. 위의 블록에서 L4를 찾아가려면 1>2>3>4로 찾아가는 과정 없이, hash1>hash1-1로 2단계로 찾아가면 되니 효율적이다.

    2. 또한 위변조를 감지할 수 있다. 하위 노드값을 변조하면 해시값이 바뀌고, 그로 인해 상위 노드값이 변하기 때문에 결국 top hash값이 바뀌게 되어 위변조가 된 것임을 알 수 있다.
    - 출처:https://blockone.tistory.com/11, http://wiki.hash.kr/index.php/%EB%A8%B8%ED%81%B4%ED%8A%B8%EB%A6%AC
  • 사용방법
    package가 있다.
    javascript에서는 merkletreejs를 이용해서 사용할 수 있다.

02.합의 알고리즘


블록체인을 이용한 코인들(?)은 대개 2가지 방식을 채택한다.

작업 증명 방식
지분 증명 방식

02-1) 작업 증명(Proof of Work)

모두가 같은 정보를 가지고 있다. 그러나 성능이 좋은 컴퓨터의 유저는 더 많은 정보를 수정할 수 있는 권한을 가질 수 밖에 없다. 왜냐하면 초고속 인터넷의 유저가 같은 시간에 다른 유저들보다 더 많은 노드에게 정보를 전달할 수 있기 때문이다. 그래서 채굴장같이 전문적으로 채굴하는 업체가 더 많은 코인을 채굴할 수 있는 것이다.

02-2) 지분 증명(Proof of Stake)

이는 내가 더 많은 지분을 가질수록 더 많이 정보를 수정할 수 있는 권한을 가질 수 있는 것이다. 이는 블록체인 창시자의 '민주화'에 반대되는 개념인데, 블록체인이 너무나도 비효율적인 시스템이기 때문에 속도를 높이기 위해 도입된 개념이다.

03. 블록의 구성 요소


3-1) HTTP 통신 규약

블록체인은 http통신 규약과 비슷한 점이 많다. http통신은 이렇게 구성되어 있다.

start line
Header

body

03-2) 비트코인의 구성 요소

const block={
	MagicNumber:"0x",
	BlockSize:"4mb",
  	header:{
    	version:"1.0.0",
    	HashPrevBlock:[나의 부모의 주소값],
    	HashMerkleRoot:`SHA256`,
    	timestamp:`시간`,
    	bits: `작업증명 난이도를 정하는 `.
    	Nonce:`4byte의 (양수의) 난수`,
    	},
   	body:{...객체일수도, 배열일수도...}
}

다른 부분을 몰라도 header와 body영역은 블럭에 필수로 들어가야 한다. . HashPrevBlock는 이전 블록의 주소값이다. 이 항목이 0으로만 구성되어있다면 처음 생성된 블록, 즉 제네시스 블록이다.
그 요소는 헤더의 내용을 스트링으로 바꿔서 암호화를 한 결과물로 이루어져 있다.

04.블럭 만들어보기


이제는 블록체인을 직접 만들어보기로 하겠다.

04-1) header만들기

먼저 폴더에 npm init을 해준다.

🐟 version

const fs = require('fs');//filesystem

function getVersion(){
    const package = fs.readFileSync("../package.json");
    console.log(package.toString("utf8"));
};


getVersion();

기본으로 내장되어있는 fs함수를 이용한다. fs는 fileSystem의약자이다. 그래서 fs.readFileSync("파일경로")를 통해서 외부 파일의 정보를 받아올 수 있다.

위 함수를 실행시키면 다음과 같이 터미널창에 뜬다.


위와 같이 작성하고 다시 파일을 실행시켜보면 다음과 같이 결과물이 나온다.

package.json의 version이 "1.0.0"이었으니 제대로 가져온 것이 맞다.

🐟timestamp

블록이 생성된 시간 정보를 담고있다. 이것은 생성될 당시의 시간을 기록하면 된다. 엄청 간단하다.
Date.now()/1000 이 한줄이면 끝난다.
Date.now()는 현재 시간을 1970년 1월 1일 0시 0분 0초부터 현재까지 경과된 밀리 초를 반환한다.

🐟HashPrevBlock

이것은 지금의 블록의 바로 이전 블록의 '주소값'을 담고 있다. 그것을 알기 위해서는 먼저 merkletree의 사용법을 알아야 한다.

💥merkletreejs를 이용해서 merkletree생성하기

$ npm i merkletreejs crypto-js
터미널에 다음과 같이 입력하여 merkletree를 설치해준다.

자바스크립트 파일을 하나 만들어준다.

const {MerkleTree} = require('merkletreejs');
const SHA256 = require('crypto-js/sha256');
console.log(SHA256('ab'))

위 코드를 작성하고 터미널로 파일 위치까지 들어와서 $ node 파일명을 입력하여 실행을 한다. 그러면 알 수 없는 값이 나올 것이다.
그러면 console.log(SHA256('ab'))console.log(SHA256('ab').toString())으로 바꾸어 우리가 알 수 있는 값으로 변환한다.

const {MerkleTree} = require('merkletreejs');
const SHA256 = require('crypto-js/sha256');

const testSet =['a','b','c','c'];
const testArray = testSet.map((v)=>SHA256(v));
console.log(testArray);

위와 같이 수정해보자. 그러면 다음과 같은 결과값을 얻을 수 있을 것이다.

[
  {
    words: [
      -896040686,
      -904151606,
      -87936589,
      -1708925875,
      -1484328968,
      343690866,
      -1182763131,
      -1343338309
    ],
    sigBytes: 32
  },
  {
    words: [
       1042540566,     3758410,
        864636773,  1692512564,
      -1950516736, -1999360950,
       -881594706,  -711196515
    ],
    sigBytes: 32
  },
  {
    words: [
      779955203,
      -1454343454,
      1710028213,
      896042405,
      865313282,
      -1658580076,
      -1720556127,
      -1571098682
    ],
    sigBytes: 32
  },
  {
    words: [
      779955203,
      -1454343454,
      1710028213,
      896042405,
      865313282,
      -1658580076,
      -1720556127,
      -1571098682
    ],
    sigBytes: 32
  }
]

그러면 merkTree 함수를 입력해보자.

const {MerkleTree} = require('merkletreejs');
const SHA256 = require('crypto-js/sha256');

const testSet =['a','b','c','c'];
const testArray = testSet.map((v)=>SHA256(v));
const tree = new MerkleTree(testArray,SHA256);
console.log(tree);

MerkleTree는 클래스이다. 인잣값으로 2가지를 받는데 첫번째는 암호화해준 결과, 그리고 어떤 암호화 방식을 사용했는지 넣어준다. 근데 npm 사이트에 들어가도 예제를 보니 다른 암호화 방식을 쓰지 않고 그냥 SHA256을 쓴다. ㅋㅋㅋㅋ

아무튼 위와 같이 입력을 하면 다음과 같이 출력이 된다.

MerkleTree의 작동 방식
출처:https://www.npmjs.com/package/merkletreejs
layers는 깊이를 이야기한다. 위의 길이에서는 layer가 4임을 알 수 있다.

const {MerkleTree} = require('merkletreejs');
const SHA256 = require('crypto-js/sha256');

const testSet =['a','b','c','c'];
const testArray = testSet.map((v)=>SHA256(v));
const tree = new MerkleTree(testArray,SHA256);
const root = tree.getRoot();
console.log(root.toString('hex'));

MerkleTree(SHA256으로 암호화처리된 배열,SHA256).getRoot()는 Merkletree의 최상위 노드의 해시값을 가져오는 함수이다. 위의 코드를 실행하면 64자리의 값을 출력한다.

이 값을 잘 기억해놓자!

merkletreejs를 사용해 만든 것이 맞는지 검증하기

const {MerkleTree} = require('merkletreejs');
const SHA256 = require('crypto-js/sha256');

const testSet =['a','b','c','c'];
const testArray = testSet.map((v)=>SHA256(v));
const tree = new MerkleTree(testArray,SHA256);
const root = tree.getRoot();

const testRoot = 'a'
const leaf = SHA256(testRoot)
const proof = tree.getProof(leaf);
console.log(tree.verify(proof,leaf,root));
console.log(tree.verify(tree.getProof(leaf),SHA256('a'),tree.getRoot()));
console.log(tree.toString());

여기서 .getProof(SHA256('텍스트값'))은 merkletree안에 이 값이 있는지 없는지 검증하는 함수이다. 다음과 같은 결과가 출력됨을 알 수 있다.

🐟 index

인덱스는 그냥 삽입하는 요소이다.

🐟 header class 만들기


04-2) body만들기

🐟 body의 형식

배열의 형태로 집어넣는다.
[ 'ssssss' ]
배열 안의 객체의 형태로 넣어도 상관이 없다.
[{ssss},{ddddd}]

5. merkletree를 활용해 HashMarkleRoot 만들기

아까도 말했듯이 HashMarkleRoot은 자신의 바디값을 sha256이라는 암호화 패턴을 이용해서 만든 것의 루트 트리 해시값을 담고 있다.

const merkle = require('merkle');

const body = ['hello'];
const tree = merkle('256').sync(body)
const root = tree.root() || '0'.repeat(64)

위의 코드에서 구한 root값이 자신의 블록의 body값을 암호화해서 구해진 값이다. 이 값이 HashMarkleRoot가 되는 것이다.

여기서 body에 하나밖에 넣지 않았지만 여러개의 값을 넣을 수 있다. merkletree에서는 body, 즉 배열의 길이가 홀수일 때는 마지막 내용을 복사한다. 이 경우에는 마지막 블록의 해시값과 그 바로 상위의 해시값이 같다.

여기서 복사라는 개념때문에 같은 값이 2개가 마지막에 들어가 짝수값이 되면 상위 노드의 해시값도 같지 않알까 생각했었다.
그러나 const body=['a','b','c','c']를 입력한 결과 다음과 같이 상위 노드의 값이 달라짐을 알 수 있었다. 즉 'cc'를 암호화했음을 알 수 있다.

그러면 이제 제네시스 블록을 모두 만들었다!

5-1) 전체코드

전체 코드

const fs = require('fs');//filesystem
const merkle = require('merkle');
const CryptoJs = require('crypto-js');

function createGenesisBlock(){
    const version = getVersion()
    const index = 0
    const timestamp = parseInt(Date.now()/1000)
    const previousHash =  '0'.repeat(64)
    //body의 내용으로 merkleroot를 만드는 것임. 그래서 먼저 body를 만듦
    const body = ['hello block']
    const tree = merkle('sha256').sync(body)
    const root = tree.root() || '0'.repeat(64)

    const header = new BlockHeader(version,index,previousHash,time,root)
    return new Block(header,body)
}

function getVersion(){
    const package = fs.readFileSync("../package.json");
    // console.log(package.toString("utf8"));
    // console.log(JSON.parse(package).version);
    return JSON.parse(package).version;
};

class BlockHeader {
    constructor(version,index,previousHash,timestamp,merkleRoot){//header를 만들 인잣값들
        this.version = version//1{version:1}
        this.index = index
        this.previousHash = previousHash
        this.timestamp = timestamp 
        this.merkleRoot = merkleRoot

    }

}

class Block{
    constructor(header,body){
        this.header = header
        this.body = body
    }
}

const block=createGenesisBlock();
console.log(block);

5-2) 결과물


터미널에서 벗어나 오랜만에 코드를 치니까 좋았다.😎
코드로 보니까 어떻게 해야할 지 알겠다.
그런데 왜 SHA256('a')의 값과 [a,b,c]의 SHA256한 것의 merkletree의 roothash값이 같은지 모르겠다. ㅠㅠ

profile
코딩 재밌어요!

0개의 댓글