Typescript로 블록체인 만들기
VSCode TypeScript 준비
- tsconfig.json 생성
(파일을 쓸 때에는 주석의 한글 설명을 삭제해야 정상 실행됩니다.)
{
"compilerOptions": { //컴파일 옵션 설정
"module": "commonjs",
"target": "es2015",
"sourceMap": true
},
"include": ["index.ts"], //컴파일과정에서 포함할 파일
"exclude": ["node_modules"], //컴파일과정에서 제외
}
- index.ts 생성
console.log("test");
-
터미널 실행
yarn init 실행 시 package.json 파일이 생성됩니다.yarn init
4. 생성된 package.json 파일 수정 TypeScript tsc는 컴파일 명령어로 TypeScript 파일(.ts)을 자바스크립트 파일로 트랜스파일링 합니다.
실행 전에 반드시 실행해야하는 명령어이므로 자동으로 실행되기 위해 package.json에 정의해줍니다.
{
"name": "typescript_blockchain",
"version": "1.0.0",
"main": "index.js",
"repository": "https://github.com/ckdqja135/Typescript-restful-starter/tree/master/typechain",
"author": "cbsong <ckdqja13580@gmail.com>",
"license": "MIT",
"scripts": {
"prestart": "tsc", //타입스크립트 실행 전에 tsc 명령어로 js로 컴파일 시켜줍니다.
"start": "node index.js" //js화 된 파일을 실행.
}
}
-
타입스크립트 실행
yarn start
--------------
1. 기본 함수 예제
``` typescript
const name = "changbeom",
age = 26,
gender = "Male";
const sayHi = (name, age, gender?) => { //물음표를 삽입하면 파라미터 정의가 되지 않아도 실행
console.log(`Hello ${name} you are ${age}, you are a ${gender}`);
}
sayHi(name, age); //gender 파라미터를 빼도 정상실행됨
export {};
const name = "changbeom",
age = 26,
gender = "Male";
const sayHi = (name, age, gender) => {
console.log(`Hello ${name} you are ${age}, you are a ${gender}`);
}
sayHi(name, age, gender); //파라미터값이 하나라도 빠지면 에러남
export {};
- Type in Typescript
파라미터 형식을 지정하지 않을 때 마우스를 오버하면 아래 그림과 같이 any=object, array, boolean 상관없이 값을 전달할 수 있습니다.
함수를 봤을 때 어떤 형식의 파라미터 값이 필요하고 어떤 형식으로 리턴받는지 예상가능한 코드가 유지보수 측면으로도 좋으므로 typed 하게 바꿔줍니다.
const sayHi = (name:string, age:number, gender:string):string => { return(`Hello ${name} you are ${age}, you are a ${gender}`); } var sayHiPrint = sayHi("dongwon", 34, "Male"); console.log(sayHiPrint); export {};
yarn start 이제 그만!
- tsc-watch 라이브러리 설치
ts파일을 수정할 때마다 yarn start를 실행하기엔 번거로운 부분이 있으므로 tsc-watch 라이브러리를 설치하여 ts파일이 수정되는 즉시 컴파일 업데이트 되도록 합니다.
yarn add tsc-watch --dev
yarn add typescript
- 설정 파일 옵션 변경
1) pakage.json : –onSuccess 옵션으로 ts compile(tsc)이 성공했을 시에만 동작하게 설정.
"scripts": {
"start": "tsc-watch --onSuccess \" node dist/index.js\" "
},
2) tsconfig.json
{
"compilerOptions": { //컴파일 옵션 설정
"module": "commonjs",
"target": "es2015",
"sourceMap": true,
"outDir": "dist"
},
"include": ["src/**/*"], //컴파일과정에서 포함할 파일
"exclude": ["node_modules"], //컴파일과정에서 제외
}
- 폴더, 파일 정리
노마드코더 영상으로 진행을 하다보면 root 경로에 ts 파일, 설정파일, 컴파일된 파일등 모두 모여있게 되는데 이를 정리하고나서,
정리된 폴더경로로 tsc-watch를 위한 설정파일 수정을 진행합니다.
/dist 폴더 생성 - 컴파일된 파일
/src 폴더 생성 - ts 파일 (index.ts 파일 이동)
- index.js, index.js.map 은 dist 폴더에 생성되게 되므로 기존에 생성된 파일은 삭제해줍니다.
-
실행
yarn start
실행해주면 index.ts를 수정 할 때마다 내용이 업데이트 됩니다.
# Interface in Typescript
파라미터 값들을 Object로 넘기고 싶을 때는 Interface 를 사용합니다.
```typescript
interface Human {
name : string;
age : number;
gender : string;
}
const person = {
name : "changbeom",
age : 26,
gender : "male"
}
const sayHi = (person:Human):string => {
return(`Hello ${person.name} you are ${person.age}, you are a ${person.gender}`);
}
var sayHiPrint = sayHi(person);
console.log(sayHiPrint);
export {};
Classes On Typescript part One
Interface 는 js로 컴파일 되지 않지만 가끔 인터페이스를 js에 넣고 싶을 때 class를 사용합니다.
js에서는 class의 속성에 대해 묘사하지 않지만 ts에서 class는 어떤 타입이어야하는지, 어떤 권한을 가져야하는지 상세하게 알려줘야 합니다.
class Human {
public name: string;
public age: number;
public gender:string;
constructor (name:string, age:number, gender:string){
this.name = name;
this.age = age;
this.gender=gender;
}
}
const changbeom = new Human("changbeom", 26, "Male");
const sayHi = (person:Human):string => {
return(`Hello ${person.name} you are ${person.age}, you are a ${person.gender}`);
}
var sayHiPrint = sayHi(dongwon);
console.log(sayHiPrint);
export {};
interface나 class는 상황에 따라 선택해서 쓰면 되는데, ts에서는 interface를 사용하기에 더 적합하고 react, express 등에서는 class를 사용해야 합니다.
클래스 내의 권한은 public, privite, protected 중 사용 용도에 따라 데이터 권한을 지정해줌으로써 좀 더 안전한 코딩이 가능해집니다.
Blockchain Creating a Block
ts에서는 let과 const를 주로 사용합니다.
단순형의 경우 값의 변경이 있는 경우 let을 사용하고, 상수형으로 사용하는 경우 const 사용합니다.
블럭구조 만들기
class Block {
public index:number;
public hash:string;
public previousHash:string;
public data:string;
public timestamp:number;
constructor(
index:number,
hash:string,
previousHash:string,
data:string,
timestamp:number
) {
this.index = index;
this.hash = hash;
this.previousHash = previousHash;
this.data = data;
this.timestamp = timestamp;
}
}
const genesisBlock = new Block(0, "123123123", "", "Hello", 123456);
let blockchain: [Block] = [genesisBlock];
console.log(blockchain);
export{};
위 코드를 실행하면 아래처럼 출력 됩니다.
typescript (ts)가 blockchain 변수에 Block만 담기는 지 검사 해줍니다.
Creating a Block part Two
-
crypto-js 라이브러리
yarn add crypto-js
설치한 후 import * as Crypto from 'crypto-js'; 명령어로 import해줍니다.
* HASH함수를 이용한 블록 생성
```typescript
import * as CryptoJS from 'crypto-js';
class Block {
public index:number;
public hash:string;
public previousHash:string;
public data:string;
public timestamp:number;
static calculateBlockHash = (
index:number,
previousHash:string,
timestamp:number,
data:string):string => CryptoJS.SHA256(index + previousHash + timestamp + data).toString();
constructor(
index:number,
hash:string,
previousHash:string,
data:string,
timestamp:number
){
this.index = index;
this.hash = hash;
this.previousHash = previousHash;
this.data = data;
this.timestamp = timestamp;
}
}
const genesisBlock = new Block(0, "123123123", "", "Hello", 123456);
let blockchain: [Block] = [genesisBlock];
//나중에 사용 할 함수 정의
const getBlockchain = () : Block[] => blockchain;
const getLatestBlock = () : Block => blockchain[blockchain.length -1];
const getNewTimeStamp = () : number => Math.round(new Date().getTime() / 1000);
console.log(blockchain);
export{};
calculateBlockHash 라는 함수를 만들어서 해쉬값을 구하는데 class 내에서 일반적인 메서드 선언 문법을 사용하면 해당 메서드는 반드시 블록을 생성했을 때에만 사용할 수 있으므로 함수를 static 으로 선언해줍니다.
Creating a Block part Three
import * as CryptoJS from 'crypto-js';
class Block {
public index: number;
public hash: string;
public previousHash: string;
public data: string;
public timestamp: number;
static calculateBlockHash = (
index: number,
previousHash: string,
data: string,
timestamp: number
): string => CryptoJS.SHA256(index + previousHash + timestamp + data).toString();
constructor(index: number, hash: string, previousHash: string, data: string, timestamp: number) {
this.index = index;
this.hash = hash;
this.previousHash = previousHash;
this.data = data;
this.timestamp = timestamp;
}
}
const genesisBlock: Block = new Block(0, "123123123", "", "hello", 123456);
let blockchain: Block[] = [genesisBlock];
const getBlockchain = () : Block[] => blockchain;
const getLatestBlock = () : Block => blockchain[blockchain.length - 1];
const getNewTimesStamp = () : number => Math.round(new Date().getTime() / 1000);
const createNewBlock = (data: string) : Block => {
const previousBlock: Block = getLatestBlock();
const newIndex: number = previousBlock.index + 1;
const nextTimestamp: number = getNewTimesStamp();
const nextHash: string = Block.calculateBlockHash(newIndex, previousBlock.hash, data, nextTimestamp);
const newBlock: Block = new Block(newIndex, nextHash, previousBlock.hash, data, nextTimestamp); return newBlock;
}
console.log(createNewBlock("hello"));
console.log(createNewBlock("bye bye"));
export {};
위 코드를 실행하면 아래와 같이 출력이 되는데 아직 블록체인에 push 하지 않았기 때문에 Index가 둘 다 1로 출력됩니다.
Validating Block Structure
블록이 isValid라는 구조를 가지는지 확인합니다
Ts가 많이 체크를 해주지만 해쉬가 정확한지 확인필요하며 블록의 구조가 유효한지 검사합니다.
import * as CryptoJS from 'crypto-js';
class Block {
static calculateBlockHash = (
index: number,
previousHash: string,
data: string,
timestamp: number
): string => CryptoJS.SHA256(index + previousHash + timestamp + data).toString();
static validateStructure = (aBlock: Block): boolean =>
typeof aBlock.index === "number" &&
typeof aBlock.hash === "string" &&
typeof aBlock.previousHash === "string" &&
typeof aBlock.data ==="string" &&
typeof aBlock.timestamp === "number";
public index: number;
public hash: string;
public previousHash: string;
public data: string;
public timestamp: number;
constructor(index: number, hash: string, previousHash: string, data: string, timestamp: number) {
this.index = index;
this.hash = hash;
this.previousHash = previousHash;
this.data = data; this.timestamp = timestamp;
}
}
const genesisBlock: Block = new Block(0, "123123123", "", "hello", 123456);
let blockchain: Block[] = [genesisBlock];
const getBlockchain = () : Block[] => blockchain;
const getLatestBlock = () : Block => blockchain[blockchain.length - 1];
const getNewTimesStamp = () : number => Math.round(new Date().getTime() / 1000);
const createNewBlock = (data: string) : Block => {
const previousBlock: Block = getLatestBlock();
const newIndex: number = previousBlock.index + 1;
const nextTimestamp: number = getNewTimesStamp();
const nextHash: string = Block.calculateBlockHash(newIndex, previousBlock.hash, data, nextTimestamp);
const newBlock: Block = new Block(newIndex, nextHash, previousBlock.hash, data, nextTimestamp);
return newBlock;
}
const getHashforBlock = (aBlock: Block) : string => Block.calculateBlockHash(aBlock.index, aBlock.previousHash, aBlock.data, aBlock.timestamp);
const isBlockValid = (candidateBlock: Block, previousBlock: Block): boolean => {
if (!Block.validateStructure(candidateBlock)) { //candidateBlock 이 유효하지 않으면
False return false;
} else if (previousBlock.index + 1 !== candidateBlock.index) { //previousBlock의 인덱스+1랑 candidateBlock블록의 인덱스가 다르면 False
return false;
} else if (previousBlock.hash !== candidateBlock.previousHash) { //previousBlock의 해쉬와 candidateBlock블록의 previousHash가 다르면 False
return false;
} else if (getHashforBlock(candidateBlock) !== candidateBlock.hash) { //따로 해쉬를 계산해서 블록의 해쉬가 유효한지 체크
return false;
} else {
return true;
}
}
export {};
Validating Block Structure Part Two
-
블록을 블록체인에 추가
import * as CryptoJS from 'crypto-js'; class Block { static calculateBlockHash = ( index: number, previousHash: string, data: string, timestamp: number ): string => CryptoJS.SHA256(index + previousHash + timestamp + data).toString(); static validateStructure = (aBlock: Block): boolean => typeof aBlock.index === "number" && typeof aBlock.hash === "string" && typeof aBlock.previousHash === "string" && typeof aBlock.data ==="string" && typeof aBlock.timestamp === "number"; public index: number; public hash: string; public previousHash: string; public data: string; public timestamp: number; constructor(index: number, hash: string, previousHash: string, data: string, timestamp: number) { this.index = index; this.hash = hash; this.previousHash = previousHash; this.data = data; this.timestamp = timestamp; } } const genesisBlock: Block = new Block(0, "123123123", "", "hello", 123456); let blockchain: Block[] = [genesisBlock]; const getBlockchain = () : Block[] => blockchain; const getLatestBlock = () : Block => blockchain[blockchain.length - 1]; const getNewTimesStamp = () : number => Math.round(new Date().getTime() / 1000); const createNewBlock = (data: string) : Block => { const previousBlock: Block = getLatestBlock(); const newIndex: number = previousBlock.index + 1; const nextTimestamp: number = getNewTimesStamp(); const nextHash: string = Block.calculateBlockHash(newIndex, previousBlock.hash, data, nextTimestamp); const newBlock: Block = new Block(newIndex, nextHash, previousBlock.hash, data, nextTimestamp); return newBlock; } const getHashforBlock = (aBlock: Block) : string => Block.calculateBlockHash(aBlock.index, aBlock.previousHash, aBlock.data, aBlock.timestamp); const isBlockValid = (candidateBlock: Block, previousBlock: Block): boolean => { if (!Block.validateStructure(candidateBlock)) { //candidateBlock 이 유효하지 않으면 False return false; } else if (previousBlock.index + 1 !== candidateBlock.index) { //previousBlock의 인덱스+1랑 candidateBlock블록의 인덱스가 다르면 False return false; } else if (previousBlock.hash !== candidateBlock.previousHash) { //previousBlock의 해쉬와 candidateBlock블록의 previousHash가 다르면 False return false; } else if (getHashforBlock(candidateBlock) !== candidateBlock.hash) { //따로 해쉬를 계산해서 블록의 해쉬가 유효한지 체크 return false; } else { return true; } } const addBlock = (candidateBlock: Block) : void => { if(isBlockValid(candidateBlock, getLatestBlock())) { blockchain.push(candidateBlock); } } export {};
CreateNewBlock 에서 생성한 블록을 addBlock를 사용하여 유효성을 체크한 후 블록체인에 등록해줍니다.
실행
createNewBlock("second block");
createNewBlock("thrid block");
createNewBlock("fourth block");
console.log(blockchain);
위 코드로 실행을 해보면 블록 세개가 추가 된 것이 확인되며, 블록체인 구조인 이전의 해쉬값과 생성된 해쉬값들이 잘 들어가있어 정상적으로 수행되었다는 것을 알 수 있습니다.
code
'이것저것 > 블록체인' 카테고리의 다른 글
[바미] 블록체인 한번에 이해하기 (0) | 2021.03.02 |
---|---|
[바미] 블록체인 용어사전 (ㄱ~Z) (0) | 2021.03.02 |