목차
1. 브라우저와 노드의 통신
2. 화면 구성
3. 코드 작성하기
이전 코드
https://github.com/100space/2304/tree/main/0504
1. 브라우저와 노드의 통신
브라우저와 노드 1개인 상황에서의 통신을 구현하는 코드를 작성할 것이다.
간단한 흐름을 보기 위한 코드이기 때문에 nunjucks를 이용해서 화면을 구성할 예정이고, CSS는 제외했다.
디렉토리 구조도 잘 나누어야 하지만, wallet_front 디렉토리를 이용하여 front 서버를 이용해서 화면을 그릴 것이다.
브라우저는 3000번 포트를 이용하고, 노드는 8545번 포트를 이용한다.
2. 화면 구성
2-1. Wallet
wallet 영역은 버튼을 이용해서 privateKey, publicKey, account, balance(잔액)을 생성하고, 이를 보여주는 영역이다.
privateKey, publicKey, account의 경우에는 브라우저 서버에서 단순히 메서드 호출을 이용해서 화면을 그릴 수 있지만, balance의 경우에는 UTXO(미사용 트랜잭션 출력값)을 이용해서 구해야 하기 때문에 노드에서 해당 account에 대한 UTXO만 얻어서 잔액을 구할 것이다.
2-2. Wallet List
Wallet List는 생성버튼을 이용해서 만드는 keypair 및 account가 여러 개 있을 경우 그 목록을 보여주는 역할을 하며, account 값만 나열한다. 이 account 값을 누르면 wallet의 항목에 내가 선택한 계정의 정보가 보인다.
2-3. Transaction
Transaction 영역은 송금을 보내는 역할로 "받는 사람"과 "보내는 금액"을 작성할 수 있다.
wallet 영역에 선택된 계정에서 보내며, 해당 계정의 UTXO를 사용한다.
3. 코드 작성하기
3-1. Node 만들기
// index.ts
const chain = new Chain()
const crypto = new CryptoModule()
const proof = new ProofOfWork(crypto)
const workProof = new WorkProof(proof)
const block = new Block(crypto, workProof)
const transaction = new Transaction(crypto)
const unspent = new Unspent()
const digitalSignature = new DigitalSignature(crypto)
const accounts = new Wallet(digitalSignature)
const baekspace = new Ingchain(chain, block, transaction, unspent, accounts)
const app = App(baekspace) // server/app.ts
app.listen(8545, () => {
console.log(`server start`)
})
//server/app.ts
import Ingchain from "@core/ingchain"
import express from "express"
export default (blockchain: Ingchain) => {
const app = express()
app.use(express.json())
app.get("/", (req, res) => {
res.send("Hello, Ingchain")
})
//잔액 구하기
app.post("/getBalance", (req, res) => {
const { account } = req.body
const balance = blockchain.getBlance(account)
res.json({ balance })
})
//계정 생성
app.put("/accounts", (req, res) => {
const account = blockchain.accounts.create() // account는 public으로 주입했다.
res.json({ ...account })
})
//계정 목록 불러오기
app.get("/accounts", (req, res) => {
const account = blockchain.accounts.getAccounts()
res.json(account)
})
//블록 마이닝
app.post("/mineblock", (req, res) => {
const { account } = req.body
const newBlock = blockchain.mineBlock(account)
res.json(newBlock)
})
// 트랜잭션
app.post("/transaction", (req, res) => {
const { receipt } = req.body
receipt.amount = parseInt(receipt.amount)
const transaction = blockchain.sendTransaction(receipt)
res.json({
transaction,
})
})
return app
}
3-2. 브라우저 Wallet 만들기
브라우저에서 wallet을 화면에 그릴 때 기존에 코드를 잘 작성해 놓게 되면 코드의 재활용이 좋다.
단순히 Node를 구성하는 데에만 쓰는 메서드가 아닌 브라우저에서도 사용할 수 있기 때문에 코드를 확장성 있게 잘 구현하면 좋다.
src/wallet_front 디렉토리 안에서 구현할 예정이다
// wallet_front/index.ts
import CryptoModule from "@core/crypto/crypto.module"
import DigitalSignature from "@core/wallet/digitalSignature"
import Wallet from "@core/wallet/wallet"
import WalletClient from "@wallet_front/app"
const crypto = new CryptoModule()
const digitalSignature = new DigitalSignature(crypto)
const accounts = new Wallet(digitalSignature)
const app = WalletClient(accounts)
app.listen(3000, () => {
console.log(`wallet Start`)
})
// wallet_front/app.ts
import express from "express"
import nunjucks from "nunjucks"
import axios from "axios"
import path from "path"
import Wallet from "@core/wallet/wallet"
export default (accounts: Wallet) => {
const app = express()
const viewDir = path.join(__dirname, "views")
app.use(express.json())
app.set("view engine", "html")
nunjucks.configure(viewDir, {
express: app,
})
app.get("/", (req, res) => {
res.render("index")
})
//계정 생성하기
app.post("/wallet", async (req, res) => {
const account = accounts.create()
const {
data: { balance },
} = await axios.post("http://127.0.0.1:8545/getBalance", {
account: account.account,
})
res.json({ ...account, balance })
})
//계정 목록 불러오기
app.get("/wallet", (req, res) => {
const accountList = accounts.getAccounts()
res.json(accountList)
})
//계정을 이용해서 잔액 구하기
app.get("/wallet/:account", async (req, res) => {
const account = accounts.get(req.params.account)
const {
data: { balance },
} = await axios.post("http://127.0.0.1:8545/getBalance", {
account: account.account,
})
res.json({ ...account, balance })
})
// 트랜잭션 만들기
app.post("/transaction", async (req, res) => {
const { sender, received, amount } = req.body
const { publicKey, privateKey } = accounts.get(sender)
const receipt = accounts.sign(
{
sender: {
account: sender,
publicKey,
},
received,
amount,
},
privateKey
)
const tx = await axios.post("http://127.0.0.1:8545/transaction", { receipt })
res.json(tx.data)
})
return app
}
<!-- 화면 그리기 -->
<!-- views/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<h2>wallet</h2>
<button id="wallet_btn">지갑생성</button>
<ul id="wallet_list">
<li>privateKey : <span class="privateKey"></span></li>
<li>publicKey : <span class="publicKey"></span></li>
<li>account : <span class="account"></span></li>
<li>balance : <span claa="balance"></span></li>
</ul>
<h2>Wallet List</h2>
<ul id="wallet_list2"></ul>
<h2>Transaction</h2>
<form action="" id="transactionForm">
<ul>
<li>received : <input type="text" id="received" placeholder="보낼계정" /></li>
<li>amount : <input type="text" id="amount" placeholder="보낼금액" /></li>
</ul>
<button type="submit">전송</button>
</form>
</body>
<script type="text/javascript">
const walletBtn = document.querySelector("#wallet_btn")
const walletUl = document.querySelector("#wallet_list2")
const transactionForm = document.querySelector("#transactionForm")
const createWallet = async () => {
const response = await axios.post("http://127.0.0.1:3000/wallet")
console.log(response.data)
view(response.data)
walletList()
}
const view = (accounts) => {
const walletList = document.querySelectorAll("#wallet_list > li > span")
walletList[0].innerHTML = accounts.privateKey
walletList[1].innerHTML = accounts.publicKey
walletList[2].innerHTML = accounts.account
walletList[3].innerHTML = accounts.balance
}
const walletList = async () => {
const { data } = await axios.get("http://127.0.0.1:3000/wallet")
const accountList = data.map((account) => `<li>${account}</li>`).join("")
walletUl.innerHTML = accountList
}
const clickHandler = async (e) => {
try {
const account = e.target.innerHTML
if (account.length !== 40) return
const { data } = await axios.get(`http://127.0.0.1:3000/wallet/${account}`)
view(data)
} catch (e) {
console.error(e.message)
}
}
const submitHansdler = async (e) => {
e.preventDefault()
const request = {
sender: document.querySelector(".account").innerHTML,
received: e.target.received.value,
amount: e.target.amount.value,
}
await axios.post("http://127.0.0.1:3000/transaction", {
...request,
})
}
walletBtn.addEventListener("click", createWallet)
walletUl.addEventListener("click", clickHandler)
transactionForm.addEventListener("submit", submitHansdler)
walletList()
</script>
</html>
nunjucks를 이용해서 간단하게 구현하는 코드가 완성되었다.
트랜잭션을 전송하게 되면 화면에 보이는 것이 없어서 제대로 트랜잭션이 생성되었는지 확인할 수 없지만 진행된 것이기 때문에 여러 번 누르지 않도록 해야 하고, 보통은 다음블록이 생성되면서 트랜잭션이 데이터에 포함되기 전까지 로딩페이지를 보여줄 수 있고, 아니면 락 기능을 이용해서 처리가 된 것을 시각화할 수 있다.
그리고, 현재 작성한 코드는 트랜잭션이 발생해도 다음 블록이 생성돼야 잔액이 보이기 때문에 트랜잭션을 생성한 후에 블록을 생성하여서 제대로 트랜잭션이 처리되었는지 확인할 수 있다.
'💠BlockChain💠' 카테고리의 다른 글
블록체인 - P2P Network, Node Connect (0) | 2023.05.11 |
---|---|
블록체인 - P2P 네트워크 (0) | 2023.05.09 |
블록체인 - 블록체인 네트워크와 Infura (0) | 2023.05.08 |
블록체인 - 트랜잭션 만들기 (3) (0) | 2023.05.04 |
블록체인 - 지갑만들기, 코드 리팩토링 및 블록체인 구현 (0) | 2023.05.04 |
댓글