Python 版本奇亚(Chia, XCH) 核心之创建私钥、助记词和指纹(公钥)

yufei       4 月, 3 周 前       347

Python 版本奇亚(Chia, XCH) 核心之创建私钥、助记词和指纹(公钥),其实真正的核心是创建助记词

为了完成创建私钥、助记词和指纹(公钥)的功能,需要以下几步

  1. 创建 24 个单词的助记词 (mnemonic)
  2. 通过助记词创建 seed,这次通过添加密码的方式来改变 seed
  3. 通过 seed 来获取私钥
  4. 通过私钥来获取公钥和指纹

Python3 使用的是 3.9 的版本

开始之前需要安装一大堆的库 pip freeze 的结果如下

argon2-cffi==20.1.0
bitstring==3.1.7
blspy==1.0.2
cffi==1.14.5
importlib-metadata==4.0.1
keyring==23.0.1
keyrings.cryptfile==1.3.8
pycparser==2.20
pycryptodome==3.10.1
six==1.16.0
zipp==3.4.1

然后完整的代码如下

import unicodedata
from hashlib import pbkdf2_hmac
from secrets import token_bytes
from sys import platform
from typing import List, Optional, Tuple

import keyring as keyring_main
import pkg_resources
from bitstring import BitArray
import blspy
from blspy import AugSchemeMPL, G1Element, PrivateKey
from keyrings.cryptfile.cryptfile import CryptFileKeyring

import io
from typing import Any, BinaryIO


if platform == "win32" or platform == "cygwin":
    import keyring.backends.Windows

    keyring.set_keyring(keyring.backends.Windows.WinVaultKeyring())
elif platform == "darwin":
    import keyring.backends.macOS

    keyring.set_keyring(keyring.backends.macOS.Keyring())
elif platform == "linux":
    keyring = CryptFileKeyring()
    keyring.keyring_key = "your keyring password"  # type: ignore
else:
    keyring = keyring_main



def hexstr_to_bytes(input_str: str) -> bytes:
    """
    Converts a hex string into bytes, removing the 0x if it's present.
    """
    if input_str.startswith("0x") or input_str.startswith("0X"):
        return bytes.fromhex(input_str[2:])
    return bytes.fromhex(input_str)


def make_sized_bytes(size: int):
    """
    Create a streamable type that subclasses "bytes" but requires instances
    to be a certain, fixed size.
    """
    name = "bytes%d" % size

    def __new__(cls, v):
        v = bytes(v)
        if not isinstance(v, bytes) or len(v) != size:
            raise ValueError("bad %s initializer %s" % (name, v))
        return bytes.__new__(cls, v)  # type: ignore

    @classmethod  # type: ignore
    def parse(cls, f: BinaryIO) -> Any:
        b = f.read(size)
        assert len(b) == size
        return cls(b)

    def stream(self, f):
        f.write(self)

    @classmethod  # type: ignore
    def from_bytes(cls: Any, blob: bytes) -> Any:
        # pylint: disable=no-member
        f = io.BytesIO(blob)
        result = cls.parse(f)
        assert f.read() == b""
        return result

    def __bytes__(self: Any) -> bytes:
        f = io.BytesIO()
        self.stream(f)
        return bytes(f.getvalue())

    def __str__(self):
        return self.hex()

    def __repr__(self):
        return "<%s: %s>" % (self.__class__.__name__, str(self))

    namespace = dict(
        __new__=__new__,
        parse=parse,
        stream=stream,
        from_bytes=from_bytes,
        __bytes__=__bytes__,
        __str__=__str__,
        __repr__=__repr__,
    )

    return type(name, (bytes,), namespace)


bytes32 = make_sized_bytes(32)
MAX_KEYS = 100

def std_hash(b) -> bytes32:
    """
    The standard hash used in many places.
    """
    return bytes32(blspy.Util.hash256(bytes(b)))


def bip39_word_list() -> str:
    return pkg_resources.resource_string(__name__, "english.txt").decode()


def generate_mnemonic() -> str:
    mnemonic_bytes = token_bytes(32)
    mnemonic = bytes_to_mnemonic(mnemonic_bytes)
    return mnemonic


def bytes_to_mnemonic(mnemonic_bytes: bytes) -> str:
    if len(mnemonic_bytes) not in [16, 20, 24, 28, 32]:
        raise ValueError(
            f"Data length should be one of the following: [16, 20, 24, 28, 32], but it is {len(mnemonic_bytes)}."
        )
    word_list = bip39_word_list().splitlines()
    CS = len(mnemonic_bytes) // 4

    checksum = BitArray(bytes(std_hash(mnemonic_bytes)))[:CS]

    bitarray = BitArray(mnemonic_bytes) + checksum
    mnemonics = []
    assert len(bitarray) % 11 == 0

    for i in range(0, len(bitarray) // 11):
        start = i * 11
        end = start + 11
        bits = bitarray[start:end]
        m_word_position = bits.uint
        m_word = word_list[m_word_position]
        mnemonics.append(m_word)

    return " ".join(mnemonics)


def bytes_from_mnemonic(mnemonic_str: str) -> bytes:
    mnemonic: List[str] = mnemonic_str.split(" ")
    if len(mnemonic) not in [12, 15, 18, 21, 24]:
        raise ValueError("Invalid mnemonic length")

    word_list = {word: i for i, word in enumerate(bip39_word_list().splitlines())}
    bit_array = BitArray()
    for i in range(0, len(mnemonic)):
        word = mnemonic[i]
        if word not in word_list:
            raise ValueError(f"'{word}' is not in the mnemonic dictionary; may be misspelled")
        value = word_list[word]
        bit_array.append(BitArray(uint=value, length=11))

    CS: int = len(mnemonic) // 3
    ENT: int = len(mnemonic) * 11 - CS
    assert len(bit_array) == len(mnemonic) * 11
    assert ENT % 32 == 0

    entropy_bytes = bit_array[:ENT].bytes
    checksum_bytes = bit_array[ENT:]
    checksum = BitArray(std_hash(entropy_bytes))[:CS]

    assert len(checksum_bytes) == CS

    if checksum != checksum_bytes:
        raise ValueError("Invalid order of mnemonic words")

    return entropy_bytes


def mnemonic_to_seed(mnemonic: str, passphrase: str) -> bytes:
    """
    Uses BIP39 standard to derive a seed from entropy bytes.
    """
    salt_str: str = "mnemonic" + passphrase
    salt = unicodedata.normalize("NFKD", salt_str).encode("utf-8")
    mnemonic_normalized = unicodedata.normalize("NFKD", mnemonic).encode("utf-8")
    seed = pbkdf2_hmac("sha512", mnemonic_normalized, salt, 2048)

    assert len(seed) == 64
    return seed


def create_private_key_and_mnemonic(passphrase):
    mnemonic = generate_mnemonic()
    seed = mnemonic_to_seed(mnemonic, passphrase)
    key  = AugSchemeMPL.key_gen(seed)
    pk = bytes(key.get_g1()).hex() 
    fingerprint = key.get_g1().get_fingerprint()
    return dict(mnemonic=mnemonic,seed=seed.hex(),priavate_key=pk,fingerprint=fingerprint)


if __name__ == '__main__':

    print(create_private_key_and_mnemonic(""))

创建私钥只要

create_private_key_and_mnemonic("")

注意:目前 Python 版本的虽然做出了密码的功能,但是没有密码的选项

执行结果如下

{
    'mnemonic': 'recipe avoid speak recycle sphere cricket inside wire require dash give fabric loop review bitter enjoy palm voice talk planet track region polar hotel', 
    'seed': '542d7df6fa146c6db2eda09896768d1b378c988f1e7a88aa6ac7ea4131acf3f892efe9456f704b810e66d6e0a6dc6cd56bad6a1290c71c3dea3927cb23d85ad8', 
    'priavate_key': 'a9dcdd57d93b9bd14578331852063bb4788cdc25e0804d4e81b8628b25ba1fe1c315a2031e84ef6364305d291fbbc3c6', 
    'fingerprint': 671522950}
目前尚无回复
简单教程 = 简单教程,简单编程
简单教程 是一个关于技术和学习的地方
现在注册
已注册用户请 登入
关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2018 简单教程 twle.cn All Rights Reserved.