pyrxd.hd — BIP-32/39/44 HD wallets¶
- class pyrxd.hd.AddressRecord[source]¶
Bases:
objectAddressRecord(address: ‘str’, change: ‘int’, index: ‘int’, used: ‘bool’)
- __init__(address, change, index, used)¶
- class pyrxd.hd.HdWallet[source]¶
Bases:
objectBIP44 HD wallet for Radiant with gap-limit discovery and encrypted persistence.
- addresses¶
{path_key: AddressRecord}where path_key isf"{change}/{index}".- Type:
- __init__(_xprv, _seed, account=0, external_tip=0, internal_tip=0, addresses=<factory>)¶
- Parameters:
_xprv (Xprv)
_seed (SecretBytes)
account (int)
external_tip (int)
internal_tip (int)
addresses (dict[str, AddressRecord])
- Return type:
None
- build_send_max_tx(triples, to_address, *, fee_rate=10000)[source]¶
Sweep all triples to to_address minus fee. No change output.
- build_send_tx(triples, to_address, photons, *, fee_rate=10000, change_address=None)[source]¶
Build and sign a P2PKH transfer from HD UTXOs to to_address.
Pure offline operation. Mirrors
RxdWallet.build_send_tx()but accepts (utxo, address, privkey) triples so each input is signed by the correct HD-derived key.change_addressdefaults to the next unused internal index; callers can override (e.g. to keep change on the external chain for a single-address-style wallet).
- async collect_spendable(client)[source]¶
Return
(utxo, address, privkey)triples for every UTXO across known addresses.Address→key mapping is preserved so signing works correctly per UTXO. Falls back gracefully if any per-address fetch fails (the failed address contributes nothing rather than crashing the whole collection — the caller decides whether the resulting balance is enough).
- Parameters:
client (ElectrumXClient)
- Return type:
- classmethod from_mnemonic(mnemonic, passphrase='', account=0)[source]¶
Create a fresh wallet from a BIP39 mnemonic.
- async get_balance(client)[source]¶
Return total confirmed + unconfirmed satoshis across all known addresses.
Uses
ElectrumXClient.get_balanceper address. Callrefresh()first to ensure the address set is current.- Parameters:
client (ElectrumXClient)
- Return type:
- async get_utxos(client)[source]¶
Return all UTXOs across all known addresses.
- Parameters:
client (ElectrumXClient)
- Return type:
list[UtxoRecord]
- known_addresses(*, change=None)[source]¶
Return all known address records, optionally filtered by chain.
- Parameters:
change (int | None)
- Return type:
- classmethod load(path, mnemonic, passphrase='')[source]¶
Load a previously saved wallet from path.
The mnemonic is needed to derive the decryption key. Raises
FileNotFoundErrorif path does not exist — a typo’d path will not silently produce an empty wallet that subsequently overwrites a real wallet on save. Callers that explicitly want the create-on-missing behavior should useload_or_create().
- classmethod load_or_create(path, mnemonic, passphrase='', account=0)[source]¶
Load a wallet from path, or build a fresh one if the file is missing.
Spelled separately from
load()so the create-on-missing intent is explicit at the call site. A common safety failure with the old single-load API was that a typo in path would produce an empty wallet that subsequently overwrote the real wallet on save.
- next_receive_address()[source]¶
Return the first external (change=0) address with no recorded history.
- Return type:
- async refresh(client)[source]¶
Run BIP44 gap-limit scan on both external and internal chains.
Discovers which derived addresses have on-chain history. Stops after
_GAP_LIMIT(20) consecutive unused addresses per chain.Network errors (a transient ElectrumX outage, a server hangup mid-scan) propagate to the caller as
NetworkError— previously they were silently treated as “address unused”, which made a funded wallet look empty after a flaky lookup.Returns the count of newly discovered used addresses.
- Parameters:
client (ElectrumXClient)
- Return type:
- save(path)[source]¶
Encrypt and atomically save wallet state to path.
Atomicity & permissions¶
- Writes via mkstemp + fchmod(0o600) + fsync + os.replace, so:
The file is never visible at a wider mode than 0o600 — the mode is set on the fd before any bytes are written.
A crash mid-write cannot leave a half-encrypted blob in place — either the old file remains, or the new fully-fsynced file does.
Encryption¶
AES-256-GCM under a key derived from the BIP39 seed via scrypt with a per-file random salt. Tampering with the ciphertext breaks the GCM tag —
load()raises rather than returning attacker-shaped JSON.- Parameters:
path (Path)
- Return type:
None
- async send(client, to_address, photons, *, fee_rate=10000, change_address=None)[source]¶
Fetch UTXOs, build, sign, broadcast. Returns broadcast txid.
Raises
ValidationErroron bad inputs or insufficient funds,NetworkErroron RPC failure.
- async send_max(client, to_address, *, fee_rate=10000)[source]¶
Sweep all UTXOs to to_address minus fee. Returns broadcast txid.
- Parameters:
client (ElectrumXClient)
to_address (str)
fee_rate (int)
- Return type:
- addresses: dict[str, AddressRecord]¶
- class pyrxd.hd.WordList[source]¶
Bases:
objectBIP39 word list
- files: dict[str, str] = {'en': '/home/runner/work/pyrxd/pyrxd/src/pyrxd/hd/wordlist/english.txt', 'zh-cn': '/home/runner/work/pyrxd/pyrxd/src/pyrxd/hd/wordlist/chinese_simplified.txt'}¶
- path = '/home/runner/work/pyrxd/pyrxd/src/pyrxd/hd/wordlist'¶
- class pyrxd.hd.Xkey[source]¶
Bases:
object[ : 4] prefix [ 4: 5] depth [ 5: 9] parent public key fingerprint [ 9:13] child index [13:45] chain code [45:78] key (private/public)
- class pyrxd.hd.Xprv[source]¶
Bases:
Xkey- classmethod from_seed(seed, network=Network.MAINNET)[source]¶
derive master extended private key from seed
- pyrxd.hd.bip32_derive_xkeys_from_xkey(xkey, index_start, index_end, path='m/', change=0)[source]¶
Derive a range of extended keys from Xprv and Xpub keys using BIP32 path structure.
- pyrxd.hd.bip32_derive_xprv_from_mnemonic(mnemonic, lang='en', passphrase='', prefix='mnemonic', path='m/', network=Network.MAINNET)[source]¶
Derive the subtree root extended private key from mnemonic and path.
- pyrxd.hd.bip32_derive_xprvs_from_mnemonic(mnemonic, index_start, index_end, lang='en', passphrase='', prefix='mnemonic', path='m/', change=0, network=Network.MAINNET)[source]¶
Derive a range of extended keys from a nmemonic using BIP32 format
- pyrxd.hd.bip44_derive_xprv_from_mnemonic(mnemonic, lang='en', passphrase='', prefix='mnemonic', path="m/44'/236'/0'", network=Network.MAINNET)[source]¶
Derives extended private key using BIP44 format- it is a subset of BIP32. Inherits from BIP32, only changing the default path value.
- pyrxd.hd.bip44_derive_xprvs_from_mnemonic(mnemonic, index_start, index_end, lang='en', passphrase='', prefix='mnemonic', path="m/44'/236'/0'", change=0, network=Network.MAINNET)[source]¶
Derive a range of extended keys from a nmemonic using BIP44 format
- pyrxd.hd.ckd(xkey, path)[source]¶
ckd = “Child Key Derivation” derive an extended key according to path like “m/44’/0’/1’/0/10” (absolute) or “./0/10” (relative)
- pyrxd.hd.derive_xkeys_from_xkey(xkey, index_start, index_end, change=0)[source]¶
- [DEPRECATED] Use bip32_derive_xkeys_from_xkey instead.
This function name is kept for backward compatibility.
- pyrxd.hd.derive_xprv_from_mnemonic(mnemonic, lang='en', passphrase='', prefix='mnemonic', path="m/44'/236'/0'", network=Network.MAINNET)[source]¶
- [DEPRECATED] Use bip44_derive_xprv_from_mnemonic instead.
This function name is kept for backward compatibility.
- pyrxd.hd.derive_xprvs_from_mnemonic(mnemonic, index_start, index_end, lang='en', passphrase='', prefix='mnemonic', path="m/44'/236'/0'", change=0, network=Network.MAINNET)[source]¶
- [DEPRECATED] Use bip44_derive_xprvs_from_mnemonic instead.
This function name is kept for backward compatibility.