Python 版本奇亚(Chia, XCH) 核心之创建私钥、助记词和指纹(公钥),其实真正的核心是创建助记词
为了完成创建私钥、助记词和指纹(公钥)的功能,需要以下几步
- 创建 24 个单词的助记词 (mnemonic)
- 通过助记词创建 seed,这次通过添加密码的方式来改变 seed
- 通过 seed 来获取私钥
- 通过私钥来获取公钥和指纹
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}
目前尚无回复