정신없이 블록체인에 대한 새로운 내용들을 머리에 집어넣느라 멘탈이 상당히 안좋아졌는데 실습 단계로 들어가게되었다. 그래도 글로만 보는 것 보다는 구현해보면서 공부하면 더 이해가 잘 되지 않을까 하는 기대로 다시 한 번 멘탈을 부여잡고... 앞서 블로깅하지 못했던 내용들은 실습 내용에 덧대어 작성해보면서 공부할 계획이다.
router.post("/newMnemonic", async(req, res) => {
let mnemonic;
try {
mnemonic = lightwallet.keystore.generateRandomSeed();
res.json({mnemonic});
} catch(err) {
console.log(err);
}
});
http://localhost:3000/wallet/newMnemonic
에 POST 요청을 하면 다음과 같은 형태로 응답이 온다.
{
"mnemonic":
"laugh vendor crucial length cup surface practice toilet license easy dash rug"
}
password와 mnemonic을 입력값으로 하는 요청이 서버로 들어온다.
router.post("/newWallet", async(req, res) => {
let password = req.body.password;
let mnemonic = req.body.mnemonic;
try {
lightwallet.keystore.createVault(
{
password: password,
seedPhrase: mnemonic,
hdPathString: "m/0'/0'/0'"
},
function (err, ks) {
ks.ketFromPassword(password, function (err, pwDerivedKey) {
ks.generateNewAddress(pwDerivedKey, 1);
let address = ks.getAddresses().toString();
let keystore = ks.serialize();
res.json({keystore: keystore, address: address});
});
}
);
} catch (exception) {
console.log("NewWallet ==>>>> " + exception);
}
});
lightwallet.keystore.createVault
를 사용해 키스토어를 생성한다.
첫번째 인자(options)에는 password, seedPhrase, hdPathString을 담는다.
두번째 인자(callback)에는 키스토어를 인자로 사용하는 함수를 만든다.
eth-lightwallet 모듈
의 keystore.keyFromPassword(password, callback)
내장 함수를 사용한다. 첫번째 인자에는 password, 두번째 인자(callback)에는 pwDerivedKey를 인자로 사용하는 함수를 만든다.
두번째 콜백함수가 실행되면 eth-lightwallet 모듈
의 keystore.generateNewAddress(pwDerivedKey, [num])
을 이용해 새로운 주소 생성 함수를 실행한다.
address 변수를 만들고 keystore.getAddresses()
를 문자열로 할당한다.
keystore 변수를 만들고 keystore.serialize()
를 할당한다.
http://localhost:3000/wallet/newWallet
에 위 과정을 통해 얻은 니모닉 코드와 패스워드를 POST 요청과 함께 보낸다.
응답으로는 keystore와 address가 온다.
응답 대신 fs.writeFile 또는 fs.writeFileSync를 사용해 keystore를 JSON파일로 로컬 서버에 저장한다.
router.post("/newWallet", async(req, res) => {
let password = req.body.password;
let mnemonic = req.body.mnemonic;
try {
lightwallet.keystore.createVault(
{
password: password,
seedPhrase: mnemonic,
hdPathString: "m/0'/0'/0'"
},
function (err, ks) {
ks.ketFromPassword(password, function (err, pwDerivedKey) {
ks.generateNewAddress(pwDerivedKey, 1);
let address = ks.getAddresses().toString();
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);
}
});
POST 요청시 아래와 같은 내용을 담은 JSON 파일이 생성된다.
{"encSeed":{"encStr":"xeqc9REtweRhjs6WsJYiNUaFGn2pbSAdvAPO3TO4uU6c6Z34uMbpyQnRljp82fCbaOSbfGSarV/8+NV9DPGvszmHM4RLc85cU7Da+hsL2UP8Bh7brZVCqMLFeQ8+WwCMzZyU+i2t1cB5pM2921oYjkJ5IAnhnAg1REP8ABHGsMEh+m1HldUY1Q==","nonce":"QL7ZKkDwDeNCMuS0rFC61E7Sq8ehS0ir"},"encHdRootPriv":{"encStr":"2ntcEGH5T27CO0kNsifSZHrcfEN2Xx3SDo4VDiMihYg4n+gcG3NM8uhaszpeFCMXUiDguzX7QXAGEcf7HF25D9JLO94luxlp/+QIg39iC3jtJ1bOKU8pL5fkWdR+prCrs82B5MZM8ciakVMY0Duv4hzTtw/16OLyA33ampgvnw==","nonce":"1py2CkO+g62b3bi/xcNIDJ0DG0nDNSeP"},"addresses":["372a17eabadc3b37563d435f73eef3f8cef02891"],"encPrivKeys":{"372a17eabadc3b37563d435f73eef3f8cef02891":{"key":"CJ0C2RpSB2L0mNMTc8fv9x1g7cCrfL4fBubVOw3za5MNSzkARR11ryCMLx1J0zqM","nonce":"RpmSBKthq9x5Mnuenf71f2lZBo828XDV"}},"hdPathString":"m/0'/0'/0'","salt":"fHNeQi1HlGrUnRg84QHcodEgQIUZ8kikSEb0/LuuD2I=","hdIndex":1,"version":3}
암호화폐 지갑을 사용하기 위한 프라이빗 키를 비밀번호로 암호화한 텍스트 또는 파일이다. 키스토어는 텍슽트나 파일을 보관하고 있다가 다른 지갑에 불러올 때 이를 입력한 후 알맞은 비밀번호를 입력하면 복호화되어 프라이빗 키를 도출할 수 있게 되고 한 번 암호화된 파일이므로 키스토어 파일은 컴퓨터나 메모장 등에 보관하기 안전하다. 단, 비밀번호가 너무 쉬우면 무차별대입 공격으로 쉽게 뚫릴 수 있다.
결정적 지갑에서 난수를 12개의 영단어로 인코딩한 영단어 그룹으로 BIP39에서 제안되었다. 기존의 시드 키는 숫자와 문자로 구성된 난수이기 때문에 사용자가 기억하기 어려웠지만 니모닉 코드 단어의 경우 사용자가 기억하고 사용하기 쉬운 형태로 구성되어 있다는 장점이 있다.
결정적 지갑의 난수를 인코딩한 영단어 시퀀스로 결정적 지갑을 사용하는 지갑 앱에서는 사용자가 지갑을 생성하면 해당 지갑에서 만든 난수로부터 영단어 시퀀스를 만들어 사용자에게 제공한다. 그리고 이 단어 시퀀스에서 시드 키를 생성한다.
사용자가 이 단어 시퀀스를 기억하고 있으면 호환되는 다른 지갑 앱에서도 시드 키 및 파생된 모든 비밀 키를 복구할 수 있다.