commands: add new cmd "getprivatekeyforpath" to export a WIF for a path
related: #6061
This commit is contained in:
@@ -449,7 +449,7 @@ class AddressSynchronizer(Logger):
|
||||
domain = set(domain)
|
||||
# 1. Get the history of each address in the domain, maintain the
|
||||
# delta of a tx as the sum of its deltas on domain addresses
|
||||
tx_deltas = defaultdict(int)
|
||||
tx_deltas = defaultdict(int) # type: Dict[str, Optional[int]]
|
||||
for addr in domain:
|
||||
h = self.get_address_history(addr)
|
||||
for tx_hash, height in h:
|
||||
|
||||
@@ -565,8 +565,8 @@ def is_segwit_script_type(txin_type: str) -> bool:
|
||||
return txin_type in ('p2wpkh', 'p2wpkh-p2sh', 'p2wsh', 'p2wsh-p2sh')
|
||||
|
||||
|
||||
def serialize_privkey(secret: bytes, compressed: bool, txin_type: str,
|
||||
internal_use: bool=False) -> str:
|
||||
def serialize_privkey(secret: bytes, compressed: bool, txin_type: str, *,
|
||||
internal_use: bool = False) -> str:
|
||||
# we only export secrets inside curve range
|
||||
secret = ecc.ECPrivkey.normalize_secret_bytes(secret)
|
||||
if internal_use:
|
||||
|
||||
@@ -414,6 +414,13 @@ class Commands:
|
||||
domain = address
|
||||
return [wallet.export_private_key(address, password) for address in domain]
|
||||
|
||||
@command('wp')
|
||||
async def getprivatekeyforpath(self, path, password=None, wallet: Abstract_Wallet = None):
|
||||
"""Get private key corresponding to derivation path (address index).
|
||||
'path' can be either a str such as "m/0/50", or a list of ints such as [0, 50].
|
||||
"""
|
||||
return wallet.export_private_key_for_path(path, password)
|
||||
|
||||
@command('w')
|
||||
async def ismine(self, address, wallet: Abstract_Wallet = None):
|
||||
"""Check if address is in wallet. Return true if and only address is in wallet"""
|
||||
|
||||
@@ -180,3 +180,17 @@ class TestCommandsTestnet(TestCaseForTestnet):
|
||||
}
|
||||
self.assertEqual("0200000000010139c5375fe9da7bd377c1783002b129f8c57d3e724d62f5eacb9739ca691a229d0100000000feffffff01301b0f0000000000160014ac0e2d229200bffb2167ed6fd196aef9d687d8bb0247304402206367fb2ddd723985f5f51e0f2435084c0a66f5c26f4403a75d3dd417b71a20450220545dc3637bcb49beedbbdf5063e05cad63be91af4f839886451c30ecd6edf1d20121021f110909ded653828a254515b58498a6bafc96799fb0851554463ed44ca7d9da00000000",
|
||||
cmds._run('serialize', (jsontx,)))
|
||||
|
||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||
def test_getprivatekeyforpath(self, mock_save_db):
|
||||
wallet = restore_wallet_from_text('north rent dawn bunker hamster invest wagon market romance pig either squeeze',
|
||||
gap_limit=2,
|
||||
path='if_this_exists_mocking_failed_648151893',
|
||||
config=self.config)['wallet']
|
||||
cmds = Commands(config=self.config)
|
||||
self.assertEqual("p2wpkh:cUzm7zPpWgLYeURgff4EsoMjhskCpsviBH4Y3aZcrBX8UJSRPjC2",
|
||||
cmds._run('getprivatekeyforpath', ([0, 10000],), wallet=wallet))
|
||||
self.assertEqual("p2wpkh:cUzm7zPpWgLYeURgff4EsoMjhskCpsviBH4Y3aZcrBX8UJSRPjC2",
|
||||
cmds._run('getprivatekeyforpath', ("m/0/10000",), wallet=wallet))
|
||||
self.assertEqual("p2wpkh:cQAj4WGf1socCPCJNMjXYCJ8Bs5JUAk5pbDr4ris44QdgAXcV24S",
|
||||
cmds._run('getprivatekeyforpath', ("m/5h/100000/88h/7",), wallet=wallet))
|
||||
|
||||
@@ -44,7 +44,7 @@ from abc import ABC, abstractmethod
|
||||
import itertools
|
||||
|
||||
from .i18n import _
|
||||
from .bip32 import BIP32Node, convert_bip32_intpath_to_strpath
|
||||
from .bip32 import BIP32Node, convert_bip32_intpath_to_strpath, convert_bip32_path_to_list_of_uint32
|
||||
from .crypto import sha256
|
||||
from .util import (NotEnoughFunds, UserCancelled, profiler,
|
||||
format_satoshis, format_fee_satoshis, NoDynamicFeeEstimates,
|
||||
@@ -462,7 +462,7 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
|
||||
"""Return script type of wallet address."""
|
||||
pass
|
||||
|
||||
def export_private_key(self, address, password) -> str:
|
||||
def export_private_key(self, address: str, password: Optional[str]) -> str:
|
||||
if self.is_watching_only():
|
||||
raise Exception(_("This is a watching-only wallet"))
|
||||
if not is_address(address):
|
||||
@@ -475,6 +475,9 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
|
||||
serialized_privkey = bitcoin.serialize_privkey(pk, compressed, txin_type)
|
||||
return serialized_privkey
|
||||
|
||||
def export_private_key_for_path(self, path: Union[Sequence[int], str], password: Optional[str]) -> str:
|
||||
raise Exception("this wallet is not deterministic")
|
||||
|
||||
@abstractmethod
|
||||
def get_public_keys(self, address: str) -> Sequence[str]:
|
||||
pass
|
||||
@@ -2201,6 +2204,13 @@ class Deterministic_Wallet(Abstract_Wallet):
|
||||
pubkeys = self.derive_pubkeys(for_change, n)
|
||||
return self.pubkeys_to_address(pubkeys)
|
||||
|
||||
def export_private_key_for_path(self, path: Union[Sequence[int], str], password: Optional[str]) -> str:
|
||||
if isinstance(path, str):
|
||||
path = convert_bip32_path_to_list_of_uint32(path)
|
||||
pk, compressed = self.keystore.get_private_key(path, password)
|
||||
txin_type = self.get_txin_type() # assumes no mixed-scripts in wallet
|
||||
return bitcoin.serialize_privkey(pk, compressed, txin_type)
|
||||
|
||||
def get_public_keys_with_deriv_info(self, address: str):
|
||||
der_suffix = self.get_address_index(address)
|
||||
der_suffix = [int(x) for x in der_suffix]
|
||||
@@ -2301,7 +2311,7 @@ class Deterministic_Wallet(Abstract_Wallet):
|
||||
def get_fingerprint(self):
|
||||
return self.get_master_public_key()
|
||||
|
||||
def get_txin_type(self, address):
|
||||
def get_txin_type(self, address=None):
|
||||
return self.txin_type
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user