BlockChain) 연속하는 블록체인 만들기 및 검증하기

YU YU·2021년 9월 2일
1

경일_BlockChain

목록 보기
2/24

1. genesis block 생성 후 다음 블록 만들기


1-1. 전체적인 흐름

genesis block에 만들었던 것처럼 똑같은 알고리즘으로 만들면 된다. 다만, 이전 이전 블록의 header값으로 previousHash값을 만든다는 점만 다를 뿐이다.

index만 이전 블록의 header.index의 값에 1만 추가해주면 된다.

1-2. genesis 블록 만들기까지의 전체적인 코드

const fs = require('fs');//filesystem
const merkle = require('merkle');
const CryptoJs = require('crypto-js');
const SHA256 = require('crypto-js/sha256');
let index=0;
function createGenesisBlock(aaaaa){
    //1.header만들기(5개의 인자를 만들기)
    const version = getVersion()
    const time = parseInt(Date.now()/1000)
    const previousHash =  aaaaa || '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");
    return JSON.parse(package).version;
};

class BlockHeader {
    constructor(version,index,previousHash,timestamp,merkleRoot){//header를 만들 인잣값들
        this.version = version//
        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();

let Blocks= [createGenesisBlock()]

return 부분을 보면 return new Block(header,body)으로 되어 클래스로 되어있음을 알 수 있다. 즉, 각 블록은 class로 되어있음을 알 수 있다.

1-3.다음 블록을 만들기 위한 코드

함수의 기본 원칙이 있다.

하나함수하나기능!

이번 코드는 이걸 적용해서 엄청 복잡한 코드가 되었다. 그러나 논리적으로는 그렇게 어렵지 않다.

1-3-1.addBlock()

function addBlock(data){

    const newBlock = nextBlock(data)
    Blocks.push(newBlock);
}

이 함수는 블록을 추가해주기 위해 만든 코드이다.

역할

  1. 인자값으로 data를 넣어 nextBlock의 함수를 실행해 블록을 생성한다.(인자값인 data를 배열의 형태로 넣어준다.)
  2. 그 결과물을 블록체인인 Blocks 배열에 마지막 블록으로 추가해주는 역할을 한다.

1-3-2.nextBlock()

nextBlock의 역할

전체적인 블록을 만든다.
이전 genesis블록과 다른 점이 있다면
previousHash부분과 index부분이 있다.

function nextBlock(data){
    //header
    const prevBlock = getLastBlock()
    const version = getVersion()
    const index = prevBlock.header.index + 1
    const previousHash = createHash(prevBlock) 
    const time = parseInt(Date.now()/1000);
    const merkleTREE = merkle('sha256').sync(data)
    const merkleRoot = merkleTREE.root() || '0'.repeat(64)
    const header = new BlockHeader(version,index,previousHash,time,merkleRoot)
    return new Block(header,data)
}

getLastBlock()을 통해 지금 Blocks 안의 마지막 항목의 블록을 가져온다. 그래서 index가져온 항목.header.index+1의 값을 넣어준다.

1-3-3.getLastBlock()

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

우리가 만들고있는 블록체인 배열 Block의 현재의 마지막 블록을 반환해주는 역할을 한다.

1-3-4.createHash()

무슨 정보를 가지고 암호화를 하는가?
바로 이전 블록의 header의 value값들을 가져와서 string으로 바꾼 다음에 다 합친 내용을 암호화를 한다.

인잣값으로는 이전 블록의 값을 넣어준다.

function createHash(data){
    //header
    const {
        version,
        index,
        previoushash,
        time,
        merkleRoot
    } = data.header
    const blockString = version+index+previoushash+time+merkleRoot
    const Hash = CryptoJs.SHA256(blockString).toString()
    return Hash
}

1-4. 전체적인 코드

const fs = require('fs');
const merkle = require('merkle');
const CryptoJs = require('crypto-js');
const SHA256 = require('crypto-js/sha256');
let index=0;
function createGenesisBlock(aaaaa){
    //1.header만들기(5개의 인자를 만들기)
    const version = getVersion()
    const time = parseInt(Date.now()/1000)
    const previousHash =  aaaaa || '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");
    return JSON.parse(package).version;
};

class BlockHeader {
    constructor(version,index,previousHash,timestamp,merkleRoot){//header를 만들 인잣값들
        this.version = version//
        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();

let Blocks= [createGenesisBlock()]
console.log(Blocks)

function getBlock(){
    return Blocks
}

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

function nextBlock(data){
    //header
    const prevBlock = getLastBlock()
    const version = getVersion()
    const index = prevBlock.header.index + 1
    const previousHash = createHash(prevBlock) 
    const time = parseInt(Date.now()/1000);
    const merkleTREE = merkle('sha256').sync(data)
    const merkleRoot = merkleTREE.root() || '0'.repeat(64)
    /*이전 해쉬값의
    SHA256(versiton+index+previousHash+timestamp+merkleRoot)*/

    const header = new BlockHeader(version,index,previousHash,time,merkleRoot)
    return new Block(header,data)
}

function createHash(data){
    //header
    const {
        version,
        index,
        previoushash,
        time,
        merkleRoot
    } = data.header
    const blockString = version+index+previoushash+time+merkleRoot
    const Hash = CryptoJs.SHA256(blockString).toString()
    return Hash
}

function addBlock(data){
    //header+body
    //검증을 위한 미리 공간을 확보해 놓은 것
    // 함수 하나에는 함수 하나의 기능만 할 수 있게끔
    const newBlock = nextBlock(data)
    Blocks.push(newBlock);
}

// addBlock();
// addBlock();
addBlock(["hello1"]);
addBlock(["hello2"]);
addBlock(["hello3"]);
console.log(Blocks);

1-5. 전체적인 흐름도





2.검증하기


아무런 검증없이 블럭을 넣어준다?
블럭체인은 절.대.로 그럴 수 없다.
블록을 올리면 모두가 검증하고 맞다고 검증한 수가 과반수가 넘었을 때에 비로소 등록이 되는 알고리즘이기 때문이다.
이제 검증하는 것을 하겠다.

검증할 것

  • 올리려는 현재 블록
    - 각 타입들이 맞게 잘 들어갔는지
    • 암호화가 제대로 되어있는지(merkleroot부분)
    • 인덱스가 이전 블록에서 +1이 되어있는지
  • 이전 블록들
    - 제네시스 블록이 맞는지
    • 배열별로 각 항목이 제대로 되어있는지 확인

2-1.전제적인 코드


const fs = require('fs');//filesystem
const merkle = require('merkle');
const CryptoJs = require('crypto-js');
const SHA256 = require('crypto-js/sha256');
let index=0;
function createGenesisBlock(aaaaa){
    const version = getVersion()
    const time = parseInt(Date.now()/1000)
    const previousHash =  aaaaa || '0'.repeat(64)
    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");
    return JSON.parse(package).version;
};

class BlockHeader {
    constructor(version,index,previousHash,timestamp,merkleRoot){//header를 만들 인잣값들
        this.version = version//
        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();

let Blocks= [createGenesisBlock()]

function getBlock(){
    return Blocks
}

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

function nextBlock(data){
    //header
    const prevBlock = getLastBlock()
    const version = getVersion()
    const index = prevBlock.header.index + 1
    const previousHash = createHash(prevBlock) 
    const time = Math.floor(Date.now()/1000);
    const merkleTREE = merkle('sha256').sync(data)
    const merkleRoot = merkleTREE.root() || '0'.repeat(64)
    const header = new BlockHeader(version,index,previousHash,time,merkleRoot)
    return new Block(header,data)
}

function createHash(data){
    const {
        version,
        index,
        previoushash,
        time,
        merkleRoot
    } = data.header
    const blockString = version+index+previoushash+time+merkleRoot
    const Hash = CryptoJs.SHA256(blockString).toString()
    return Hash
}

function addBlock(data){
    const newBlock = nextBlock(data)
    if(isValidBlock(newBlock,getLastBlock())){
        
        Blocks.push(newBlock);
        return true;
    }
    return false;
}
 

function isValidBlock(currentBlock,previousBlock){
    if(!isValidType(currentBlock)){
        console.log(`invalid block structure ${JSON.stringify(currentBlock)}`)
        return false
    };
    if(previousBlock.header.index+1 !== currentBlock.header.index){
        console.log(`invalid index`)
    }
    if(createHash(previousBlock) !== currentBlock.header.previousHash){
        console.log(`invalid hash value`)
    }
    if(currentBlock.body.length ===0 || (merkle("sha256").sync(currentBlock.body).root() !== currentBlock.header.merkleRoot)){
        console.log(`invalid body`)
    }

    isValidBlock2()
    return true
}

function isValidType(block){
    return(
    typeof(block.header.index)==="number" &&//num
    typeof(block.body)==="object" &&//obj
    typeof(block.header.version)==="string" &&//str
    typeof(block.header.previousHash)==="string" &&//str
    typeof(block.header.timestamp)==="number" &&//num
    typeof(block.header.merkleRoot)==="string"//str
    )
}

addBlock(["hello1"]);
addBlock(["hello2"]);
addBlock(["hello3"]);
console.log(Blocks)


function isValidBlock2(){
    if(JSON.stringify(Blocks[0]) !== JSON.stringify(createGenesisBlock())){
        console.log(`invalid genesisBlock`)
        console.log(JSON.stringify(Blocks[0]))
        console.log(JSON.stringify(createGenesisBlock()))
        console.log('보여줬다.')
        return false
    }
    let tempBlocks = [Blocks[0]]
    for (let i =1; i<Blocks.length; i++){
        if(isValidBlock(Blocks[i],tempBlocks[i-1])){
            tempBlocks.push(Blocks[i])
        }else {
            console.log('이것도 안됨')
            return false
        }
    }
    
    return true;
}

module.exports={
    getBlock,getLastBlock,addBlock,getVersion
}

위의 코드는 오류가 있어서 잠시 보류하도록 하겠다. 그러나 위의 주의사항을 잘 숙지하면 된다.

profile
코딩 재밌어요!

0개의 댓글