본문 바로가기

블록체인 기반 핀테크 및 응용 SW개발자 양성과정 일기

[117일차 복습] 블록체인 제네시스 블록 만들기 with JavaScript

반응형

블록체인 블록 만들기 

 

첫 블록, = Genesis block 은 개발자가 하드코딩을 해야함 

 

블록의 구성 요소 

A block is composed of a header and a body, where a header contains the hash of previous block, a timestamp, Nonce and the Merkle root. The Merkle root is the root hash of a Merkle tree which is stored in the block body.

 

 


 

 

제네시스 블록, Genesis Block 만들기 

 

1. 기본 세팅 

사용할 폴더 안에서 * 경로 주의 (이제 만들 src 폴더의 상위 폴더에 들어가서 npm 실행 )  

npm init 

- src 폴더 생성 > block.js 파일 생성 

 

 

 

 

2. block의 header에 들어갈 version 을 package.json에서 가져오기 

 

block.js 코드 작성

const fs = require('fs')

function getVersion(){
    const package = fs.readFileSync('../package.json');
    
    console.log(package)
    console.log(JSON.parse(package))
    console.log(JSON.parse(package).version)

    return JSON.parse(package).version;

    // 위와 같은 코드 
    // const {version} = JSON.parse(fs.readFileSync('../package.json'))
    // return version 
}

getVersion()

처음 그냥 가져오면 컴퓨터가 이해하는 언어 binary 형태로 가져오고 이를 JSON.parse를 통해 우리가 이해할 수 있는 JavaScript Object로 바꿈 -> 객체 안의 key  = version값을 가져옴 

 

 

JSON.parse()
A common use of JSON is to exchange data to/from a web server. When receiving data from a web server, the data is always a string. Parse the data with JSON.parse() , and the data becomes a JavaScript object.

 

 

 

 

3. Timestamp (유닉스 기준일 1970년 1월 1일) 구하기 

이번에는 header에 들어가는 timestamp 구하기 

 

block.js

function getCurrentTime(){
    console.log(Math.ceil(new Date().getTime()/1000))
    return Math.ceil(new Date().getTime()/1000)
}

getCurrentTime()

1000/1초까지 시간단위가 나와서 1000으로 나누고 Math.ceil로 무조건 올림 하기 

 

 

1970년 1월 1일부터 지금까지의 초이다. 아래 대략 계산해보니 정말 50년쯤 지났다! 

function getCurrentTime(){
    console.log(new Date().getTime()/1000/60/60/24/365)
    return Math.ceil(new Date().getTime()/1000/60/60/24/30/12)
}

getCurrentTime()

다시 첫번째 코드로 돌아가기 ** 

 

 

 

 

4. Merkle Tree, Merkle Root 

머클트리 (이진 트리) 는 블록들이 체인으로 연결되어 무수히 많아졌을 때 그 중, 하나의 특정 거래를 찾을 때 효율적으로 해싱한 경우의 수번만 찾으면 되도록 한 데이터 구조이다. 모든 노드 (거래)는 쌍으로 해싱을하게되고 또 해싱된 노드들이 또 짝을 이루어 해싱하다보면 Merkle root (최상위 노드) 로 수렴된다. 

 

 

** 연습 ** 

src 폴더 안에 merkle.js 파일 생성 

 

- SHA256, merkle위한 packages 설치 (경로 : src ** ) 

npm i merkletreejs crypo-js 

- block.js 에 가져오기

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

- 사용해보기 

console.log(SHA256('emily'))

emily라는 string을 sha256으로 암호화한 상태 여기에 toString()을 붙여보면 암호화된 string이 된다 

console.log(SHA256('emily').toString())

 

이제 배열로 만들어서 'a', 'b', 'c'를 각각 암호화 (sha256)해서 배열에 넣어보기 

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

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

const tree = new MerkleTree(testSet, SHA256)
console.log(tree)

testSet & tree

 

머클 트리의 최상위 노드 (Merkle root)를 가져오기 (매서드 있음) 

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

const tree = new MerkleTree(testSet, SHA256)
console.log(tree)

const root = tree.getRoot().toString('hex')
console.log('root=',root)

tree.getRoot()만 하면 binary 형태로 나오고 toString('hex')를 해야 위의 string값이 나온다. 이 때 그냥 toString() 만하면 안나옴 ! 

 

merkle.js 전체 코드 

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

// 3. merkle tree 연습 
const testSet = ['a','b','c'].map((v)=>SHA256(v))
console.log(testSet)

const tree = new MerkleTree(testSet, SHA256)
console.log(tree)

const root = tree.getRoot().toString('hex')
console.log('root=',root)

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

 

 

hex 란? 16진수 

Hexadecimal (or hex) is 
a base 16 system used to simplify how binary is represented. A hex digit can be any of the following 16 digits: 0 1 2 3 4 5 6 7 8 9 A B C D E F. Each hex digit reflects a 4-bit binary sequence.

 

 

질문

tree.verify(proof, leaf, root)의 의미 -> 특정 노드 (proof)  = true 인지 tree.verify() 매서드로 확인 

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

const tree = new MerkleTree(testSet, SHA256)
console.log(tree)

const root = tree.getRoot().toString('hex')
console.log('root=',root)

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

 

tree 연습 끝 


 

 

npm i merkle 

 

block.js 

merkle & cryptoJs 가져오기 

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

 

  Header  

BlockHeader Class 만들기 (붕어빵 틀 만들기) 

 

class BlockHeader {
    constructor(version, index, previousHash, time, merkleRoot){
        this.version = version
        this.index = index
        this.previousHash = previousHash
        this.time = time
        this.merkleRoot = merkleRoot
    }
}

const header = new BlockHeader(1,2,3,4,5)
console.log(header)

 

 

이제 Block 이라는 또다른 Class를 방금 만든 BlockHeader 사용해서 만들기

 

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

// 4. Header 만들기 by Class 
class BlockHeader {
    constructor(version, index, previousHash, time, merkleRoot){
        this.version = version
        this.index = index
        this.previousHash = previousHash
        this.time = time
        this.merkleRoot = merkleRoot
    }
}

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

const blockchain = new Block(new BlockHeader(1,2,3,4,5), ['hello'])
console.log(blockchain)

하나의 블록을 생성함 

블록 안에는 크게 header, body가 있어서 block class에 인자값에 header, body가 있다 ! 그리고 넣어줄 때 header를 생성하는 classs를 (인자값 5개) 넣고 두번째 인자값으로 body값을 넣어줌 

=> 이제 제대로 된 정보를 넣으면 요 것이 최초의 블록 = 제네시스 블록 

 

 

 

 

 

 제네시스 블록 만드는 함수 만들기 

 

block.js

function createGenesisBlock(){
    const version = getVersion();
    const time = getCurrentTime();
    const index = 0; 
    const previousHash = '0'.repeat(64) // 제네시스 블록 : 이전hash 없으므로 자리수만 64로 맞춰서 제공 
    // body는 배열 형태 *** 
    const body = ['hello block I am body ']

    // body를 가지고 merkle tree 값을 구성하기 
    const tree = merkle('sha256').sync(body)
    const root = tree.root() || '0'.repeat(64) // 예외처리 

    const header = new BlockHeader(version, index, previousHash, time, root)
    console.log('header=', header)
    console.log(new Block(header, body))

    return new Block(header,body)
}

createGenesisBlock()

 

 

 

 

다음 사용자 / 블록이 생길 때 

- 블록을 생성할 때마다 -> 배열에 넣기 

- 전역변수로 배열 만들기 

// 6. 블록 생성할 때마다 -> 배열에 넣기 예정 /  마지막 블록 가져오기 
let Blocks = [createGenesisBlock()]
function getLastBlock(){
    return Blocks[Blocks.length-1]
}
getLastBlock()

 

 

오늘 전체 코드 

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

// 1. version 구하기  
function getVersion(){
    const package = fs.readFileSync('../package.json');    
    // console.log(package)
    // console.log(JSON.parse(package))
    console.log('version=',JSON.parse(package).version)
    return JSON.parse(package).version;
    // 위와 같은 코드 
    // const {version} = JSON.parse(fs.readFileSync('../package.json'))
    // return version 
}
// getVersion()

// 2. StampTime 구하기 
function getCurrentTime(){
    console.log('TimeStamp = ', Math.ceil(new Date().getTime()/1000))
    return Math.ceil(new Date().getTime()/1000)
}
// getCurrentTime()


// 3. merkle tree 연습 - > src > merkle.js 파일에 있음 


// 4. Header 만들기 by Class 
class BlockHeader {
    constructor(version, index, previousHash, time, merkleRoot){
        this.version = version
        this.index = index
        this.previousHash = previousHash
        this.time = time
        this.merkleRoot = merkleRoot
    }
}

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

// const blockchain = new Block(new BlockHeader(1,2,3,4,5), ['hello'])
// console.log(blockchain)

// 5. 제네시스 블록 만들기 
function createGenesisBlock(){
    const version = getVersion();
    const time = getCurrentTime();
    const index = 0; 
    const previousHash = '0'.repeat(64) // 제네시스 블록 : 이전hash 없으므로 자리수만 64로 맞춰서 제공 
    // body는 배열 형태 *** 
    const body = ['hello block I am body ']

    // body를 가지고 merkle tree 값을 구성하기 
    const tree = merkle('sha256').sync(body)
    const root = tree.root() || '0'.repeat(64) // 예외처리 

    const header = new BlockHeader(version, index, previousHash, time, root)
    //console.log('header=', header)
    //console.log(new Block(header, body))

    return new Block(header,body)
}

createGenesisBlock()


// 6. 블록 생성할 때마다 -> 배열에 넣기 예정 /  마지막 블록 가져오기 
let Blocks = [createGenesisBlock()]
function getLastBlock(){
    return Blocks[Blocks.length-1]
}
getLastBlock()

 

 

반응형