ลองเขียน Code เพื่อสร้าง Wallet ตาม BIP32 BIP39 BIP44

หลังจากที่เราได้ทำความเข้าใจเรื่อง Crypto wallet แบบเจาะลึก และมาตรฐาน BIP32 BIP39 BIP44 กันมาแล้ว (ถ้ายังไม่ได้อ่าน ควรอ่านก่อนนะครับ ไม่อย่างนั้น มันจะ งงไปหมดว่าค่าอะไร คืออะไร ทำไมตรงเปลี่ยนตรงไหนเพราะอะไร และต้องเปลี่ยนเป็นอะไร พยายามอธิบายเพื่อให้พอเข้าใจง่ายๆแล้ว) คราวนี้เราจะลงมือเขียนโค้ด เพื่อให้เห็นของจริงกันบ้าง ไม่ยากเกินไปแน่นอน

เครื่องมือที่จะใช้ ก็คือ Google Colab ด้วยภาษา Python โดยเปิด browser ที่คุณใช้งาน ค้น google colab แล้วกด link แรกที่เจอ จากนั้นก็กด new notebook ได้เลย เพื่อให้เปิด browser รองรับสำหรับการเขียนโค้ด

จากนั้นเริ่มต้นติดตั้ง library hdwallet โดยการพิมพ์โค้ดนี้เข้าไป

!pip install hdwallet

แล้วกดปุ่ม shift + enter พร้อมกัน แล้วรอพักนึง เพื่อให้ ระบบทำการติดตั้ง library hdwallet ให้พร้อมใช้

Google Colab

เป็นเครื่องมือสำหรับเขียน code ภาษา python โดยเราสามารถเขียนโค้ด และสั่งประมวลผลได้ผ่าน web browser ได้ทันที โดยไม่ต้องติดตั้งอะไรลงเครื่องเลย มันคือเครื่องมือที่ช่วยเราเล่นกับภาษา python ได้ง่ายและทรงพลังมากๆเลย เพราะทีมผมก็มีการใช้งานใน production บางส่วนด้วยนะ ทำเป็นเล่นไป

อธิบายเล็กน้อย รูปนี้คือผลการติดตั้ง hdwallet library ซึ่งก็ใช้งานได้แล้ว จากนั้นที่กล่องด้านล่าง เราก็พิมพ์โค้ดลงไปต่อได้เลย ดังนี้

from hdwallet import HDWallet
from hdwallet.utils import generate_entropy
from hdwallet.symbols import BTC as SYMBOL
from typing import Optional
 
import json
 
# Choose strength 128, 160, 192, 224 or 256
STRENGTH: int = 128  # Default is 128
# Choose language english, french, italian, spanish, chinese_simplified, chinese_traditional, japanese or korean
LANGUAGE: str = "english"  # Default is english
# Generate new entropy hex string
ENTROPY: str = generate_entropy(strength=STRENGTH)
# Secret passphrase for mnemonic
PASSPHRASE: Optional[str] = None  # 

จากนั้นกด shift + enter เพื่อให้ประมวลผล โดยขั้นตอนนี้จะไม่มี output อะไรออกมา เพราะเป็นการ initial library ต่างๆขึ้นมา และการตั้งค่าเท่านั้น โดยอธิบาย ก็คือ STRENGTH เนี่ย มันก็คือ จำนวน bit ของ mnemonic ที่เคยเล่าไปตอน BIP39 แล้วนั่นเอง 128 bits ก็คือ 12 words ส่วน LANGUAGE คือ ภาษาของ mnemonic ที่แนะนำไว้แล้วว่าควรใช้ภาษาอังกฤษจะดีที่สุด ENTROPY คือค่าที่ได้จากการสุ่ม 128 bits ที่ตั้งค่าใน STRENGTH ไว้ ส่วน PASSPHRASE ก็คือ เราไม่ระบุ ถือว่าไม่มีนั่นเอง ส่วนบรรทัดที่ 3 เป็นการประกาศว่าทำงานด้วย SYMBOL BTC

บรรทัดต่อมาให้เราพิมพ์โค้ด

บรรทัดต่อมาให้เราพิมพ์โค้ด
# Initialize Bitcoin mainnet HDWallet
hdwallet: HDWallet = HDWallet(symbol=SYMBOL, use_default_path=False)
# Get Bitcoin HDWallet from entropy
hdwallet.from_entropy(
    entropy=ENTROPY, language=LANGUAGE, passphrase=PASSPHRASE
)

แล้วกด shift + enter เพื่อประมวลผลเหมือนเดิม จะได้ output ประมาณนี้ ( object id จะไม่เหมือนกัน ไม่ต้องตกใจ เป็นเรื่องปกติ) 

<hdwallet.hdwallet.HDWallet at 0x7f80f03017d0>

อธิบาย โค้ดชุดนี้ ก็คือ การสั่งสร้าง HD Wallet ขึ้นมา เก็บไว้ใน object hdwallet เพื่อให้พร้อมสำหรับการเรียกใช้ตาม BIP44 ต่อไป

จากนั้น เราจะทำการสร้างกระเป๋าใบแรก พร้อมกับเรียกข้อมูลมาดู ดังนี้ (ใส่โค้ดในบรรทัดใหม่ด้านล่างไปเรื่อยๆ อย่าไปแก้โค้ดที่เดิม)

hdwallet.from_path("m/44'/0'/0'/0/0")
print(json.dumps(hdwallet.dumps(), indent=4, ensure_ascii=False))

เราก็จะได้ผลลัพท์แบบนี้เลย

{
    "cryptocurrency": "Bitcoin",
    "symbol": "BTC",
    "network": "mainnet",
    "strength": 128,
    "entropy": "902264f763dd0cbf1d12d85e88cc4cba",
    "mnemonic": "mother basket differ sibling speak garbage inmate force future edit basket input",
    "language": "english",
    "passphrase": null,
    "seed": "8866a75b1fe86f933bd144502a5ffed0ec0e4d3501123bbcbcc20836be9306ef74b81cc1b0475899f9a7eed91764232161c926d6d0a1c15d6f59d35b2a7e809e",
    "root_xprivate_key": "xprv9s21ZrQH143K2R5syJygQ4VQWbxbMXSpm1kQp6SdnPVBYcCfcTRPubMxGJDU1FUkN86Skx9BvAe1zbMMNKPjnFsBGEKP9KvU1HcA4PiGrDV",
    "root_xpublic_key": "xpub661MyMwAqRbcEuAM5LWgmCS94do5kzAg8Eg1cUrFLj2ARQXp9zjeTPgS7ags7axDmcgaQbQn4K8SsTA9zF6nV68XodvbhckURe14jJSvraa",
    "xprivate_key": "xprvA46sr1UGjZrR5CLKgFrCGN7F6BSfyuLhEz1fTeeuYUT4De8zvkeLtCQfMhGHHUNSHAr69a9k8y3bwD4j6PGAK5BizADhYvDco4Pg7CtnPvE",
    "xpublic_key": "xpub6H6EFX1AZwQiHgQnnHPCdW3yeDHAPN4YcCwGG34X6oz36SU9UHxbRzj9CyWHkvqTuij3i9Q1p6csYjb2B22Mtgb3NnkRgYTBXmLzzdwmKBT",
    "uncompressed": "4697dfbb42c3fbdd1c44ce30229074bed7672e11130daa2ffe8fa618a3497fad8cb80e093188ed7bf290c2f3738743c08f65800be123e1d4a015985d1569f749",
    "compressed": "034697dfbb42c3fbdd1c44ce30229074bed7672e11130daa2ffe8fa618a3497fad",
    "chain_code": "3d97d7fd8d8b581f89944f226d8a6f9326dc9c5cefbbe285ec7e5ddece732471",
    "private_key": "a719b2e961c38cb8c51d4b1957af5e5ac9623fd40caa0ee9972a6bae547a6baa",
    "public_key": "034697dfbb42c3fbdd1c44ce30229074bed7672e11130daa2ffe8fa618a3497fad",
    "wif": "L2pXpZZ9FXYi56vfYGCatvDExyg3rqzv1BttDGNLNJBroAVNSdEr",
    "finger_print": "059a93c3",
    "semantic": "p2pkh",
    "path": "m/44'/0'/0'/0/0",
    "hash": "059a93c3ffb472626d76dcb916a1a02227fd0ae0",
    "addresses": {
        "p2pkh": "1WdZAyTDPiReXY5tyCPJ36YBtdsnvrz2S",
        "p2sh": "33HnbGNAXDU1DscooBkPJsApX8FpUEvei9",
        "p2wpkh": "bc1qqkdf8sllk3exymtkmju3dgdqygnl6zhqp3l4h5",
        "p2wpkh_in_p2sh": "3Ac1aQTg2qCSdAfeFvHSA5vETR7tCNce1a",
        "p2wsh": "bc1qny9575sftt3cepl5ppnkdsxjccxjp9gukh75za8yt7eqrtgwtp3ssadm53",
        "p2wsh_in_p2sh": "3CrWMV9s3y2baw2ntRPEy2vgKJqiPe9ZYQ"
    }
}

ซึ่งค่าต่างๆนี้คือค่าที่ใช้อธิบาย HD Wallet ทั้งหมด รวมถึงกระเป๋าใบที่เรากำหนดด้วย เช่น private_key , public_key นี่ก็คือ ค่าเฉพาะของกระเป๋าใบนี้นั่นเอง ส่วน public address ก็คือ ส่วนที่อยู่ใน addresses ซึ่งมีหลายรูปแบบตาม format ที่เราต้องการหยิบไปใช้ได้เลย มันทำงานได้เหมือนกัน แต่แค่เป็นคนละ standard กันเท่านั้นเอง

ทีนี้ อยากให้ลองต่อ โดยการเปลี่ยน index = 1 ใน derivation path จะได้โค้ดดังนี้

# Initialize Bitcoin mainnet HDWallet
hdwallet: HDWallet = HDWallet(symbol=SYMBOL, use_default_path=False)
# Get Bitcoin HDWallet from entropy
hdwallet.from_entropy(
    entropy=ENTROPY, language=LANGUAGE, passphrase=PASSPHRASE
)
hdwallet.from_path("m/44'/0'/0'/0/1")
print(json.dumps(hdwallet.dumps(), indent=4, ensure_ascii=False))

ก็จะได้กระเป๋าใบที่สองออกมาแล้ว โดยจะเห็นได้ว่า ค่าต่างๆหลายๆค่าจะเหมือนกัน นั่นเป็นเพราะ root เดียวกันนั่นเองครับ แล้วค่อยไปประมวลแยกเป็นกระเป๋าย่อยๆอีกทีตามที่อธิบายไปก่อนหน้านี้ทั้งหมดแล้ว

พิสูจน์ของจริง?

ทีนี้ถ้าสงสัย ว่า metamask ทำงานแบบนี้หรือเปล่า ก็ง่ายเลยครับ คือเราต้องสั่งสร้าง wallet โดยใช้ derivation pathเดียวกันขึ้นมาก่อน จากนั้นเอา seed ไป import metamask แล้วดู address 0 ว่ามี address รวมทั้ง compare private key ว่าเหมือนกันมั้ย ถ้าใช่ก็จบแล้วครับ

เริ่มต้น สร้างตาม derivation path ของ metamask (มันเป็น ETH default ครับ) โดยแก้โค้ดส่วน initial จาก

from hdwallet.symbols import BTC as SYMBOL

ให้เป็น

from hdwallet.symbols import ETH as SYMBOL

แล้ว shift + enter จากนั้น สั่งสร้างตามมาตรฐาน BIP44 ที่ต้องกำหนด coin type = 60 (ETH)

# Initialize Bitcoin mainnet HDWallet
hdwallet: HDWallet = HDWallet(symbol=SYMBOL, use_default_path=False)
# Get Bitcoin HDWallet from entropy
hdwallet.from_entropy(
    entropy=ENTROPY, language=LANGUAGE, passphrase=PASSPHRASE
)
hdwallet.from_path("m/44'/60'/0'/0/0")
print(json.dumps(hdwallet.dumps(), indent=4, ensure_ascii=False))

กรณีของผม จะได้ดังนี้

{
    "cryptocurrency": "Ethereum",
    "symbol": "ETH",
    "network": "mainnet",
    "strength": 128,
    "entropy": "db9c6ac1ba56cf0a9991f0829648dcec",
    "mnemonic": "swear today race input hollow luxury green dignity little rather damp sudden",
    "language": "english",
    "passphrase": null,
    "seed": "e0b23f31926a9b633764bb11434b9e3c3d084a5cf765c170c1c797a390749548b1868dc20137eab85fa21929924723f6f83b0bedf0f8ec253fd3f0700fbe60d9",
    "root_xprivate_key": "xprv9s21ZrQH143K3orJm7v9BANJh2JGze5VZLdwdD8WHduLLdaHs1STdFXSubTH9HVHrThibSnZh7ca2pRHmNTTwfvnkd9xuDPnftRAUGznVNS",
    "root_xpublic_key": "xpub661MyMwAqRbcGHvms9T9YJK3F48mQ6oLvZZYRbY7qySKDRuSQYkiB3qvkrij5Mg2hNL4Rxh2wKKBAdQTUE8mjMcVuJ2EPofuKsGsai3EfrJ",
    "xprivate_key": "xprvA4CPJGsxDSVhwtt1CcxPpX4FZAhV2mTPjLcVMGeVySRVayuzmKDzRgghYJPDRZieNFjhbc8mFJ1RnehP2vTnwogsN3eo5ZyBtUiqYVBhtsV",
    "xpublic_key": "xpub6HBjhnQr3p41ANxUJeVQBezz7CXySEBF6ZY69f47XmxUTnF9JrYEyV1BPZp7Knk6k13jcXFSEKezgKQd8DCC2GQtmk6rWSFtXPhFN4xf4Hj",
    "uncompressed": "5a608706fb85e5cedab60c3a6b817cef334a4ee905860497a58de05f70d87151d873e2f30d76574798152c69e340aae6f83ef7d84c123e61c360f6faa057aff0",
    "compressed": "025a608706fb85e5cedab60c3a6b817cef334a4ee905860497a58de05f70d87151",
    "chain_code": "8e8e83309d07ae9be15e1b1cc55c33331b775cb8d87a3664d671c364aba4a5e1",
    "private_key": "25dc5ad91c59f8bb28a6b2a33039a13cf8decbe5c5d7595b230525da5d1e7179",
    "public_key": "025a608706fb85e5cedab60c3a6b817cef334a4ee905860497a58de05f70d87151",
    "wif": "KxVJmrY6WNebvZFewkkYDU9sbJhWZ9eV8s9sEUs2tqHEFUf9a6um",
    "finger_print": "ce2ba1a9",
    "semantic": "p2pkh",
    "path": "m/44'/60'/0'/0/0",
    "hash": "ce2ba1a971e48fbb7092c5a37dfec20a155d269f",
    "addresses": {
        "p2pkh": "0x14Ca6e7EeA0Fa533986BC01501EFf9393AB50780",
        "p2sh": "37Tm9BvNQrP4S9HEJdSK69ru59Rp8UKybr",
        "p2wpkh": "bc1qec46r2t3uj8mkuyjck3hmlkzpg246f5lr3prp5",
        "p2wpkh_in_p2sh": "3BfWBu75gzt9pGJmCMb12d17TiuaxYZK8M",
        "p2wsh": "bc1q6h2dfwwdyuce056h0vu0fdf325yshjr2j7fsggxfqn6ueunvzljs87jk6v",
        "p2wsh_in_p2sh": "3FHRXaeGNP9vc8aRPUjdeKNFE1iwLUMkEp"
    }
}

จะเห็น “mnemonic”: “swear today race input hollow luxury green dignity little rather damp sudden” นี่แหล่ะ คือ seed word

เพื่อทำให้ได้ address   “p2pkh”: “0x14Ca6e7EeA0Fa533986BC01501EFf9393AB50780”

จากนั้นเปิด browser ใหม่ แล้วลองติดตั้ง metamask แล้วทดสอบ import seed word กันครับ 

จัดไป

โป๊ะเชะ address เดียวกันเปี้ยบ เนียนแน่นอน

อะ ไหน export private key มาดูหน่อยสิ (ปุ่ม export private key) ซึ่งจะต้องได้เหมือนข้อมูลที่เราสร้างข้างบนก็คือ   “private_key”: “25dc5ad91c59f8bb28a6b2a33039a13cf8decbe5c5d7595b230525da5d1e7179”

เป๊ะอ่ะ เหมือนเปี๊ยบ

ก็แน่ล่ะครับ เพราะถ้าไม่เหมือนกันขึ้นมาล่ะก็ บันเทิงเลย ไม่ใครก็ใครต้องผิดอะไรบางอย่างแน่นอนแล้ว และถ้าขยันมากกว่านี้ ก็ลองกด new wallet ใน metamask แล้วลองเทียบ address , private key กับการเขียนโค้ดจาก hdwallet ดูนะครับ (โค้ดชุดบน เปลี่ยนแต่ index เป็น 1 แล้วรันใหม่) ก็จะพบว่ามันก็ได้เหมือนกันไปเรื่อยนี่แหล่ะครับ

คำเตือน อย่าเอาค่าในบทความนี้ไปใช้จริง เพราะเงินจะหายจริงด้วยแน่นอน และ ถ้าจะสร้างกระเป๋าใช้เอง อย่าใช้ google colab เด็ดขาด! เพราะมันไม่ได้ปลอดภัย แนะนำให้ download & install anaconda สำหรับคนที่ต้องใช้งาน jupyter notebook (มันก็คือ google colab เลยครับ แต่รันใช้งานในเครื่องเราเอง) หรือว่าใช้ wsl2 เพื่อติดตั้ง linux แล้วติดตั้ง python3 ใช้งานรันเองก็ได้ แบบนี้จะปลอดภัยกว่า ระดับเดียวกับ metamask เลยครับ ข้อได้เปรียบคือตั้ง passphrase ได้ด้วยนะ (แต่ metamask ไม่รองรับ passphrase นะ ดังนั้น มันจะไปใช้ใน metamask ไม่ได้ครับ)

ถึงตรงนี้แล้ว ก็น่าจะเขียน python แบบง่ายๆ เพื่อ สร้าง HD wallet เป็นของตัวเองกันได้แล้วนะครับ รวมทั้งได้ค่าต่างๆที่จำเป็นสำหรับการเอาไปใช้งานต่อได้ด้วย 

สำหรับคนที่ไม่รู้ว่าต้องเขียนโค้ด หรือใช้ Google Colab อย่างไร หรือว่า ใช้ Jupyter notebook แล้วไม่รู้ต้องโค้ดอย่างไร ลอง download ipynb นี้ไป แล้วเอาไป import แล้วลองเล่นดูได้เลยครับ

ซึ่ง wallet ที่ได้จาก hdwallet library นี้ก็ปลอดภัยมากพอที่จะเอาไปใช้งานจริงได้นะครับ แต่ระวังแค่เครื่องมือกับ network ที่เราใช้งานมันเท่านั้นเอง ควรเป็น private + offline เท่านั้นจึงจะมั่นใจได้ครับ อย่าไว้ใจอะไรที่ online หรือข้อมูลที่วิ่งผ่าน internet เป็นอันขาด เพราะอาจจะไม่ใช่เราคนเดียวที่ได้เห็นค่าเหล่านี้ครับ