니모닉(Mnemonic)이란 결정적 지갑에서 난수 12개의 영단어로 인코딩한 영단어 그룹으로, BIP-39에서 제안되었습니다.
암호화폐 지갑은 비대칭키 암호 방식
을 사용합니다. 이때 공개키와 개인키(=비밀키)가 사용이 되는데, 이 개인키를 사람이 쓰기 편하게 만들어진 것이 바로, 니모닉(mnemonic)입니다.
키 스트레칭 함수 PBKDF2() 함수에 니모닉 코드 단어 + Salt를 넣는다.
Go-ethereum 계정을 구현하는 패키지이다.
https://github.com/miguelmota/go-ethereum-hdwallet
go get github.com/miguelmota/go-ethereum-hdwallet
// TODO : mnemonic 패키지의 함수를 사용하여 랜덤한 니모닉 코드를 얻습니다.
func NewMnemonic(c *gin.Context) {
// entropy 변수에는 hdwallet.NewEntropy 의 결과값을 받습니다.
// 256 비트의 엔트로피를 생성합니다.
entropy, err := hdwallet.NewEntropy(256)
utils.ErrorHandler(err, c, http.StatusServiceUnavailable)
// mnemonic 변수에는 hdwallet.NewMnemonicFromEntropy 의 결과값을 받습니다.
// entropy를 인자값으로 줍니다.
mnemonic, err := hdwallet.NewMnemonicFromEntropy(entropy)
utils.ErrorHandler(err, c, http.StatusServiceUnavailable)
// mnemonic 응답 값을 담을 model 구조체를 만듭니다.
// (응답) model 구조체 변수에 mnemonic을 담아 응답으로 전송합니다.
c.IndentedJSON(http.StatusOK, model.MnemonicResponse{Mnemonic: mnemonic})
}
func NewEntropy(bits int) ([]byte, error) {
return bip39.NewEntropy(bits)
}
func NewMnemonicFromEntropy(entropy []byte) (string, error) {
return bip39.NewMnemonic(entropy)
}
// TODO : 니모닉 코드를 이용해 private key, address를 생성합니다.
func NewWallet(c *gin.Context) {
// 요청에 포함되어 있는 mnemonic을 파싱합니다.
var body model.WalletCreateRequest
if err := c.ShouldBindJSON(&body); err != nil {
utils.ErrorHandler(err, c, http.StatusBadRequest)
}
//mnemonic 변수에는 body 값에서 파싱한 mnemonic 값을 받습니다.
mnemonic := body.Mnemonic
// seed 변수에는 hdwallet.NewSeedFromMnemonic 의 결과값을 받습니다.
// mnemonic 값을 인자값으로 줍니다.
seed, err := hdwallet.NewSeedFromMnemonic(mnemonic)
utils.ErrorHandler(err, c, http.StatusServiceUnavailable)
// wallet 변수에는 hdwallet.NewFromSeed 의 결과값을 받습니다.
// seed 값을 인자값으로 줍니다.
wallet, err := hdwallet.NewFromSeed(seed)
utils.ErrorHandler(err, c, http.StatusServiceUnavailable)
// path 변수에는 hdwallet.MustParseDerivationPath 의 결과값을 받습니다.
// bip44 경로를 인자값으로 줍니다.
path := hdwallet.MustParseDerivationPath("m/44'/60'/0'/0/0")
// account 변수에는 *wallet.Derive 의 결과값을 받습니다.
// path 값을 인자값으로 줍니다.
account, err := wallet.Derive(path, false)
utils.ErrorHandler(err, c, http.StatusServiceUnavailable)
// privateKey 변수에는 *wallet.PrivateKeyHex 의 결과값을 받습니다.
// account 값을 인자값으로 줍니다.
privateKey, _ := wallet.PrivateKeyHex(account)
// address 변수에는 account.Address 의 결과값을 받습니다.
address := account.Address.Hex()
// privateKey, address 응답 값을 담을 model 구조체를 만듭니다.
var result model.WalletResponse
// (응답) model 구조체 변수에 privateKey, address 값을 담아 응답으로 전송합니다.
result.PrivateKey = privateKey
result.Address = address
c.IndentedJSON(http.StatusOK, result)
}
func NewSeedFromMnemonic(mnemonic string) ([]byte, error) {
if mnemonic == "" {
return nil, errors.New("mnemonic is required")
}
return bip39.NewSeedWithErrorChecking(mnemonic, "")
}
func NewFromSeed(seed []byte) (*Wallet, error) {
if len(seed) == 0 {
return nil, errors.New("seed is required")
}
return newWallet(seed)
}
m/44'/60'/0'/0/0
m / purpose' / coin_type' / account' / change / address_index
purpose : 항상 44
coin_type : 코인 종류(암호화폐의 유형)
account : 계정
change : 잔돈 계정 여부
address_index : 사용 가능한 주소 인덱스
func MustParseDerivationPath(path string) accounts.DerivationPath {
parsed, err := accounts.ParseDerivationPath(path)
if err != nil {
panic(err)
}
return parsed
}
pin을 true로 설정하면 계정이 목록에 추가됩니다.
func (w *Wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error) {
// Try to derive the actual account and update its URL if successful
w.stateLock.RLock() // Avoid device disappearing during derivation
address, err := w.deriveAddress(path)
w.stateLock.RUnlock()
// If an error occurred or no pinning was requested, return
if err != nil {
return accounts.Account{}, err
}
func (w *Wallet) PrivateKeyHex(account accounts.Account) (string, error) {
privateKeyBytes, err := w.PrivateKeyBytes(account)
if err != nil {
return "", err
}
return hexutil.Encode(privateKeyBytes)[2:], nil
}
func (a Address) Hex() string {
return string(a.checksumHex())
}