주의사항
내용이 길어지다보니, 중구난방으로 흩어지는 느낌이 있어, 세세한 내용은 풀지 않고 넘어가려고 합니다. 하지만, 후속 포스트
에서 내용 부분에 있어서 각각 자세히 다뤄볼 예정입니다!
내용구성
- 사용방법
- 폴더구조
- 각각 구조에 대한 설명
- 구조에 대한 기본 설명
그럼, 시작하도록 하겠습니다.
전체적인 코드
코드, 링크에 들어가시면 전체적인 코드를 확인할 수 있습니다.
GitHub - tetgo/wm: Command LIne Project, wallet maker with html , rest, just
Command LIne Project, wallet maker with html , rest, just - GitHub - tetgo/wm: Command LIne Project, wallet maker with html , rest, just
github.com
단편적으로 글을 작성하여, 전체적인 코드를 보면서, 글을 읽는게 도움이 될 것 입니다.
사용방법
#go가 설치되어 있어야 합니다.
# 설치
go get github.com/tetgo/wm
# 도움말 확인
wm --help
# html start, html 소스코드가 있어야하므로, 소스코드 root 폴더에서 실행해야합니다.
wm html
# html start with setting set port
wm html --port=8000
# rest start,
wm rest
# rest start with setting port
wm rest --port=8000
폴더구조
폴더구조는 cli
, mnemonicre
, rest
, utils
, explorer
로 나뉘어지게 됩니다. 각각 담당하는 부분이 있습니다.
사용 | |
---|---|
cli | 옵션 값을 받아서, 어떤 것을 실행할지 알려줍니다. |
mnemonicre | 니모닉, 프라이빗 키, 퍼블릭 키를 생성합니다. |
rest | rest api 서비스를 실행합니다. |
utils | 에러 부분을 담당합니다. |
explorer | html 서비스를 만듭니다. |
표와 같이 각각 나누어 관리하기 코드를 관리하기 쉽게 나누어 봤습니다. 모든 서비스들이 mnemonicre 패키지에서 정보를 받아서 결국 client에게 전송하는 것이라고 보면 됩니다. 이제, 각각 폴더별로 소스코드의 중요한 점을 보도록 하겠습니다.
cli
func Start() {
switch kingpin.MustParse(app.Parse(os.Args[1:])) {
case "rest": // rest 받았을 시
fmt.Printf("rest api start, port %d", *rPort)
rest.Start(*rPort)
case "html": // html 받았을 시
explorer.Start(*hPort)
case "just": // just 받았을 시
mnemonicre.Create(*Ent, *hPass)
}
}
cli
package는 소스코드의 시작점이라고 볼 수 있습니다. Arguments
를 받아서 Parse후에 명령어에 맞는 서비스를 시작합니다. 이때 rest
, html
은 포트번호를 따로 받거나, 기본값을 넣어주게 됩니다. just
는 단지 지갑을 만드는 행위입니다. 이 때 필수적으로 passphrase와 entropybit가 필수적으로 받아야합니다.
explorer(html)
explorer
는 html 서비스를 말합니다.
func home(rw http.ResponseWriter, r *http.Request) {
data := homeData{"home", false}
switch r.Method { // method 구분
case "GET":
templates.ExecuteTemplate(rw, "home", data) // 실행
case "POST":
// data 가져오기
entropyBit, _ := strconv.Atoi(r.FormValue("entropyBit"))
passphrase := r.FormValue("passphrase")
// 니모닉과 프라이빗, 퍼블릭 키 생성
a, b, c := mnemonicre.Create(entropyBit, passphrase)
mnemonicBody := Mnemonic{
PageTitle: "home",
Mnemonic: a,
PrivateKey: b,
PublicKey: c,
Success: true,
}
templates.ExecuteTemplate(rw, "home", mnemonicBody) // 생성한 데이터 전송
}
}
func Start(port int) {
handler := http.NewServeMux() // html hander
handler.HandleFunc("/", home)
// html source 로드
templates = template.Must(template.ParseGlob(templateDir + "pages/*.gohtml"))
templates = template.Must(templates.ParseGlob(templateDir + "partials/*.gohtml"))
fmt.Printf("Listening on localhost:%d\n", port)
// 포트번호로 시작
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), handler))
}
html
은 GUI 환경을 만들어줘야하기 때문에 html 소스코드를 만들어주고, 로딩해줘야합니다. 그리고 GET
일 때는 아직 wallet 요청이 없기 떄문에 form을 보여줘야하고, POST
는 결과값을 보여줘야하기 때문에 구분짓는 로직이 필요합니다.
rest
rest
는 기본적으로 http 프로토콜을 그대로 사용하지만, GUI로 제공하지 않고 자원만 주고 받는 형태입니다. 이때 자원은 json, xml로 주로 주고 받습니다.
func mnemonic(rw http.ResponseWriter, r *http.Request) {
var mnemonicBody mnemonicBody
utils.HandleErr(json.NewDecoder(r.Body).Decode(&mnemonicBody)) // json 데이터 받기
entBit, _ := strconv.Atoi(mnemonicBody.EntropyBit) // 형변환
a, b, c := mnemonicre.Create(entBit, mnemonicBody.Passphrase) // 반환 데이터 생성
data := Mnemonic{
Mnemonic: a,
PrivateKey: b,
PublicKey: c,
} // 값 struct 생성
rw.Header().Set("Content-Type", "application/json")
rw.WriteHeader(http.StatusCreated)
json.NewEncoder(rw).Encode(data) // json 반환
}
func Start(aPort int) {
port = fmt.Sprintf(":%d", aPort)
router := mux.NewRouter() // 라우터 생성
router.HandleFunc("/", documentation).Methods("GET") // GET 요청시, 사용법 반환
router.HandleFunc("/mnemonic", mnemonic).Methods("POST") // POST 요청시, 키, 니모닉 전송
log.Fatal(http.ListenAndServe(port, router)) // 서비스 시작
}
JSON
형식으로 값을 주고 받는 것을 확인할 수 있습니다.
utils
package utils
import "log"
func HandleErr(err error) {
if err != nil {
log.Fatal(err)
}
}
utils
부분은 매번 error를 처리하는 로직을 적어줘야 해서, 따로 함수를 만들어주면 편해서 적어둔 것 입니다.
mnemonicre
package mnemonicre
import (
"fmt"
"github.com/tyler-smith/go-bip32"
"github.com/tyler-smith/go-bip39"
)
func Create(entBit int, passphrase string) (string, string, string) {
// Generate a mnemonic for memorization or user-friendly seeds
entropy, _ := bip39.NewEntropy(entBit) // 엔트로피 생성
mnemonic, _ := bip39.NewMnemonic(entropy) // 니모닉 생성
// generate a bip39 hd wallet for the mnemonic and password
seed := bip39.NewSeed(mnemonic, passphrase) // 시드 생성
masterKey, _ := bip32.NewMasterKey(seed) // private key 대입
publicKey := masterKey.PublicKey() // public key 대입
// Display mnemonic and keys
fmt.Println("Mnemonic: ", mnemonic)
fmt.Println("Master private key: ", masterKey)
fmt.Println("Master public key: ", publicKey)
return mnemonic, masterKey.String(), publicKey.String() // 값 반환
}
128 bit으로 시작한다고 가정하고, checksum bit을 붙여서, 각 11 bit으로 나누고, 11 마다 word를 대입합니다. 128 bit 의 ENT의 경우 4 CS(CheckSum)이고 11로 나누면 132 // 11 -> 12 단어가 나오게 됩니다.
CS = ENT / 32
MS = (ENT + CS) / 11
| ENT | CS | ENT+CS | MS |
+-------+----+--------+------+
| 128 | 4 | 132 | 12 |
| 160 | 5 | 165 | 15 |
| 192 | 6 | 198 | 18 |
| 224 | 7 | 231 | 21 |
| 256 | 8 | 264 | 24 |
용어 정리
- entropy
엔트로피는 키 스트레칭 함수 PBKDF2를 통해서 512 비트의 시드를 만드는데 사용합니다.
- seed
시드는 결정론적 지갑을 구축하고 키를 파생하는 데 사용됩니다.
- mnemonic
니모닉 코드
는 entropy bit에 따라서 달라집니다. 각각 128, 160, 192, 224, 256 비트가 있으며, 이 니모닉 길이는 12, 15, 18, 21, 24 단어를 의미합니다.
CS = ENT / 32
MS = (ENT + CS) / 11
| ENT | CS | ENT+CS | MS |
+-------+----+--------+------+
| 128 | 4 | 132 | 12 |
| 160 | 5 | 165 | 15 |
| 192 | 6 | 198 | 18 |
| 224 | 7 | 231 | 21 |
| 256 | 8 | 264 | 24 |
reference
- bip39
https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki
GitHub - bitcoin/bips: Bitcoin Improvement Proposals
Bitcoin Improvement Proposals. Contribute to bitcoin/bips development by creating an account on GitHub.
github.com
- bip32
http://wiki.hash.kr/index.php/BIP32
회고, 고칠점
코드를 작성하며, 완성만 생각하다보니, 내가 짠 코드 필터링 부분이 많이 허술하다고 느껴졌습니다. 예를들어서 128, 160, 192, 224, 256 엔트로비트 말고 다른 비트가 들어올 경우? 이런 점들을 개선하여 코드를 다시 수정해보는 시간을 가져봐야겠습니다.
'etc > 블록체인' 카테고리의 다른 글
DID는 무엇일까? (0) | 2022.01.26 |
---|