2022-01-26 07:42:16 +03:00
|
|
|
import os
|
|
|
|
import sys
|
2021-03-26 06:25:45 +03:00
|
|
|
|
2022-01-26 07:42:16 +03:00
|
|
|
from blspy import AugSchemeMPL, G1Element, G2Element, PrivateKey
|
|
|
|
from enum import Enum
|
|
|
|
from pathlib import Path
|
|
|
|
from typing import Any, Dict, List, Optional, Tuple, Union
|
2021-03-26 06:25:45 +03:00
|
|
|
|
2021-04-04 06:55:26 +03:00
|
|
|
from chia.consensus.coinbase import create_puzzlehash_for_pk
|
|
|
|
from chia.util.bech32m import encode_puzzle_hash
|
|
|
|
from chia.util.config import load_config
|
|
|
|
from chia.util.default_root import DEFAULT_ROOT_PATH
|
|
|
|
from chia.util.ints import uint32
|
2022-01-26 07:42:16 +03:00
|
|
|
from chia.util.keychain import Keychain, bytes_to_mnemonic, generate_mnemonic, mnemonic_to_seed, unlocks_keyring
|
|
|
|
from chia.wallet.derive_keys import (
|
|
|
|
master_sk_to_farmer_sk,
|
|
|
|
master_sk_to_pool_sk,
|
|
|
|
master_sk_to_wallet_sk,
|
|
|
|
master_sk_to_wallet_sk_unhardened,
|
|
|
|
)
|
2021-03-26 06:25:45 +03:00
|
|
|
|
|
|
|
|
|
|
|
def generate_and_print():
|
|
|
|
"""
|
|
|
|
Generates a seed for a private key, and prints the mnemonic to the terminal.
|
|
|
|
"""
|
|
|
|
|
|
|
|
mnemonic = generate_mnemonic()
|
|
|
|
print("Generating private key. Mnemonic (24 secret words):")
|
|
|
|
print(mnemonic)
|
2021-05-13 01:24:55 +03:00
|
|
|
print("Note that this key has not been added to the keychain. Run chia keys add")
|
2021-03-26 06:25:45 +03:00
|
|
|
return mnemonic
|
|
|
|
|
|
|
|
|
2021-08-04 22:46:55 +03:00
|
|
|
@unlocks_keyring(use_passphrase_cache=True)
|
2021-03-26 06:25:45 +03:00
|
|
|
def generate_and_add():
|
|
|
|
"""
|
|
|
|
Generates a seed for a private key, prints the mnemonic to the terminal, and adds the key to the keyring.
|
|
|
|
"""
|
|
|
|
|
|
|
|
mnemonic = generate_mnemonic()
|
|
|
|
print("Generating private key")
|
|
|
|
add_private_key_seed(mnemonic)
|
|
|
|
|
|
|
|
|
2021-08-04 22:46:55 +03:00
|
|
|
@unlocks_keyring(use_passphrase_cache=True)
|
2021-03-26 06:25:45 +03:00
|
|
|
def query_and_add_private_key_seed():
|
|
|
|
mnemonic = input("Enter the mnemonic you want to use: ")
|
|
|
|
add_private_key_seed(mnemonic)
|
|
|
|
|
|
|
|
|
2021-08-04 22:46:55 +03:00
|
|
|
@unlocks_keyring(use_passphrase_cache=True)
|
2021-03-26 06:25:45 +03:00
|
|
|
def add_private_key_seed(mnemonic: str):
|
|
|
|
"""
|
|
|
|
Add a private key seed to the keyring, with the given mnemonic.
|
|
|
|
"""
|
|
|
|
|
|
|
|
try:
|
|
|
|
passphrase = ""
|
2022-01-26 07:42:16 +03:00
|
|
|
sk = Keychain().add_private_key(mnemonic, passphrase)
|
2021-03-26 06:25:45 +03:00
|
|
|
fingerprint = sk.get_g1().get_fingerprint()
|
Pools mainnet (#7047)
* added clarifying comments
* WIP test
* added WIP test
* Refine genesis challenge. Remove unnecessary pool_puzzle function
* Sign spend. Remove create_member_spend. Rename state transition function to create_travel_spend
* Rename create_member_spend to create_travel_spend
* Add singleton id logging
* Enhance logging for debugging
* renaming
* rephrase inside the puzzle
* fixed signing and added some support functions
* Fix issue with announcement
* Progress spending the singleton
* Fix arguments to pool_state_to_inner_puzzle call
* Fix arguments to pool_state_to_inner_puzzle
* Improve error message when wallet is not running
* Remove misleading message about missing wallet process, when problem is the farmer by making poolnft command error out earlier
* Fix parent coin info bug
* Multiple state transitions in one block
* Lint
* Remove assert
* Fix incorrect p2_singleton_ph calculation (thanks nil00)
* Update waiting room puzzle to accept genesis_challenge
* Update calls to create_waiting
* Go to waiting state from committed state
* Augment debug_spend_bundle
* fix 2 bugs in wallet
* Fix lint
* fix bad_agg_sig bug
* Tests and lint
* remove breakpoint
* fix clvm tests for new hexs and hashs
* Fixed a bug in the coin store that was probably from merging. (#6577)
* Fixed a bug in the coin store that was probably from merging.
* The exception doesn't need to be there
* CI Lint fix
* Added lifecycle tests for pooling drivers (#6610)
* Ms.poolabsorb (#6615)
* Support for absorbing rewards in pools (untested)
* Style improvements
* More work on absorb
* Revert default root and remove log
* Revert small plots
* Use real sub slot iters
* Update types
* debug1
* Fix bugs
* fix output of agg sig log messages
* Make fewer calls to pw_status in test
* remove old comment
* logging and state management
* logging
* small bug fix & rename for accuracy
* format
* Fix types for uncurry function
* lint
* Update test to use exceptions
* Change assumptions about self-pooling in lifecycle test
* Install types for mypy
* Revert "Install types for mypy"
This reverts commit a82dcb712a6a97b8789b17c98cac8eafaffe90f5.
* install types for mypy
* install types for mypy
* More keys
* Remove flags requiring interactive prompts
* Change initial spend to waiting room if self-pooling
* lint
* lint
* linting
* Refactor test
* Use correct value in log message
* update p2_singleton_or_delated_puzhash
* initial version of pool wallet with p2_singleton_or_delay
* run black formatting
* fix rebase wonkiness
* fix announcement code in p2_singleton_or_delayed
* removed redundant defaulting
standardised hexstr handling
* lint fixes
* Fixed pool lifecycle tests to current standards, but discovered tests are not validating signatures
* Signatures validate on this test now although the test still does not check it.
* Lint fix
* Fixed plotnft show and linting errors
* fixed failing farmer/harvester rpc test
* lint fix
* Commenting out some outdated tests
* Updated test coverage
* lint fix
* Some minor P2singleton improvements (#6325)
* Improve some debugging tools.
* Tidy pool clvm.
* Use `SINGLETON_STRUCT`. Remove unused `and` macro.
* Use better name `SINGLETON_MOD_HASH`.
* Finish lifecycle test suite.
* Fixing for merge with chia-blockchain/pools_delayed_puzzle (#72)
Co-authored-by: Matt Hauff <quexington@gmail.com>
* Default delay time was being set incorrectly
* Extracted get_delayed_puz_info_from_launcher_spend to driver code
* Ms.taproot plot2 (#6692)
* Start work on adding taproot to new plots
* Fix issue in block_tools
* new test-cache
* Lint
* DID fixexs
* Fix other tests
* Python black
* Fix full node store test
* Ensure block index <= 128 bits.
* fix test_pool_config test
* fix comments in pool_config and in chialisp files
* self_pool -> pool -> self_pool
* Implement leaving pools
* Fix conflicts with main via mini-rebase
* Fixup rebase mistakes
* Bring in Mariano's node discovery chagnes from pools.dev
* Fix adapters - Thanks Richard
* build tests
* Add pools tests
* Disable DID tests
* farmer|protocol: Some renaming related to the pool protocol
* farmer: Use `None` instead of `{}` and add local `pool_state`
* protocol|farmer: Introduce and use `PoolErrorCode`
* rename: `pool_payout_instructions` -> `payout_instructions`
* refactor: `AuthenticationKeyInfo` -> `authentication_key`
* refactor: Move `launcher_id` up
* rename: Some variable name changes
* rename: `points_balance` -> `points`
* format: Squash aggregation into one line
* farmer: Make `update_pool_state` public
* farmer: Print traceback if `update_pool_state` fails
* farmer: Periodically call `GET /pool_info`, add `_pool_get_pool_info`
* farmer: Add `authentication_token_timeout` to `pool_state`
Fetch it from `GET /pool_info`
* protocol|farmer: Implement support for `GET|POST|PUT /farmer`
* farmer: Make use of `GET|POST /farmer`
- To make the farmer known by the pool
- To update local balance/difficulty from the pool periodically
* farmer|protocol: Adjust `POST /partial` to match the latest spec
* farmer: Hash messages before signing
* pools: Drop unused code
* farmer: Fix aggregation of partial signatures
* farmer: support self pooling, don't pool if url==""
* wallet: return uint64 for delay time, instead of bytes
* pool: add error code for delay time too short
* farmer: cleaner logging when no connection to pool
* farmer: add harvester node id to pool protocol
* Rename method (test fix) and lint fix
* Change errors to warnings (pool communication)
* Remove pool callbacks on a reorg
* farmer: Continue earlier when no pool URL is provided
* farmer: Print method in log
* farmer: Handle exceptions for all pool endpoint calls
* farmer|protocol: Keep track of failed requests to the pool
* farmer: Fix typo which caused issue with pooling
* wallet: simplify solution_to_extra_data
* tests: Comment out DID tests which are not working yet
* Remove DID Wallet test workflows
* Return launcher_id when creating Pool Wallet
* Name p2_singleton_puzzle_hash correctly
* Improve 'test_singleton_lifecycle_fast.py'.
* Make test more robust in the face of asynchronous adversity
* Add commandline cmds for joining and leaving pools
* Fix poolnft leave params
* Remove redundant assignment brought in from main
* Remove unneeded code
* Style and parsimony
* pool_puzzles: Check was wrong, and bad naming
* format: Fix linting
* format: Remove log and rename variable
* pool-wallet: Fix self pooling with multiple pool wallets. Don't remove interested puzzle_hash
* gui: Use pools branch
* format: fix lint
* Remove ununsed code, improve initial_pool_state_from_dict
* farmer: Instantly update the config, when config file changes
* format: Speed up loading of the authentication key
* logging: less annoying logging
* Test pool NFT creation directly to pool
* Test switching pools without self-farming in between
* lint
* pooling: Use integer for protocol version (#6797)
* pooling: Use integer for protocol version
* pooling: Fix import
* Update GUI commit
* Ms.login2 (#6804)
* pooling: Login WIP
* pooling: add RPC for get_link
* dont use timeout
* pooling: rename to get_login_link
* format: remove logging
* Fix SES test
* Required cli argument
Co-authored-by: almog <almogdepaz@gmail.com>
* farmer|protocols: Rename `current_difficulty` for `POST /partial` (#6807)
* Fix to farm summary
* Use target_puzzlehash param name in RPC call
* Pool test coverage (#6782)
* Improvement in test coverage and typing
* Added an extra absorb to the pool lifecycle test (only works when merged with https://github.com/Chia-Network/chia-blockchain/pull/6733)
* Added new drivers for the p2_singleton puzzles
* Added new tests and test coverage for singletons
* organize pools testing directory
* black formatting
* black formatting in venv
* lint fix
* Update CI tests
* Fixing tests post rebase
* lint fix
* Minor readability fix
Co-authored-by: matt <matt@chia.net>
* farmer: Drop `target_puzzle_hash` from `GET /farmer` and `GET /login` (#6816)
* Allow creation of PlotNFTs in self-farming state
* gui: Fix install with more RAM (#6821)
* Allow implicit payout_address in self-pool state, improve error messages and param ergonomics
* print units in non-standard wallets correctly
* Fix farmer import
* Make syncing message in CLI more intuitive like the GUI
* Fix linting and show header hash instead of height
* gui: Update to 725071236eff8c81d5b267dc8eb69d7e03f3df8c
* Revert "Merge"
This reverts commit 23a1e688c5fb4f72983fd896d4933336a365fe38, reversing
changes made to a850246c6f4de4d2eb65c4ac1d6023431f3ba7e9.
* Revert "Revert "Merge""
This reverts commit 680331859f5dc404cca9c2ff8f4a61df374db125.
* Treat tx_record as Dict. Refactor tx submission
* Also add passed-in coin spends when processing new blocks in reconsider_peak
* Test utilities had moved
* Fix import of moved block_tools
* Potentially fix yaml
* Previously didn't take the right part of this change
* Add -y flag, improve commandline plotnft handling
* Fix typo
* Add -y flag to plotnft create
* pool_wallet: Restore from DB properly
* wallet: ignore bad pool configs
* Reduce memory
* pool_wallet: Add claim command
* pool_wallet: Set transaction records to confirmed
* wallet: Fix bug in transaction cache
* Formatting and remove log
* pool_wallet: CLI balance and improvements to plotnft_funcs.py
* pool_wallet: Simplify, and fix issue with double submission
* pool_wallet: Fix tests
* pool_wallet: Don't allow switching before relative lock height
* update gui
* change to 3000 mem
* Correct sense of -y flag for self-pooling
* cli: Display payout instructions for pool
* pool_wallet: Don't create massive transactions
* cli: Improvements to plotnft
* pool_wallet: Get correct pool state
* pool_wallet: Use last transaction block to prevent condition failure
* Add block height for current state
* Add outstanding unconfirmed transactions to pw_status
* Refine command line plotnft show pending transactions
* Fix tests by using the correct output from pw_status
* Try to fix windows build
* Print expected leave height
* label pool urls
* pool_wallet: Don't include pool 1.75 rewards in total
* wallet: Add RPC and CLI for deleting unconfirmed transactions for a wallet
* pool_wallet: If farming to a pool, show 0 balance in wallet
* pool_wallet: Show error message if invalid state, in CLI
* pool_wallet: Don't allow switching if there are pending unconfirmed transactions
* tests: Clean up pool test logging
* tests: Fix lint
* Changed the pool innerpuzzes (#6802)
* overload solutions for pool_innerpuz parameters
* Fix tests for reduced size puzzles
* deleted messy deprecated test
* Fix lint.
* fix bug where spend types were the wrong way around
* merge with richard's lint fix
* fix wallet bug
remove unnecessary signature
add defun-inline for clarity
* Swap to defun for absorb case
Use cons box for member innerpuz solution
* fix if statement for cons box p1
* remove unnecessary solution arg
* quick innerpuz fix to make tests pass
* Switch to key-value pairs
Undo cons box solution in pool_member inner puzzle
* fix singleton lifecycle test
* added some comments to calrify the meaning on "ps"
* lint fix
* reduce label size, search for label when reconstructing from solution
* no need to keep looping if `p` found
* lint fix
* Removed unecessary defun-inline and changed hyphens to underscores
* Changed created_coin_value_or_0 to an inline function
* Changed morph_condition to an inline function
* Added a comment for odd_cons_m113
* Rename output_odd and odd_output_found
* Add inline functions to document the lineage proof values
* Stager two rewrite
* Added an ASSER_MY_AMOUNT to p2_singleton_or_delayed
* Extract truth functionality to singleton_truths.clib
* Fix tree hashes
* Changed truths to a struct rather than a list.
* fix test_singletons
update did_innerpuz
* recompile did_innerpuz
* fix a log error
* Renamed variable and factored out code per @richardkiss
* lint fix
* switch launcher extra_data to key_value pairs
* fix parsing of new format of extra_data in launcher solution
* fix broken test for new launcher solution format
* remove bare raise
Co-authored-by: Richard Kiss <him@richardkiss.com>
Co-authored-by: Matt Hauff <quexington@gmail.com>
* Also add passed-in coin spends when processing new blocks in reconsider_peak (#6898)
Co-authored-by: Adam Kelly <aqk>
* Moved debug_spend_bundle and added it to the SpendBundle object (#6840)
* Moved debug_spend_bundle and added it to the SpendBundle object
* Remove problematic typing
* Add testnet config
* wallet: Memory would get corrupted if there was an error (#6902)
* wallet: Memory would get corrupted if there was an error
* wallet: Use block_record
* wallet: Add records in a full fork too
* wallet: remove unnecessary arguments in CC and DID
* add to cache, revert if transaction fails
Co-authored-by: Yostra <straya@chia.net>
* Improve comment
* pool_wallet: Fix driver bug
* wallet: Fix memory corruption
* gui: Update to latest
* Increase memory size
* tests: Add test for absorbing from pool
* small fix in solution_to_extra_data
* Fixed incorrect function name
* pooling: Fix EOS handling in full node
* [pools.testnet9]add post /partial and /farmer header (#6957)
* Update farmer.py
add post header
* Update farmer_api.py
add post header
* Update chia/farmer/farmer.py
Co-authored-by: dustinface <35775977+xdustinface@users.noreply.github.com>
* Update chia/farmer/farmer_api.py
Co-authored-by: dustinface <35775977+xdustinface@users.noreply.github.com>
Co-authored-by: Mariano Sorgente <3069354+mariano54@users.noreply.github.com>
Co-authored-by: dustinface <35775977+xdustinface@users.noreply.github.com>
* Fix lint and cleanup farmer.py
* farmer: Fix linting issues (#7010)
* Handle the case of incorrectly formatted PoolState data returned from inner singleton
* wallet: Resubmit transaction if not successful, rename to new_transaction_block_callback (#7008)
* Fix lint in pool_puzzles
* pooling: Fix owner private key lookup, and remove unnecessary argument
* pooling: Clear target state on `delete_unconfirmed_transactions`
* Lint
* Fix non-deterministic test
* Slight cleanup clvm driver code (#7028)
* Return None when a deserialized CLVM structure does not fit the expected format of var-value pair for singleton data
* lint
Co-authored-by: Adam Kelly <aqk>
* Revert "Add testnet config"
This reverts commit 98124427241b8a268fbab43ac116887c89e9974f.
Co-authored-by: matt <matt@chia.net>
Co-authored-by: Adam Kelly <aqk@aqk.im>
Co-authored-by: Mariano Sorgente <sorgente711@gmail.com>
Co-authored-by: Matt Hauff <quexington@gmail.com>
Co-authored-by: Mariano Sorgente <3069354+mariano54@users.noreply.github.com>
Co-authored-by: Adam <aqk@Adams-MacBook-Pro.local>
Co-authored-by: Adam Kelly <aqk>
Co-authored-by: Richard Kiss <him@richardkiss.com>
Co-authored-by: xdustinface <xdustinfacex@gmail.com>
Co-authored-by: almog <almogdepaz@gmail.com>
Co-authored-by: dustinface <35775977+xdustinface@users.noreply.github.com>
Co-authored-by: Earle Lowe <e.lowe@chia.net>
Co-authored-by: arvidn <arvid@libtorrent.org>
Co-authored-by: willi123yao <willi123yao@gmail.com>
Co-authored-by: arty <art.yerkes@gmail.com>
Co-authored-by: William Blanke <wjb98672@gmail.com>
Co-authored-by: matt-o-how <48453825+matt-o-how@users.noreply.github.com>
Co-authored-by: Chris Marslender <chrismarslender@gmail.com>
Co-authored-by: Yostra <straya@chia.net>
Co-authored-by: DouCrazy <43004977+lpf763827726@users.noreply.github.com>
2021-06-30 00:21:25 +03:00
|
|
|
print(f"Added private key with public key fingerprint {fingerprint}")
|
2021-03-26 06:25:45 +03:00
|
|
|
|
|
|
|
except ValueError as e:
|
|
|
|
print(e)
|
2021-05-11 08:05:45 +03:00
|
|
|
return None
|
2021-03-26 06:25:45 +03:00
|
|
|
|
|
|
|
|
2021-08-04 22:46:55 +03:00
|
|
|
@unlocks_keyring(use_passphrase_cache=True)
|
2021-04-18 21:07:02 +03:00
|
|
|
def show_all_keys(show_mnemonic: bool):
|
2021-03-26 06:25:45 +03:00
|
|
|
"""
|
|
|
|
Prints all keys and mnemonics (if available).
|
|
|
|
"""
|
|
|
|
root_path = DEFAULT_ROOT_PATH
|
|
|
|
config = load_config(root_path, "config.yaml")
|
2022-01-26 07:42:16 +03:00
|
|
|
private_keys = Keychain().get_all_private_keys()
|
2021-03-26 06:25:45 +03:00
|
|
|
selected = config["selected_network"]
|
|
|
|
prefix = config["network_overrides"]["config"][selected]["address_prefix"]
|
|
|
|
if len(private_keys) == 0:
|
|
|
|
print("There are no saved private keys")
|
2021-05-11 08:05:45 +03:00
|
|
|
return None
|
2021-09-22 20:44:29 +03:00
|
|
|
msg = "Showing all public keys derived from your master seed and private key:"
|
2021-04-28 22:45:37 +03:00
|
|
|
if show_mnemonic:
|
|
|
|
msg = "Showing all public and private keys"
|
|
|
|
print(msg)
|
2021-03-26 06:25:45 +03:00
|
|
|
for sk, seed in private_keys:
|
|
|
|
print("")
|
|
|
|
print("Fingerprint:", sk.get_g1().get_fingerprint())
|
|
|
|
print("Master public key (m):", sk.get_g1())
|
|
|
|
print(
|
2021-04-18 09:59:30 +03:00
|
|
|
"Farmer public key (m/12381/8444/0/0):",
|
2021-03-26 06:25:45 +03:00
|
|
|
master_sk_to_farmer_sk(sk).get_g1(),
|
|
|
|
)
|
|
|
|
print("Pool public key (m/12381/8444/1/0):", master_sk_to_pool_sk(sk).get_g1())
|
|
|
|
print(
|
|
|
|
"First wallet address:",
|
|
|
|
encode_puzzle_hash(create_puzzlehash_for_pk(master_sk_to_wallet_sk(sk, uint32(0)).get_g1()), prefix),
|
|
|
|
)
|
|
|
|
assert seed is not None
|
2021-04-18 21:07:02 +03:00
|
|
|
if show_mnemonic:
|
2021-04-28 22:45:37 +03:00
|
|
|
print("Master private key (m):", bytes(sk).hex())
|
2021-05-03 22:55:00 +03:00
|
|
|
print(
|
|
|
|
"First wallet secret key (m/12381/8444/2/0):",
|
2021-05-24 23:26:21 +03:00
|
|
|
master_sk_to_wallet_sk(sk, uint32(0)),
|
2021-05-03 22:55:00 +03:00
|
|
|
)
|
2021-04-18 21:07:02 +03:00
|
|
|
mnemonic = bytes_to_mnemonic(seed)
|
|
|
|
print(" Mnemonic seed (24 secret words):")
|
|
|
|
print(mnemonic)
|
2021-03-26 06:25:45 +03:00
|
|
|
|
|
|
|
|
2021-08-04 22:46:55 +03:00
|
|
|
@unlocks_keyring(use_passphrase_cache=True)
|
2021-03-26 06:25:45 +03:00
|
|
|
def delete(fingerprint: int):
|
|
|
|
"""
|
|
|
|
Delete a key by its public key fingerprint (which is an integer).
|
|
|
|
"""
|
|
|
|
print(f"Deleting private_key with fingerprint {fingerprint}")
|
2022-01-26 07:42:16 +03:00
|
|
|
Keychain().delete_key_by_fingerprint(fingerprint)
|
2021-03-26 06:25:45 +03:00
|
|
|
|
|
|
|
|
2022-01-26 07:42:16 +03:00
|
|
|
def derive_sk_from_hd_path(master_sk: PrivateKey, hd_path_root: str) -> Tuple[PrivateKey, str]:
|
|
|
|
"""
|
|
|
|
Derive a private key from the provided HD path. Takes a master key and HD path as input,
|
|
|
|
and returns the derived key and the HD path that was used to derive it.
|
|
|
|
"""
|
2021-03-26 06:25:45 +03:00
|
|
|
|
2022-01-26 07:42:16 +03:00
|
|
|
from chia.wallet.derive_keys import _derive_path, _derive_path_unhardened
|
|
|
|
|
|
|
|
class DerivationType(Enum):
|
|
|
|
NONOBSERVER = 0
|
|
|
|
OBSERVER = 1
|
|
|
|
|
|
|
|
path: List[str] = hd_path_root.split("/")
|
|
|
|
if len(path) == 0 or path[0] != "m":
|
|
|
|
raise ValueError("Invalid HD path. Must start with 'm'")
|
|
|
|
|
|
|
|
path = path[1:] # Skip "m"
|
|
|
|
|
2022-01-29 02:34:39 +03:00
|
|
|
if len(path) > 0 and path[-1] == "": # remove trailing slash
|
2022-01-26 07:42:16 +03:00
|
|
|
path = path[:-1]
|
|
|
|
|
|
|
|
index_and_derivation_types: List[Tuple[int, DerivationType]] = []
|
|
|
|
|
|
|
|
# Validate path
|
|
|
|
for current_index_str in path:
|
|
|
|
if len(current_index_str) == 0:
|
|
|
|
raise ValueError("Invalid HD path. Empty index")
|
|
|
|
|
|
|
|
non_observer: bool = current_index_str[-1] == "n"
|
|
|
|
current_index: int = int(current_index_str[:-1]) if non_observer else int(current_index_str)
|
|
|
|
|
|
|
|
index_and_derivation_types.append(
|
|
|
|
(current_index, DerivationType.NONOBSERVER if non_observer else DerivationType.OBSERVER)
|
|
|
|
)
|
|
|
|
|
|
|
|
current_sk: PrivateKey = master_sk
|
|
|
|
|
|
|
|
# Derive keys along the path
|
|
|
|
for (current_index, derivation_type) in index_and_derivation_types:
|
|
|
|
if derivation_type == DerivationType.NONOBSERVER:
|
|
|
|
current_sk = _derive_path(current_sk, [current_index])
|
|
|
|
elif derivation_type == DerivationType.OBSERVER:
|
|
|
|
current_sk = _derive_path_unhardened(current_sk, [current_index])
|
|
|
|
else:
|
|
|
|
raise ValueError(f"Unhandled derivation type: {derivation_type}")
|
|
|
|
|
|
|
|
return (current_sk, "m/" + "/".join(path) + "/")
|
|
|
|
|
|
|
|
|
|
|
|
def sign(message: str, private_key: PrivateKey, hd_path: str, as_bytes: bool):
|
|
|
|
sk: PrivateKey = derive_sk_from_hd_path(private_key, hd_path)[0]
|
|
|
|
data = bytes.fromhex(message) if as_bytes else bytes(message, "utf-8")
|
|
|
|
print("Public key:", sk.get_g1())
|
|
|
|
print("Signature:", AugSchemeMPL.sign(sk, data))
|
2021-03-26 06:25:45 +03:00
|
|
|
|
|
|
|
|
|
|
|
def verify(message: str, public_key: str, signature: str):
|
|
|
|
messageBytes = bytes(message, "utf-8")
|
|
|
|
public_key = G1Element.from_bytes(bytes.fromhex(public_key))
|
|
|
|
signature = G2Element.from_bytes(bytes.fromhex(signature))
|
|
|
|
print(AugSchemeMPL.verify(public_key, messageBytes, signature))
|
2022-01-26 07:42:16 +03:00
|
|
|
|
|
|
|
|
2022-02-05 03:43:51 +03:00
|
|
|
def migrate_keys():
|
|
|
|
from chia.util.keyring_wrapper import KeyringWrapper
|
|
|
|
from chia.util.misc import prompt_yes_no
|
|
|
|
|
|
|
|
# Check if the keyring needs a full migration (i.e. if it's using the old keyring)
|
|
|
|
if Keychain.needs_migration():
|
|
|
|
KeyringWrapper.get_shared_instance().migrate_legacy_keyring_interactive()
|
|
|
|
else:
|
|
|
|
keys_to_migrate, legacy_keyring = Keychain.get_keys_needing_migration()
|
|
|
|
if len(keys_to_migrate) > 0 and legacy_keyring is not None:
|
|
|
|
print(f"Found {len(keys_to_migrate)} key(s) that need migration:")
|
|
|
|
for key, _ in keys_to_migrate:
|
|
|
|
print(f"Fingerprint: {key.get_g1().get_fingerprint()}")
|
|
|
|
|
|
|
|
print()
|
|
|
|
response = prompt_yes_no("Migrate these keys? (y/n) ")
|
|
|
|
if response:
|
|
|
|
keychain = Keychain()
|
|
|
|
for sk, seed_bytes in keys_to_migrate:
|
|
|
|
mnemonic = bytes_to_mnemonic(seed_bytes)
|
|
|
|
keychain.add_private_key(mnemonic, "")
|
|
|
|
fingerprint = sk.get_g1().get_fingerprint()
|
|
|
|
print(f"Added private key with public key fingerprint {fingerprint}")
|
|
|
|
|
|
|
|
print(f"Migrated {len(keys_to_migrate)} key(s)")
|
|
|
|
|
|
|
|
print("Verifying migration results...", end="")
|
|
|
|
if Keychain.verify_keys_present(keys_to_migrate):
|
|
|
|
print(" Verified")
|
|
|
|
print()
|
|
|
|
response = prompt_yes_no("Remove key(s) from old keyring? (y/n) ")
|
|
|
|
if response:
|
|
|
|
legacy_keyring.delete_keys(keys_to_migrate)
|
|
|
|
print(f"Removed {len(keys_to_migrate)} key(s) from old keyring")
|
|
|
|
print("Migration complete")
|
|
|
|
else:
|
|
|
|
print(" Failed")
|
|
|
|
sys.exit(1)
|
|
|
|
else:
|
|
|
|
print("No keys need migration")
|
|
|
|
|
|
|
|
Keychain.mark_migration_checked_for_current_version()
|
|
|
|
|
|
|
|
|
2022-01-26 07:42:16 +03:00
|
|
|
def _clear_line_part(n: int):
|
|
|
|
# Move backward, overwrite with spaces, then move backward again
|
|
|
|
sys.stdout.write("\b" * n)
|
|
|
|
sys.stdout.write(" " * n)
|
|
|
|
sys.stdout.write("\b" * n)
|
|
|
|
|
|
|
|
|
|
|
|
def _search_derived(
|
|
|
|
current_sk: PrivateKey,
|
|
|
|
search_terms: Tuple[str, ...],
|
|
|
|
path: str,
|
|
|
|
path_indices: Optional[List[int]],
|
|
|
|
limit: int,
|
|
|
|
non_observer_derivation: bool,
|
|
|
|
show_progress: bool,
|
|
|
|
search_public_key: bool,
|
|
|
|
search_private_key: bool,
|
|
|
|
search_address: bool,
|
|
|
|
) -> List[str]: # Return a subset of search_terms that were found
|
|
|
|
"""
|
|
|
|
Performs a shallow search of keys derived from the current sk for items matching
|
|
|
|
the provided search terms.
|
|
|
|
"""
|
|
|
|
|
|
|
|
from chia.wallet.derive_keys import _derive_path, _derive_path_unhardened
|
|
|
|
|
|
|
|
class DerivedSearchResultType(Enum):
|
|
|
|
PUBLIC_KEY = "public key"
|
|
|
|
PRIVATE_KEY = "private key"
|
|
|
|
WALLET_ADDRESS = "wallet address"
|
|
|
|
|
|
|
|
remaining_search_terms: Dict[str, None] = dict.fromkeys(search_terms)
|
|
|
|
current_path: str = path
|
|
|
|
current_path_indices: List[int] = path_indices if path_indices is not None else []
|
|
|
|
found_search_terms: List[str] = []
|
|
|
|
|
|
|
|
for index in range(limit):
|
|
|
|
found_items: List[Tuple[str, str, DerivedSearchResultType]] = []
|
|
|
|
printed_match: bool = False
|
|
|
|
current_index_str = str(index) + ("n" if non_observer_derivation else "")
|
|
|
|
current_path += f"{current_index_str}"
|
|
|
|
current_path_indices.append(index)
|
|
|
|
if show_progress:
|
|
|
|
# Output just the current index e.g. "25" or "25n"
|
|
|
|
sys.stdout.write(f"{current_index_str}")
|
|
|
|
sys.stdout.flush()
|
|
|
|
|
|
|
|
# Derive the private key
|
|
|
|
if non_observer_derivation:
|
|
|
|
child_sk = _derive_path(current_sk, current_path_indices)
|
|
|
|
else:
|
|
|
|
child_sk = _derive_path_unhardened(current_sk, current_path_indices)
|
|
|
|
|
|
|
|
child_pk: Optional[G1Element] = None
|
|
|
|
|
|
|
|
# Public key is needed for searching against wallet addresses or public keys
|
|
|
|
if search_public_key or search_address:
|
|
|
|
child_pk = child_sk.get_g1()
|
|
|
|
|
|
|
|
address: Optional[str] = None
|
|
|
|
|
|
|
|
if search_address:
|
|
|
|
# Generate a wallet address using the standard p2_delegated_puzzle_or_hidden_puzzle puzzle
|
|
|
|
# TODO: consider generating addresses using other puzzles
|
|
|
|
address = encode_puzzle_hash(create_puzzlehash_for_pk(child_pk), "xch")
|
|
|
|
|
|
|
|
for term in remaining_search_terms:
|
|
|
|
found_item: Any = None
|
|
|
|
found_item_type: Optional[DerivedSearchResultType] = None
|
|
|
|
|
|
|
|
if search_private_key and term in str(child_sk):
|
|
|
|
found_item = private_key_string_repr(child_sk)
|
|
|
|
found_item_type = DerivedSearchResultType.PRIVATE_KEY
|
|
|
|
elif search_public_key and child_pk is not None and term in str(child_pk):
|
|
|
|
found_item = child_pk
|
|
|
|
found_item_type = DerivedSearchResultType.PUBLIC_KEY
|
|
|
|
elif search_address and address is not None and term in address:
|
|
|
|
found_item = address
|
|
|
|
found_item_type = DerivedSearchResultType.WALLET_ADDRESS
|
|
|
|
|
|
|
|
if found_item is not None and found_item_type is not None:
|
|
|
|
found_items.append((term, found_item, found_item_type))
|
|
|
|
|
|
|
|
if len(found_items) > 0 and show_progress:
|
|
|
|
print()
|
|
|
|
|
|
|
|
for (term, found_item, found_item_type) in found_items:
|
|
|
|
# Update remaining_search_terms and found_search_terms
|
|
|
|
del remaining_search_terms[term]
|
|
|
|
found_search_terms.append(term)
|
|
|
|
|
|
|
|
print(
|
|
|
|
f"Found {found_item_type.value}: {found_item} (HD path: {current_path})"
|
|
|
|
) # lgtm [py/clear-text-logging-sensitive-data]
|
|
|
|
|
|
|
|
printed_match = True
|
|
|
|
|
|
|
|
if len(remaining_search_terms) == 0:
|
|
|
|
break
|
|
|
|
|
|
|
|
# Remove the last index from the path
|
|
|
|
current_path = current_path[: -len(str(current_index_str))]
|
|
|
|
current_path_indices = current_path_indices[:-1]
|
|
|
|
|
|
|
|
if show_progress:
|
|
|
|
if printed_match:
|
|
|
|
# Write the path (without current_index_str) since we printed out a match
|
|
|
|
# e.g. m/12381/8444/2/
|
|
|
|
sys.stdout.write(f"{current_path}") # lgtm [py/clear-text-logging-sensitive-data]
|
|
|
|
# Remove the last index from the output
|
|
|
|
else:
|
|
|
|
_clear_line_part(len(current_index_str))
|
|
|
|
|
|
|
|
return found_search_terms
|
|
|
|
|
|
|
|
|
|
|
|
def search_derive(
|
|
|
|
private_key: Optional[PrivateKey],
|
|
|
|
search_terms: Tuple[str, ...],
|
|
|
|
limit: int,
|
|
|
|
non_observer_derivation: bool,
|
|
|
|
show_progress: bool,
|
|
|
|
search_types: Tuple[str, ...],
|
|
|
|
derive_from_hd_path: Optional[str],
|
|
|
|
) -> bool:
|
|
|
|
"""
|
|
|
|
Searches for items derived from the provided private key, or if not specified,
|
|
|
|
search each private key in the keyring.
|
|
|
|
"""
|
|
|
|
|
|
|
|
from time import perf_counter
|
|
|
|
|
|
|
|
start_time = perf_counter()
|
|
|
|
private_keys: List[PrivateKey]
|
|
|
|
remaining_search_terms: Dict[str, None] = dict.fromkeys(search_terms) # poor man's ordered set
|
|
|
|
search_address = "address" in search_types
|
|
|
|
search_public_key = "public_key" in search_types
|
|
|
|
search_private_key = "private_key" in search_types
|
|
|
|
|
|
|
|
if "all" in search_types:
|
|
|
|
search_address = True
|
|
|
|
search_public_key = True
|
|
|
|
search_private_key = True
|
|
|
|
|
|
|
|
if private_key is None:
|
|
|
|
private_keys = [sk for sk, _ in Keychain().get_all_private_keys()]
|
|
|
|
else:
|
|
|
|
private_keys = [private_key]
|
|
|
|
|
|
|
|
for sk in private_keys:
|
|
|
|
current_path: str = ""
|
|
|
|
found_terms: List[str] = []
|
|
|
|
|
|
|
|
if show_progress:
|
|
|
|
print(f"Searching keys derived from: {sk.get_g1().get_fingerprint()}")
|
|
|
|
|
|
|
|
# Derive from the provided HD path
|
|
|
|
if derive_from_hd_path is not None:
|
|
|
|
derivation_root_sk, hd_path_root = derive_sk_from_hd_path(sk, derive_from_hd_path)
|
|
|
|
|
|
|
|
if show_progress:
|
|
|
|
sys.stdout.write(hd_path_root)
|
|
|
|
|
|
|
|
# Shallow search under hd_path_root
|
|
|
|
found_terms = _search_derived(
|
|
|
|
derivation_root_sk,
|
|
|
|
tuple(remaining_search_terms.keys()),
|
|
|
|
hd_path_root,
|
|
|
|
None,
|
|
|
|
limit,
|
|
|
|
non_observer_derivation,
|
|
|
|
show_progress,
|
|
|
|
search_public_key,
|
|
|
|
search_private_key,
|
|
|
|
search_address,
|
|
|
|
)
|
|
|
|
|
|
|
|
# Update remaining_search_terms
|
|
|
|
for term in found_terms:
|
|
|
|
del remaining_search_terms[term]
|
|
|
|
|
|
|
|
if len(remaining_search_terms) == 0:
|
|
|
|
# Found everything we were looking for
|
|
|
|
break
|
|
|
|
|
|
|
|
current_path = hd_path_root
|
|
|
|
# Otherwise derive from well-known derivation paths
|
|
|
|
else:
|
|
|
|
current_path_indices: List[int] = [12381, 8444]
|
|
|
|
path_root: str = "m/"
|
|
|
|
for i in [12381, 8444]:
|
|
|
|
path_root += f"{i}{'n' if non_observer_derivation else ''}/"
|
|
|
|
|
|
|
|
if show_progress:
|
|
|
|
# Print the path root (without last index)
|
|
|
|
# e.g. m/12381/8444/
|
|
|
|
sys.stdout.write(path_root)
|
|
|
|
|
|
|
|
# 7 account levels for derived keys (0-6):
|
|
|
|
# 0 = farmer, 1 = pool, 2 = wallet, 3 = local, 4 = backup key, 5 = singleton, 6 = pooling authentication
|
|
|
|
for account in range(7):
|
|
|
|
account_str = str(account) + ("n" if non_observer_derivation else "")
|
|
|
|
current_path = path_root + f"{account_str}/"
|
|
|
|
current_path_indices.append(account)
|
|
|
|
if show_progress:
|
|
|
|
# Print the current path index
|
|
|
|
# e.g. 2/ (example full output: m/12381/8444/2/)
|
|
|
|
sys.stdout.write(f"{account_str}/") # lgtm [py/clear-text-logging-sensitive-data]
|
|
|
|
|
|
|
|
found_terms = _search_derived(
|
|
|
|
sk,
|
|
|
|
tuple(remaining_search_terms.keys()),
|
|
|
|
current_path,
|
|
|
|
list(current_path_indices), # copy
|
|
|
|
limit,
|
|
|
|
non_observer_derivation,
|
|
|
|
show_progress,
|
|
|
|
search_public_key,
|
|
|
|
search_private_key,
|
|
|
|
search_address,
|
|
|
|
)
|
|
|
|
|
|
|
|
# Update remaining_search_terms
|
|
|
|
for found_term in found_terms:
|
|
|
|
del remaining_search_terms[found_term]
|
|
|
|
|
|
|
|
if len(remaining_search_terms) == 0:
|
|
|
|
# Found everything we were looking for
|
|
|
|
break
|
|
|
|
|
|
|
|
if show_progress:
|
|
|
|
# +1 to remove the trailing slash
|
|
|
|
_clear_line_part(1 + len(str(account_str)))
|
|
|
|
|
|
|
|
current_path_indices = current_path_indices[:-1]
|
|
|
|
|
|
|
|
if len(remaining_search_terms) == 0:
|
|
|
|
# Found everything we were looking for
|
|
|
|
break
|
|
|
|
|
|
|
|
if show_progress:
|
|
|
|
# +1 to remove the trailing slash
|
|
|
|
_clear_line_part(1 + len(current_path))
|
|
|
|
sys.stdout.flush()
|
|
|
|
|
|
|
|
end_time = perf_counter()
|
|
|
|
if len(remaining_search_terms) > 0:
|
|
|
|
for term in remaining_search_terms:
|
|
|
|
print(f"Could not find '{term}'")
|
|
|
|
|
|
|
|
if show_progress:
|
|
|
|
print()
|
|
|
|
print(f"Search completed in {end_time - start_time} seconds")
|
|
|
|
|
|
|
|
return len(remaining_search_terms) == 0
|
|
|
|
|
|
|
|
|
|
|
|
def derive_wallet_address(
|
|
|
|
root_path: Path,
|
|
|
|
private_key: PrivateKey,
|
|
|
|
index: int,
|
|
|
|
count: int,
|
|
|
|
prefix: Optional[str],
|
|
|
|
non_observer_derivation: bool,
|
|
|
|
show_hd_path: bool,
|
|
|
|
):
|
|
|
|
"""
|
|
|
|
Generate wallet addresses using keys derived from the provided private key.
|
|
|
|
"""
|
|
|
|
|
|
|
|
if prefix is None:
|
|
|
|
config: Dict = load_config(root_path, "config.yaml")
|
|
|
|
selected: str = config["selected_network"]
|
|
|
|
prefix = config["network_overrides"]["config"][selected]["address_prefix"]
|
|
|
|
path_indices: List[int] = [12381, 8444, 2]
|
|
|
|
wallet_hd_path_root: str = "m/"
|
|
|
|
for i in path_indices:
|
|
|
|
wallet_hd_path_root += f"{i}{'n' if non_observer_derivation else ''}/"
|
|
|
|
for i in range(index, index + count):
|
|
|
|
if non_observer_derivation:
|
|
|
|
sk = master_sk_to_wallet_sk(private_key, uint32(i))
|
|
|
|
else:
|
|
|
|
sk = master_sk_to_wallet_sk_unhardened(private_key, uint32(i))
|
|
|
|
# Generate a wallet address using the standard p2_delegated_puzzle_or_hidden_puzzle puzzle
|
|
|
|
# TODO: consider generating addresses using other puzzles
|
|
|
|
address = encode_puzzle_hash(create_puzzlehash_for_pk(sk.get_g1()), prefix)
|
|
|
|
if show_hd_path:
|
|
|
|
print(
|
|
|
|
f"Wallet address {i} "
|
|
|
|
f"({wallet_hd_path_root + str(i) + ('n' if non_observer_derivation else '')}): {address}"
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
print(f"Wallet address {i}: {address}")
|
|
|
|
|
|
|
|
|
|
|
|
def private_key_string_repr(private_key: PrivateKey):
|
|
|
|
"""Print a PrivateKey in a human-readable formats"""
|
|
|
|
|
|
|
|
s: str = str(private_key)
|
|
|
|
return s[len("<PrivateKey ") : s.rfind(">")] if s.startswith("<PrivateKey ") else s
|
|
|
|
|
|
|
|
|
|
|
|
def derive_child_key(
|
|
|
|
master_sk: PrivateKey,
|
|
|
|
key_type: Optional[str],
|
|
|
|
derive_from_hd_path: Optional[str],
|
|
|
|
index: int,
|
|
|
|
count: int,
|
|
|
|
non_observer_derivation: bool,
|
|
|
|
show_private_keys: bool,
|
|
|
|
show_hd_path: bool,
|
|
|
|
):
|
|
|
|
"""
|
|
|
|
Derive child keys from the provided master key.
|
|
|
|
"""
|
|
|
|
|
|
|
|
from chia.wallet.derive_keys import _derive_path, _derive_path_unhardened
|
|
|
|
|
|
|
|
derivation_root_sk: Optional[PrivateKey] = None
|
|
|
|
hd_path_root: Optional[str] = None
|
|
|
|
current_sk: Optional[PrivateKey] = None
|
|
|
|
|
|
|
|
# Key type was specified
|
|
|
|
if key_type is not None:
|
|
|
|
path_indices: List[int] = [12381, 8444]
|
|
|
|
path_indices.append(
|
|
|
|
{
|
|
|
|
"farmer": 0,
|
|
|
|
"pool": 1,
|
|
|
|
"wallet": 2,
|
|
|
|
"local": 3,
|
|
|
|
"backup": 4,
|
|
|
|
"singleton": 5,
|
|
|
|
"pool_auth": 6,
|
|
|
|
}[key_type]
|
|
|
|
)
|
|
|
|
|
|
|
|
if non_observer_derivation:
|
|
|
|
current_sk = _derive_path(master_sk, path_indices)
|
|
|
|
else:
|
|
|
|
current_sk = _derive_path_unhardened(master_sk, path_indices)
|
|
|
|
|
|
|
|
derivation_root_sk = current_sk
|
|
|
|
hd_path_root = "m/"
|
|
|
|
for i in path_indices:
|
|
|
|
hd_path_root += f"{i}{'n' if non_observer_derivation else ''}/"
|
|
|
|
# Arbitrary HD path was specified
|
|
|
|
elif derive_from_hd_path is not None:
|
|
|
|
derivation_root_sk, hd_path_root = derive_sk_from_hd_path(master_sk, derive_from_hd_path)
|
|
|
|
|
|
|
|
# Derive child keys from derivation_root_sk
|
|
|
|
if derivation_root_sk is not None and hd_path_root is not None:
|
|
|
|
for i in range(index, index + count):
|
|
|
|
if non_observer_derivation:
|
|
|
|
sk = _derive_path(derivation_root_sk, [i])
|
|
|
|
else:
|
|
|
|
sk = _derive_path_unhardened(derivation_root_sk, [i])
|
|
|
|
hd_path: str = (
|
|
|
|
" (" + hd_path_root + str(i) + ("n" if non_observer_derivation else "") + ")" if show_hd_path else ""
|
|
|
|
)
|
|
|
|
key_type_str: Optional[str]
|
|
|
|
|
|
|
|
if key_type is not None:
|
|
|
|
key_type_str = key_type.capitalize()
|
|
|
|
else:
|
|
|
|
key_type_str = "Non-Observer" if non_observer_derivation else "Observer"
|
|
|
|
|
|
|
|
print(f"{key_type_str} public key {i}{hd_path}: {sk.get_g1()}")
|
|
|
|
if show_private_keys:
|
|
|
|
print(f"{key_type_str} private key {i}{hd_path}: {private_key_string_repr(sk)}")
|
|
|
|
|
|
|
|
|
|
|
|
@unlocks_keyring(use_passphrase_cache=True)
|
|
|
|
def private_key_for_fingerprint(fingerprint: int) -> Optional[PrivateKey]:
|
|
|
|
private_keys = Keychain().get_all_private_keys()
|
|
|
|
|
|
|
|
for sk, _ in private_keys:
|
|
|
|
if sk.get_g1().get_fingerprint() == fingerprint:
|
|
|
|
return sk
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
def get_private_key_with_fingerprint_or_prompt(fingerprint: Optional[int]):
|
|
|
|
"""
|
|
|
|
Get a private key with the specified fingerprint. If fingerprint is not
|
|
|
|
specified, prompt the user to select a key.
|
|
|
|
"""
|
|
|
|
|
|
|
|
# Return the private key matching the specified fingerprint
|
|
|
|
if fingerprint is not None:
|
|
|
|
return private_key_for_fingerprint(fingerprint)
|
|
|
|
|
|
|
|
fingerprints: List[int] = [pk.get_fingerprint() for pk in Keychain().get_all_public_keys()]
|
|
|
|
while True:
|
|
|
|
print("Choose key:")
|
|
|
|
for i, fp in enumerate(fingerprints):
|
|
|
|
print(f"{i+1}) {fp}")
|
|
|
|
val = None
|
|
|
|
while val is None:
|
|
|
|
val = input("Enter a number to pick or q to quit: ")
|
|
|
|
if val == "q":
|
|
|
|
return None
|
|
|
|
if not val.isdigit():
|
|
|
|
val = None
|
|
|
|
else:
|
|
|
|
index = int(val) - 1
|
|
|
|
if index >= len(fingerprints):
|
|
|
|
print("Invalid value")
|
|
|
|
val = None
|
|
|
|
continue
|
|
|
|
else:
|
|
|
|
return private_key_for_fingerprint(fingerprints[index])
|
|
|
|
|
|
|
|
|
|
|
|
def private_key_from_mnemonic_seed_file(filename: Path) -> PrivateKey:
|
|
|
|
"""
|
|
|
|
Create a private key from a mnemonic seed file.
|
|
|
|
"""
|
|
|
|
|
|
|
|
mnemonic = filename.read_text().rstrip()
|
|
|
|
seed = mnemonic_to_seed(mnemonic, "")
|
|
|
|
return AugSchemeMPL.key_gen(seed)
|
|
|
|
|
|
|
|
|
|
|
|
def resolve_derivation_master_key(fingerprint_or_filename: Optional[Union[int, str, Path]]) -> PrivateKey:
|
|
|
|
"""
|
|
|
|
Given a key fingerprint of file containing a mnemonic seed, return the private key.
|
|
|
|
"""
|
|
|
|
|
|
|
|
if fingerprint_or_filename is not None and (
|
|
|
|
isinstance(fingerprint_or_filename, str) or isinstance(fingerprint_or_filename, Path)
|
|
|
|
):
|
|
|
|
return private_key_from_mnemonic_seed_file(Path(os.fspath(fingerprint_or_filename)))
|
|
|
|
else:
|
|
|
|
return get_private_key_with_fingerprint_or_prompt(fingerprint_or_filename)
|