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;
}
isValidNewBlock 함수 (새로 만들어진 블록 검증하는) 의 전체 코드
// 새로운 블록 검증
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]
}
//----------------------------------------------------------------------------------------
// 새로운 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;
}
'BlockChain' 카테고리의 다른 글
블록체인 지갑생성(공개키, 비밀키) (0) | 2022.01.01 |
---|---|
블록체인 네트워크 웹소켓 WebSocket http ws (0) | 2021.12.31 |
블록체인 제네시스 블록 만들기 by JavaScript (0) | 2021.12.30 |
블록체인 블록 만들어보기 (0) | 2021.12.29 |
blockchain 맛 보기 (0) | 2021.12.27 |