니모닉(Mnemonic)이란?
: 지갑을 복구하기 위한 12개의 단어로, 개인 키가 복잡한 단어들로 구성되어 입력이 불편하다는 단점을 보완하기 위한 형식이다.
암호화폐 지갑은 비대칭키 암호방식을 사용하기 때문에 공개키와 비밀키가 존재한다. 니모닉 단어 목록은 비밀키를 사용자가 쉽게 읽고 쓸 수 있도록 만들어주며, 덕분에 지갑을 백업하기에도 좋다.
니모닉 지갑 개발하기
니모닉 지갑을 직접 구현하지 않아도 손쉽게 만들 수 있게 해주는 라이브러리가 있다.
바로 eth-lightwallet 모듈이다.
니모닉 지갑의 원리를 이해하기 위해 eth-lightwallet 모듈을 이용하여 간단한 Mnemonic Wallet을 개발할 것이다.
또한 개발한 기능이 정상적으로 작동하는지 API 테스트를 통해 확인해보자.
개발 요구 사항
- eth-lightwallet 모듈에 내장되어 있는 함수를 사용하여 개발
- 랜덤한 니모닉 코드를 생성
- 니모닉을 시드로 키스토어를 생성
- 시드 값을 기반으로 파생된 주소 생성
- fs 모듈을 이용한 키스토어 로컬 저장
프로젝트 구조
.
├── .gitignore
├── app.js
├── package.json
├── routes/
│ └── wallet.js
├── wallet.json // fs모듈로 키스토어 로컬 저장시 생성되는 파일
API 명세 (Endpoint)
Method | URI | Request Body | Response | Description |
GET | / | none | status code: 200 | 서버 실행 정상 작동 확인 |
POST | /wallet/newMnemonic | none | 니모닉 코드(json 형식) | 니모닉 코드 생성 |
POST | /wallet/newWallet | - password - 니모닉 코드 |
로컬 저장 성공 결과 코드 - 1: 성공 - 999: 실패 |
니모닉 코드를 시드로 한 키스토어와 주소 생성 후, 로컬에 키스토어 파일 저장 |
소스 코드
package.json : dependencies - 해당 프로젝트에 사용되는 라이브러리
{
"name": "my-mnemonic-wallet",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "nodemon ./app.js"
},
"dependencies": {
"cookie-parser": "~1.4.4",
"cors": "^2.8.5",
"debug": "~2.6.9",
"eth-lightwallet": "^4.0.0",
"express": "~4.16.1",
"morgan": "~1.9.1"
}
}
* package.json 파일이 있으면 한번에 필요한 라이브러리를 설치할 수 있다
=> 프로젝트 루트 위치에서 npm install
명령 입력
app.js : 웹 서버 생성
const express = require('express');
const cookieParser = require('cookie-parser');
const logger = require('morgan');
const cors = require('cors');
const walletRouter = require('./routes/wallet');
const app = express();
const port = 3000;
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(
cors({
origin: ['http://localhost:3000'],
methods: ['GET', 'POST'],
credentials: true,
})
);
app.get('/', function (req, res, next) {
res.status(200).send({ message: 'Mnemonic server is running...' });
});
app.use('/wallet', walletRouter);
// 404 Not Found 에러 핸들링
app.use((req, res, next) => {
const err = new Error('Not Found');
err['status'] = 404;
next(err);
});
// 그 외의 에러 핸들링: 에러 코드를 메세지로 출력
app.use((err, req, res, next) => {
res.status(err.status || 500);
res.json({
errors: {
message: err.message,
},
});
});
app.listen(port, () => {
console.log(`
################################################
🛡️ Server listening on port: ${port} 🛡️
http://localhost:${port}
################################################
`);
});
module.exports = app;
routes/wallet.js : 지갑 생성과 관련 된 Router 모음
const express = require('express');
const router = express.Router();
const lightwallet = require('eth-lightwallet'); // TIP! github: https://github.com/ConsenSys/eth-lightwallet#readme
const fs = require('fs');
// 랜덤한 니모닉 코드 생성
router.post('/newMnemonic', async (req, res) => {
let mnemonic;
try {
// generateRandomSeed(): Generates a string consisting of a random 12-word seed and returns it.
mnemonic = lightwallet.keystore.generateRandomSeed();
res.json({ mnemonic });
} catch (err) {
console.log(err);
}
});
// 니모닉 코드와 패스워드를 이용해 keystore와 address 생성
router.post('/newWallet', async (req, res) => {
let password = req.body.password;
let mnemonic = req.body.mnemonic;
try {
// createVault(options, callback): the interface to create a new lightwallet keystore.
lightwallet.keystore.createVault(
{
password: password,
seedPhrase: mnemonic, //Optionally provide a 12-word seed phrase
hdPathString: "m/0'/0'/0'", // Optional custom HD Path String; default:"m/0'/0'/0'"
},
function (err, ks) {
// Some methods will require providing the `pwDerivedKey`,
// Allowing you to only decrypt private keys on an as-needed basis.
// You can generate that value with this convenient method:
ks.keyFromPassword(password, function (err, pwDerivedKey) {
// generateNewAddress(pwDerivedKey, [num]): default of num = 1
// generate 'num' new address/private key pairs
// the corresponding private keys are also encrypted
ks.generateNewAddress(pwDerivedKey, 1);
// getAddresses(): Returns a list of hex-string addresses currently stored in the keystore.
let address = ks.getAddresses().toString();
// serialize(): Serializes the current keystore object into a JSON-encoded string and returns that string.
let keystore = ks.serialize();
fs.writeFile('wallet.json', keystore, function (err, data) {
if (err) {
res.json({ code: 999, message: '실패' });
} else {
res.json({ code: 1, message: '성공' });
}
});
});
}
);
} catch (exception) {
console.log('NewWallet ==>>>> ' + exception);
}
});
module.exports = router;
lightWallet 모듈의 내장함수 각각에 대한 설명은 주석으로 달아놨다.
주석의 내용은 오역이 있을 수도 있으니 lightWallet 모듈의 공식 깃허브에 적힌 원어설명을 그대로 적었다.
더 자세한 설명이 필요하다거나 다른 내장함수를 확인하고 싶다면 여기 Readme 에서 확인할 수 있다.
API 테스트 결과
1. 니모닉 코드 생성
-> 니모닉 코드인 12개의 영단어가 잘 담긴 것을 확인할 수 있다.
2. 니모닉 코드를 시드로 한 키스토어와 주소 생성
2-1. 키스토어와 주소를 json 형식으로 응답해줄 때
res.json({ keystore: keystore, address: address });
2-2. 키스토어 정보 객체를 로컬 파일에 저장하고 성공 여부만을 json으로 응답해줄 때
회고
니모닉 지갑은 실습 주제 중에 공부한지 가장 오래된 주제였는데, 한번 더 복습하니까 머릿속에 내용이 더 잘 들어오는 것 같았다.
니모닉의 개념, 니모닉 지갑의 쓰임, 그리고 니모닉 지갑이 생성되는 과정에 대한 이해는 했지만, 모듈없이 니모닉 지갑을 생성하는 법은 아직 어려운 것 같다. lightwallet 모듈은 어떤 코드로 작성되어있는지 좀 더 들여다보고 깊게 공부해봐도 좋겠다는 생각이 들었다.
소스코드: https://github.com/Ahreum0714/My-Mnemonic-Wallet/tree/main
'Blockchain' 카테고리의 다른 글
[Project#1] DID를 활용한 백신 증명 시스템 (0) | 2022.06.10 |
---|---|
Error: invalid argument 0: json: cannot unmarshal hex string without 0x prefix into Go struct field TransactionArgs.data of type hexutil.Bytes (0) | 2022.05.23 |
[실습] 자바스크립트로 간단한 니모닉 지갑 (Mnemonic Wallet) 개발하기 (0) | 2022.05.18 |
블록체인 심화 (4) - 합의 알고리즘 PoW, PoS, DPoS, BFT, PBFT (0) | 2022.05.13 |
블록체인 심화 (3) - 비트코인과 이더리움의 트랜잭션 데이터 구조 차이 (0) | 2022.05.12 |