블록체인
블록체인 내용 구현
다른것도 중요하지만 전체적인 흐름이 중요하다 여겨진다. 구현의 흐름이 블록체인 검증의 흐름이나 체인에 추가하는 흐름하고는 다를것이다.
우선 block이 무엇으로 구성되는지 미리 설계를 하고 들어간다. Iblock 이라는 이름으로 interface IblockHeader를 상속을 받아 구현한다.
그내용에는 버전,높이,타임스탬프,이전해시,머클루트,nonce(논스),난이도,그리고 data 로 구성된다. 이형태는 블록을 생성하는 경우 사용된다. 약간 다른언어에서 사용하는 구조체처럼 보이기도?
merkleRoot
머클 루트에대한 설명이다. 이 사진을 보자마자 떠오른것이 있다면 레인보우 테이블이다. md5 암호화를 통해서 여러번 거쳐서 최종적인 암호 책을 만들어서 다시 해당 코드로 복호화하면서 그안에있는 내용을 찾는식인데.비슷해보였다.
GENESISBLOCK
export const GENESIS: IBlock = {
version: "1.0.0",
height: 0,
timestamp: new Date().getTime(),
hash: "0".repeat(64),
previousHash: "0".repeat(64),
merkleRoot: "0".repeat(64),
// 블록을 채굴할때 이전 블록 난이도로 머이닝을한다.
// 블록의 생성 주기를 검사를 해서 생성주기가 빠르면 블록의 난이도를 상승시키고
// 블록의 생성주기가 느리면 블록의 난이도를 하락시킨다.
difficulty: 0,
nonce: 0,
data: [
"'The Times 03/Jan/2009 Chancellor on brink of second bailout for banks'",
],
};
최초의 블록 형태이다 이전 블록이 없기떄문에 previoushash나 markleroot difiicut 등등 부분이 대부분 0으로 초기화되어있다.
최초의 비트코인 블록에는 위코드의 data내용 처럼 저 내용이 담겨있다.
CryptoModule


실제코드
class CryptoModule {
static hashtoBinary(hash: string): string {
let binary: string = "";
// 16진수를 -> 2진수로 바꾸는식
// 해시 문자열을 2글자식 가지고와서 반복
console.log("처음binary", hash);
for (let i = 0; i < hash.length; i += 2) {
// 반복문에서i를 2씩증가
const hexByte = hash.substr(i, 2);
// 16진수의 바이트를 10진수로 변환하고
const dec = parseInt(hexByte, 16);
// 10진수를 2진 문자열로 변환 8자리로 패딩
const binaryByte = dec.toString(2).padStart(8, "0");
console.log("binaryByte", binaryByte);
binary += binaryByte;
console.log("binary", binary);
}
return binary;
}
}
export default CryptoModule;
그리고 이코드는 Block 클래스 내에 findBlock 전역 메서드로선언된 block을 채굴하는 방식하고 관련이있다.
그러려면 이코드를 봐야한다.
Block의 생성
static generateBlock(_previousBlock: Block, _data: string[]): Block {
const generateBlock = new Block(_previousBlock, _data);
// 마이닝을 통해서 블록의 생성권한을 받은 블록을만들고
const newBlock = Block.findBlock(generateBlock);
return newBlock;
}
매개변수로 블록형태의 이전 블록, 문자배열의 데이터를 받고 블록을 반환해준다.
일단 generateBlock함수로 들어오면 변수내에 generateblock이 생성은 된다. new block으로 다만
block.findblock(generateblock) 에서 문제없어야 생성된 블록을 반환해준다.
그게 아래코드이다.
// 마이닝 작업 코드
// 블록의 채굴
// 연산을 통해서 난이도의 값에 다른 정답을 찾는 동작
// findBlock = 동작의 이름은 마이닝 블록을 채굴하는동작
// POW : 작업 증명 블록의 난이도에 추족하는 값을 구하기위해서 연산작업을 계속 진행해서 조건에 충족하는 값을 구하면
// 보상으로 블록의 생성권한을 얻는다.
static findBlock(generateBlock: Block) {
let hash: string;
// nonce 변수는 블록 채굴하는데 연산을 몇번 진행했는지 값을 여기에 담을 것임
let nonce: number = 0;
while (true) {
generateBlock.nonce = nonce;
// nonce 값을 증가시켜서 hash 값을 계속 바꿔서 계산을 진행한다.
nonce++;
// 블록해시 구하는구문 추가
hash = Block.createBlockHash(generateBlock);
// 16진수를 - > 2진수로 변환 해야하는데
// 16진수를 2진수로 변환해서 0 의 갯수가 난이도의 갯우에 충족하는지 체크를해서
// 맞추면 블록 채굴의 권한을 받고
// 블록을 생성할수있다.
// 충족되었는지 확인하려면 binary 2진값이 바뀌는이유는.
const binary: string = CryptoModule.hashtoBinary(hash);
console.log("binary", binary);
// 연산의 값이 난이도에 충족했는지 체클할 변수
// startsWith: 무자열의 시작이 매개변수로 전달도니 문자열로 시작하는지 체크
// "000" = 이 문자열로 시작하는지 결과가 true false 반환되고
const result: boolean = binary.startsWith(
"0".repeat(generateBlock.difficulty)
);
console.log("result", result);
// 조건에충족했다면 블록을 채굴할수있는 권한을 얻었고 조건의 충족해서 나온값을 반환
if (result) {
// 연산을 통해 완성된 hash값과
generateBlock.hash = hash;
// 완성된 블록을 내보내주자
return generateBlock;
}
}
}
쉽게 보면이렇다 .generateblock의 nonce값이 바뀌면 모든 hash값이 싹다 바뀐다. diffculty는 난이도로 연산의 과정에서 나온binary가 처음 앞숫자가 0000... 이몇개 붙냐를 나타내는것인데. 그 코드를 단순하게 보여주는게 binary.startwith{"0",repeat(generateBlock.diff)} 난이도가 3이기에 처음 0 이 3개나올떄까지 nonce를 증가시키고 hash값을 바구면서 진행하는것이다 . 마침내 통과한다면 받아온 generateblock.hash에 hash를 넘겨주고 generateblock을 반환해준다.
Block의 형태
export class Block extends BlockHeader implements IBlock {
hash: string;
merkleRoot: string;
nonce: number;
difficulty: number;
data: string[];
constructor(_previousBlock: Block, _data: string[]) {
// 부모 클래스 생성자 호출 super
super(_previousBlock);
this.merkleRoot = Block.getMerkleRoot(_data);
// 블록 본인의 데이터를 hash화한게 블록의 해시값
this.hash = Block.createBlockHash(this);
// 블록 채굴은 뒤에 추가
// 지금은 0으로
this.nonce = 0;
// 지금은 난이도 3
this.difficulty = 3;
this.data = _data;
}
super(_previousBlock) 확장받은 컨스트럭처를 초기화해주는것은 관행에 가깝다.
class BlockHeader implements IBlockHeader {
version: string;
height: number;
timestamp: number;
previousHash: string;
constructor(_previousBlock: IBlock) {
// 블록을 생성할때 이전 블록의 정보가 필요하다 .
// 이전블록의 해시나 높이나
this.version = BlockHeader.getVersion();
this.timestamp = BlockHeader.getTimestamp();
this.height = _previousBlock.height + 1;
this.previousHash = _previousBlock.hash;
}
static getVersion() {
return "1.0.0";
}
static getTimestamp() {
return new Date().getTime();
}
}
부모 blockheader에서 해당하는 데이터를 가져오면 block안에 내용이 모두 초기화되어 필요한 내용은 다 들어온다.
merkleRoot를 구하는 함수?
static getMerkleRoot<T>(_data: T[]): string {
const merkleTree = merkle("sha256").sync(_data);
return merkleTree.root();
}
블록의 해시를구하는 함수?
그냥 다 떄려넣고 암호화하면 그만이다 . 양자컴 발견되기전에는 가능
static createBlockHash(_block: Block): string {
const {
version,
height,
timestamp,
merkleRoot,
previousHash,
difficulty,
nonce,
} = _block;
const value: string = `${version}${height}${timestamp}${merkleRoot}${previousHash}${difficulty}${nonce}`;
return SHA256(value).toString();
}
검증 하는 방법 무엇으로 확인할것인가?
static isValidNewBlock(
_newBlock: Block,
_previousBlock: Block
): Failable<Block, string> {
// 블록의 유효성 검사를 하는데
// 블록의 높이가 정상적인지?
if (_previousBlock.height + 1 !== _newBlock.height) {
return { isError: true, value: "이전 높이 오류" };
}
// 이전 블록의 해시 값이 새로운 블록의 이전 해시값과 동일한지?
if (_previousBlock.hash !== _newBlock.previousHash) {
return { isError: true, value: "이전 블록 해시 오류" };
// 생성된 블록의 정보를 가지고 다시 해시해서 블록의 값이 변조되었는지 정상적인 블록인지 확인
}
if (Block.createBlockHash(_newBlock) !== _newBlock.hash) {
return { isError: true, value: "블록 해시 오류" };
}
// 블록이 유효성 검사를 통과했다 정상적인 블록이다.
return { isError: false, value: _newBlock };
}
중제목 1



'backend > blockchain' 카테고리의 다른 글
| 20231005 블록체인 baseball (1) | 2023.10.05 |
|---|---|
| 20231005 블록체인 (0) | 2023.10.05 |
| 20230912 블록 체인 지갑 (0) | 2023.09.12 |
| 블록체인 (0) | 2023.09.05 |