본문 바로가기

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

[118일차 복습] 블록체인 새 블록 추가, 연결하고 검증하기

반응형

src > block.js 

 

1. AddBlock 새로운 block 추가하는 함수 

// 새로운 block을 간단히 추가하는 함수 
function addBlock(){
    const newBlock = nextBlock(data);
    
    if(isValieNewBlock(newBlock, getLastBlock())){
        Blocks.push(newBlock);
        return true;
    }
    return false;
}

 

 

2. newBlock 새로운 블록을 생성하는 함수

// 다음 블럭(nextBlock)의 header와 body를 만들어주는 함수 
function nextBlock(data){
    // 이전 block 가져오기 
    const prevBlock = getLastBlock();
    
    // Header 
    const version = getVersion();
    const index = prevBlock.header.index + 1;
    const previousHash = createHash(prevBlock);
    const time = getCurrentTime();
    // Mekle root를 가져오기 위해 tree 만들기 
    const merkleTree = merkle('sha256').sync(data)
    const merkleRoot = merkleTree.root() || '0'.repeat(64);
    const newBlockHeader = new BlockHeader(version, index, previousHash, time, merkleRoot)
    
    // header + data(body) 로 new block return 
    return new Block(newBlockHeader, data)
}

 

3. createHash 함수 생성 - 이전 블록의 모든 header string값을 받아와 새 hash 암호화 

function createHash(block){
    // 이전 블록을 인자로 받아와 해당 블록 header의 요소들 가져오기 
    const {version, index, previousHash, time, merkleRoot} = block.header;
    // 모든 string을 + 더하기 
    const blockString = version + index+previousHash+time+merkleRoot;
    const Hash = CryptoJs.SHA256(blockString).toString();
    return Hash;

}

 

 

4. 새로 만든 블록 검증 -> 후에 add 

뭔가 이어지는 함수가 많아서 잘 생각하기 !! 

addBlock : 새로운 블록을 추가하는 함수 

isValidNewBlock : 새로운 블록과, 이전 블록을 세트로 검증하는 함수 

isValieType : 새로운 블록의 Type 을 검증하는 함수 

// 새로운 block을 간단히 추가하는 함수 
function addBlock(data){
    const newBlock = nextBlock(data);
    
    if(isValidNewBlock(newBlock, getLastBlock())){
        Blocks.push(newBlock);
        return true;
    }
    return false;
}

// 새로운 블록 검증 
function isValidNewBlock(currentBlock, previousBlock){
    // 1. 새로운 블록의 type 검증
    isValidType(currentBlock)
    return true;

    
}

function isValidType(block){
    console.log(block)
}

addBlock(['asdf']);

 

1) type검증 

// 새로운 블록 검증 
function isValidNewBlock(currentBlock, previousBlock){
    // 1. 새로운 블록의 type 검증
    if(!isValidType(currentBlock)){
        // "" or '' or `` string 값으로 만들 때 객체는[Object Object]로 나와서 JSON.stringify 붙여주기 
        console.log(`inValidType is found in currentBlock = ${currentBlock}`);
        console.log(`inValidType is found in currentBlock = ${JSON.stringify(currentBlock)}`);
        return false;
    }
    return true;
}

// 새로운 블록 type 검증 
function isValidType(block){
    return (
    typeof(block.header.version)=="string" &&
    typeof(block.header.index)=="numbr" &&
    typeof(block.header.previousHash)=="string" &&
    typeof(block.header.time)=="number" &&
    typeof(block.header.merkleRoot)=="string" &&
    typeof(block.body)=="object" )
}

 

2) index 검증 

// 새로운 블록 검증 
function isValidNewBlock(currentBlock, previousBlock){
    // 1. 새로운 블록의 type 검증
    if(!isValidType(currentBlock)){
        // "" or '' or `` string 값으로 만들 때 객체는[Object Object]로 나와서 JSON.stringify 붙여주기 
        console.log(`inValidType is found in currentBlock = ${currentBlock}`);
        console.log(`inValidType is found in currentBlock = ${JSON.stringify(currentBlock)}`);
        return false;
    }

    // 2. index 검증
    if(previousBlock.header.index+1 !== currentBlock.header.index){
        console.log(`invalid index found`)
        return false;
    }
    return true;
}

 

 

3) previous Hash 췤

    // 3. previousHash check
    if(currentBlock.header.previousHash !== createHash(previousHash)){
        console.log(`invalid previousHash found`)
        return false;
    }

 

 

4) root 쳌 

merkle 목적 : data 위조 방지  (+ 특정 노드 효율적으로 찾기) 

    // 4. merkleRoot check (root가 정확한지) 
    if(currentBlock.header.merkleRoot !== merkle('sha256').sync(currentBlock.body).root()){
        console.log(`invalid body found`)
        return false;
    }

 

 

5) body 쳌

    // 5. body check (body에 내용이 반드시 있어야함 !)
    if(currentBlock.body.length===0){
        console.log(`invalid body found, it's empty`);
        return false;
    }

 

isValieNewBlock 함수 (새로 만들어진 블록 검증하는) 의 전체 코드 

// 새로운 블록 검증 
function isValidNewBlock(currentBlock, previousBlock){
    // 1. 새로운 블록의 type 검증
    if(!isValidType(currentBlock)){
        // "" or '' or `` string 값으로 만들 때 객체는[Object Object]로 나와서 JSON.stringify 붙여주기 
        console.log(`inValidType is found in currentBlock = ${currentBlock}`);
        console.log(`inValidType is found in currentBlock = ${JSON.stringify(currentBlock)}`);
        return false;
    }

    // 2. index 검증
    if(previousBlock.header.index+1 !== currentBlock.header.index){
        console.log(`invalid index found`)
        return false;
    }

    // 3. previousHash check
    if(currentBlock.header.previousHash !== createHash(previousBlock)){
        console.log(`invalid previousHash found`)
        return false;
    }

    // 4. merkleRoot check (root가 정확한지) 
    if(currentBlock.header.merkleRoot !== merkle('sha256').sync(currentBlock.body).root()){
        console.log(`invalid body found`)
        return false;
    }

    // 5. body check (body에 내용이 반드시 있어야함 !)
    if(currentBlock.body.length===0){
        console.log(`invalid body found, it's empty`);
        return false;
    }    
    return true;
}

 

 

 

6. Blocks 안 전체 배열 검증 

1) 제네시스 블록 유효한지, 데이터가 바뀐 적이 없는지 확인

2) Blocks 배열 안의 모든 요소들을 짝으로 검사 (10개 요소라면 -> 9번 검사) 

 

 

1) 제네시스 블록 검증

JavaScript 특징 상, {} !== {} 이다. 제네시스 블록은 객체이므로 JSON.stringiyi() 로 string으로 만들어 비교를 진행해야 한다. 

// 제네시스 블록 검증
function isValidBlock(Blocks){
    if(JSON.stringify(Blocks[0]) !== JSON.stringify(createGenesisBlock())){
        console.log(`Invalie Genesis Block found`);
        return false;
    }
}

 

 

 

2) Blocks 배열 요소 검증 

// 6. 전체 검증 (제네시스블록 & 블록을 담은 Blocks 배열 전체)
function isValidBlock(Blocks){
    // GENESIS BLOCK 검증
    if(JSON.stringify(Blocks[0]) !== JSON.stringify(createGenesisBlock())){
        console.log(`Invalie Genesis Block found`);
        return false;
    }

    // BLOCKS ARRAY 검증 
    // tempBlocks 변수에 제네시스 블록 배열로 담기 
    let tempBlocks = [Blocks[0]];
    for(let i=1; i<Blocks.length; i++){
        if(isValidNewBlock(Blocks[i], tempBlocks[i-1])){
            tempBlocks.push(Blocks[i])
        }else{
            console.log(`invalid Block found, located at ${i} in Blocks Array`)
            return false;
        }
    }
    return true;
}

 

 

 

3) 위의 isValidBlock() 함수를 addBlcok() 함수에 추가해주기 

// 얘는 단순히 Push만 할 것 ! 인자값에 배열을 넣어주기 (data)
function addBlock(data) {
    // new header 만들어서 => new block(header, body) 
    const newBlock = nextBlock(data) // return 다음 블록에 대한 객체 
    // newBlock과 마지막 
    if(isValidNewBlock(newBlock, getLastBlock()) && isValidBlocks(Blocks)){
        Blocks.push(newBlock);
        return newBlock;  //함수가 여기서 끝나버리게
    }

    return false;
}

 

 

 

 

 

전체 코드 

block.js

const fs = require('fs')
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 
}

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

// 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'])

// 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)
}

// 6. 블록 생성할 때마다 -> 배열에 넣기 예정 /  마지막 블록 가져오기 
let Blocks = [createGenesisBlock()]
console.log('Blocks=', Blocks)

function getLastBlock(){
    return Blocks[Blocks.length-1]
}


//---------------------------2번째 수업 --------------------------------


// 새로운 block을 간단히 추가하는 함수 
function addBlock(data){
    const newBlock = nextBlock(data);
    
    if(isValidNewBlock(newBlock, getLastBlock()) && isValidBlocks(Blocks)){
        Blocks.push(newBlock);
        return true;
    }
    return false;
}

// 새로운 블록 검증 
function isValidNewBlock(currentBlock, previousBlock){
    // 1. 새로운 블록의 type 검증
    if(!isValidType(currentBlock)){
        // "" or '' or `` string 값으로 만들 때 객체는[Object Object]로 나와서 JSON.stringify 붙여주기 
        console.log(`inValidType is found in currentBlock = ${currentBlock}`);
        console.log(`inValidType is found in currentBlock = ${JSON.stringify(currentBlock)}`);
        return false;
    }

    // 2. index 검증
    if(previousBlock.header.index+1 !== currentBlock.header.index){
        console.log(`invalid index found`)
        return false;
    }

    // 3. previousHash check
    if(currentBlock.header.previousHash !== createHash(previousBlock)){
        console.log(`invalid previousHash found`)
        return false;
    }

    // 4. merkleRoot check (root가 정확한지) 
    if(currentBlock.header.merkleRoot !== merkle('sha256').sync(currentBlock.body).root()){
        console.log(`invalid body found`)
        return false;
    }

    // 5. body check (body에 내용이 반드시 있어야함 !)
    if(currentBlock.body.length===0){
        console.log(`invalid body found, it's empty`);
        return false;
    }    
    return true;
}

// 새로운 블록 type 검증 
function isValidType(block){
    return (
    typeof(block.header.version)=="string" &&
    typeof(block.header.index)=="number" &&
    typeof(block.header.previousHash)=="string" &&
    typeof(block.header.time)=="number" &&
    typeof(block.header.merkleRoot)=="string" &&
    typeof(block.body)=="object" )
}

// 6. 전체 검증 (제네시스블록 & 블록을 담은 Blocks 배열 전체)
function isValidBlocks(Blocks){ 
    // GENESIS BLOCK 검증 
    if(JSON.stringify(Blocks[0]) !== JSON.stringify(createGenesisBlock())){
        console.log(`Invalie Genesis Block found`);
        return false;
    }
 
    // BLOCKS ARRAY 검증 
    // tempBlocks 변수에 제네시스 블록 배열로 담기 
    let tempBlocks = [Blocks[0]];
    for(let i=1; i<Blocks.length; i++){
        if(isValidNewBlock(Blocks[i], tempBlocks[i-1])){
            tempBlocks.push(Blocks[i])
        }else{
            console.log(`invalid Block found, located at ${i} in Blocks Array`)
            return false;
        }
    }
    return true;
}


addBlock(['asdf']);


// 다음 블럭(nextBlock)의 header와 body를 만들어주는 함수 
function nextBlock(data){
    // 이전 block 가져오기 
    const prevBlock = getLastBlock();
    
    // Header 
    const version = getVersion();
    const index = prevBlock.header.index + 1;
    const previousHash = createHash(prevBlock);
    const time = getCurrentTime();
    // Mekle root를 가져오기 위해 tree 만들기 
    const merkleTree = merkle('sha256').sync(data)
    const merkleRoot = merkleTree.root() || '0'.repeat(64);
    const newBlockHeader = new BlockHeader(version, index, previousHash, time, merkleRoot)
    
    // header + data(body) 로 new block return 
    return new Block(newBlockHeader, data)
}

function createHash(block){
    // 이전 블록을 인자로 받아와 해당 블록 header의 요소들 가져오기 
    const {version, index, previousHash, time, merkleRoot} = block.header;
    // 모든 string을 + 더하기 
    const blockString = version + index+previousHash+time+merkleRoot;
    const Hash = CryptoJs.SHA256(blockString).toString();
    return Hash;

}

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))

 

반응형