일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- JWT 하드코딩
- 비동기파일업로드
- JWT 만들어보기
- cookie-parser 만들어보기
- FormData()
- javascript기초
- ws 라이브러리
- node.js path
- express실행
- 라우터미들웨어 분리
- buffer.from
- 라우터 분리
- 아이디 중복체크기능
- 세션으로 로그인 구현
- nodejs파일업로드
- useContext
- express session
- useEffect clean up
- Uncaught Error: could not find react-redux context value; please ensure the component is wrapped in a <Provider>
- 블록 만들기
- next 매개변수
- OAuth 카카오
- express router
- css기초
- express.static
- 라우터와 미들웨어
- JWT 로그인 기능 구현
- mysql wsl
- 라우트 매개변수
- 시퀄라이즈 기본설정
- Today
- Total
즐코
지갑, 트랜잭션 구현 (6) - 트랜잭션 풀 업데이트와 브로드캐스트 본문
해당 포스팅에선 블록 채굴 후 트랜잭션 풀의 업데이트 및 블록체인 네트워크 상의 다른 노드들에게 트랜잭션 풀을 공유 즉, 브로드캐스트 해주는 것에 대해서 정리한다.
1/ 트랜잭션 풀 업데이트
우선은 tx를 데이터로 넣고 채굴한 블록을 체인에 추가하면서 txPool도 같이 업데이트해줘야하므로 chain에 txPool 속성을 추가해준다. (txPool 관련 이전 포스팅 https://yjleekr.tistory.com/81?category=1284408)
- 트랜잭션 발생 시 tx pool에 새로운 tx가 추가되게끔 appendTxPool 메소드도 만들어준다.
- 블록 채굴 후 블록체인에 추가 시 block으로 만들어진 data는 이제 필요가 없으므로 pool에서 빼줘야한다. updateTxPool()
// src/core/blockchain/chain.ts
export class Chain {
private blockchain: Block[];
private utxo: IUtxo[];
private txPool: ITransaction[];
constructor() {
this.blockchain = [Block.createGENESIS(GENESIS)];
this.utxo = [];
this.txPool = [];
}
public getTxPool(): ITransaction[] {
return this.txPool;
}
public appendTxPool(_transacion: ITransaction): void {
this.txPool.push(_transaction);
}
// 새로운 블럭 받아서 txPool 업데이트
public updateTxPool(_newBlock: IBlock): void {
let curTxPool: ITransaction[] = this.getTxPool();
_newBlock.data.forEach((tx: ITransaction) => {
curTxPool = curTxPool.filter((eachTx) => {
eachTx.hash !== tx.hash;
});
})
this.txPool = curTxPool;
}
}
트랜잭션 발생 시 새로운 트랜잭션을 추가해주고 체인 내 utxo도 업데이트해준다.
// index.ts
app.post("/sendTransaction", (req, res) => {
try{
const rcvdTx: ReceivedTx = req.body;
const tx = Wallet.sendTransaction(rcvdTx, ws.getUTXO());
ws.appendTxPool(tx); // 트랜잭션 풀에 트랜잭션 추가
ws.updateUTXO(tx); // 새로운 트랜잭션으로 인한 utxo 업데이트
}catch(e){
if (e instanceof Error) console.error(e.message);
}
res.json([]);
})
새로운 블럭 추가 시 chain클래스 메소드에 두 가지 수정사항이 있다.
1/ 새로운 블럭 채굴 시 새로운 tx뿐만 아니라 기존 txPool에 그동안 쌓여있던 tx들도 가져와줘야하므로 miningBlock 메소드 내에서 addBlock 실행 시 인자를 [transaction] 이 아닌 전체 기존 txpool의 tx도 가져와서 넣어준다.
return this.addBlock([transaction, ...this.getTxPool()]);
// src/core/blockchain/chain.ts
public miningBlock(_account: string): Failable<Block, string> {
// 1. 채굴보상을 넣어준다
const txin: ITxIn = new TxIn("", this.getLatestBlock().height + 1);
const txout: ITxOut = new TxOut(_account, 50);
const transaction: Transaction = new Transaction([txin], [txout]);
// 2. 채굴보상 내역에 대해 utxo를 생성하고 이를 체인 내 utxo 배열 내에 추가해준다.
const utxo = transaction.createUTXO();
this.appendUTXO(utxo);
// 3. addBlock 실행 - 새로운 tx와 기존 txPool의 tx들을 불러와서 블럭의 데이터로 넣어준다.
return this.addBlock([transaction, ...this.getTxPool()]);
}
2/ 블럭 검증 후 새로운 블럭의 데이터에 들어있는 tx의 txIns를 utxo 배열에서 없애줌과 동시에 tx 자체도 txPool에서 빼줘야한다. 이걸 addBlock 메서드에서 해결해준다.
// src/core/blockchain/chain.ts
public addBlock(data: ITransaction[]): Failable<Block, string> {
const previousBlock = this.getLatestBlock();
previousBlock.height + 1;
const adjustmentBlock: Block = this.getAdjustmentBlock();
const newBlock = Block.generateBlock(previousBlock, data, adjustmentBlock);
const isValid = Block.isValidNewBlock(newBlock, previousBlock);
if (isValid.isError) return { isError: true, error: isValid.error };
this.blockchain.push(newBlock);
newBlock.data.forEach((_tx: ITransaction) => {
this.updateUTXO(_tx);
});
this.updateTxPool(newBlock);
return { isError: false, value: newBlock };
}
updateTxPool() 에선 기존 txPool을 가져와서, 새로운 블럭의 tx들의 hash값과 txPool에 모여있는 tx들의 hash 값을 비교해서 다른 거만 curTxPool 에 담아주고 그걸 현재 txPool로 대체해준다.
// src/core/blockchain/chain.ts
public updateTxPool(_newBlock: IBlock): void {
// 새로운 블록을 가져온 뒤 그 block의 tx hash랑 txPool의 tx들 해쉬랑 비교해서 같은 거는 없애준다.
let curTxPool: ITransaction[] = this.getTxPool();
_newBlock.data.forEach((tx: ITransaction) => {
curTxPool = curTxPool.filter((eachTx) => {
eachTx.hash !== tx.hash;
});
});
this.txPool = curTxPool;
}
2/ 트랜잭션 풀 Broadcast
이후 이 업데이트한 txPool을 같은 블록체인 네트워크 상의 노드들에게 공유해줘야하기때문에 이전에 만들어둔 블록체인 네트워크의 p2p.ts 수정도 해준다.
웹소켓을 통한 p2p 통신이므로, 이전에 블럭과 체인을 broadcast 했던 방식대로 Message를 만들어서 tx를 공유해준다.
1/ 우선 tx에 대한 메시지이므로 MessageType을 추가해준다. (rcvdTx)
2/ 현재 받은 rcvdTx가 내 트랜잭션 풀에 없다면 추가해주고, payload에 해당 tx를 넣어서 다른 노드들에게 broadcast 해준다.
// src/serve/p2p.ts
enum MessageType {
latest_block = 0,
all_block = 1,
rcvdChain = 2,
rcvdTx = 3,
}
messageHandler(socket: WebSocket) {
const cb = (data: string) => {
const result: Message = P2PServer.dataParse<Message>(data);
const send = this.send(socket);
switch (result.type) {
// 이전에 블럭과 체인에 관련된 메시지 케이스들은 생략한다.
case MessageType.rcvdTx: {
const rcvdTx: ITransaction = result.payload;
if (rcvdTx === null) break;
const sameTx = this.getTxPool().find((_tx: ITransaction) => {
return _tx.hash === rcvdTx.hash;
});
if (!sameTx) {
// 받은 트랜잭션 내용이 내 트랜잭션 풀에 없다면 내 풀에 추가해준다.
this.appendTxPool(rcvdTx);
const msg: Message = {
type: MessageType.rcvdTx,
payload: rcvdTx,
};
// 다른 노드에게도 업데이트된 트랜잭션 풀을 넘겨준다
this.broadcast(msg);
}
break;
}
}
};
socket.on("message", cb);
}
3/ 트랜잭션 요청이 들어오면 트랜잭션을 생성하고, 이 트랜잭션을 다른 노드에게 공유해야하므로, p2p.ts에서 설정해둔대로 Message를 만들어서 payload로 해당 트랜잭션을 전달하면 된다.
// index.ts
app.post("/sendTransaction", (req, res) => {
console.log("reqbody", req.body);
try {
const rcvdTx: ReceivedTx = req.body;
const tx = Wallet.sendTransaction(rcvdTx, ws.getUTXO());
ws.appendTxPool(tx);
ws.updateUTXO(tx);
// 다른 노드에게 트랜잭션을 공유하기위해 메시지를 만든다.
const msg: Message = {
type: MessageType.rcvdTx,
payload: tx,
};
// 다른 노드에게 트랜잭션이 담긴 메시지를 브로드캐스트!
ws.broadcast(msg);
} catch (e) {
if (e instanceof Error) console.error(e.message);
}
res.json([]);
});
또한, 블럭과 체인을 다른 노드에게 broadcast 받았을 때도, 블럭이나 전체 체인 내 블럭에 담긴 tx를 나의 txPool에서 빼는 작업을 해줘야하므로, 받은 블럭을 체인에 추가해주는 addToChain() 메서드와 받은 체인을 통째로 교체하는 replaceChain() 메서드 상에도 updateTxPool() 메서드를 써서 txPool을 업데이트해줘야한다.
// src/core/blockchain/chain.ts
public addToChain(_rcvdBlock: Block): Failable<undefined, string> {
const isValid = Block.isValidNewBlock(_rcvdBlock, this.getLatestBlock());
if (isValid.isError) return { isError: true, error: isValid.error };
this.blockchain.push(_rcvdBlock);
_rcvdBlock.data.forEach((tx) => this.updateUTXO(tx));
this.updateTxPool(_rcvdBlock);
return { isError: false, value: undefined };
}
replaceChain 시엔, txPool 업데이트와 동시에 utxo 업데이트도 필요하다.
// src/core/blockchain/chain.ts
replaceChain(rcvdChain: Block[]): Failable<undefined, string> {
const latestRcvdBlock: Block = rcvdChain[rcvdChain.length - 1];
const latestBlock: Block = this.getLatestBlock();
if (latestRcvdBlock.height === 0) {
return { isError: true, error: "받은 최신블록이 제네시스 블록입니다." };
}
if (latestRcvdBlock.height <= latestBlock.height) {
return { isError: true, error: "내 블체가 더 길거다 같다" };
}
if (latestRcvdBlock.previousHash === latestBlock.hash) {
return { isError: true, error: "블록이 하나 모자르다" };
}
this.blockchain = rcvdChain;
// tx pool 업뎃 + utxo 업뎃
this.blockchain.forEach((block) => {
this.updateTxPool(block);
block.data.forEach((tx) => {
this.updateUTXO(tx);
});
});
return { isError: false, value: undefined };
}
'BlockChain' 카테고리의 다른 글
json-rpc 프로토콜 / web3 라이브러리 / GAS 란?! (0) | 2022.06.28 |
---|---|
이더리움 / 스마트 컨트랙트 (EVM?!) / MAC Geth 설치 (1) | 2022.06.28 |
지갑, 트랜잭션 구현 (5) - 트랜잭션, utxo 업데이트! (0) | 2022.06.23 |
지갑, 트랜잭션 구현 (4) - txIn, txOut, UTXO (0) | 2022.06.22 |
지갑, 트랜잭션 구현 (3) - 지갑 저장, 트랜잭션 보내기 (0) | 2022.06.21 |