modified playbook.md
This commit is contained in:
143
playbook.md
143
playbook.md
@@ -1,10 +1,15 @@
|
||||
# pyhdwallet v1.0.5 (hdwalletpy)
|
||||
|
||||
A command-line tool for generating and recovering HD wallets (BIP39) with support for Ethereum, Solana, and Bitcoin. It is designed for offline operation, optional PGP encryption, and writing deterministic AES-encrypted ZIP “wallet artifacts” into a local `.wallet/` folder.
|
||||
A command-line tool for generating and recovering HD wallets (BIP39) with support for Ethereum, Solana, and Bitcoin. It is designed for offline operation, optional PGP encryption, and writing deterministic AES-encrypted ZIP "wallet artifacts" into a local `.wallet/` folder.
|
||||
|
||||
Repository structure (current):
|
||||
|
||||
- `src/pyhdwallet.py` — main CLI script
|
||||
- `tests/` — offline regression test suite
|
||||
- `test_vectors.py` — pytest suite for derivation integrity
|
||||
- `bootstrap_vectors.py` — one-time script to generate test vectors
|
||||
- `vectors.json` — frozen expected values (committed)
|
||||
- `data/recipient.asc` — test PGP key (committed)
|
||||
- `.wallet/` — generated wallet artifacts (should be gitignored)
|
||||
- `requirements.in`, `requirements.txt`
|
||||
- `README.md`, `playbook.md`, `LICENSE`
|
||||
@@ -21,6 +26,7 @@ Repository structure (current):
|
||||
- **AES-encrypted ZIP artifacts**: When `--file` is used, output is written as an AES-encrypted ZIP via `pyzipper`.
|
||||
- **TTY safety guard + --force**: If stdout is piped/redirected (non-TTY), the tool refuses to print sensitive data unless `--force` is explicitly set (to avoid accidental leaks into logs/files). `isatty()` is the classic way to detect whether stdout is connected to a terminal.
|
||||
- **Off-screen mode**: `--off-screen` suppresses printing sensitive data to stdout.
|
||||
- **Regression test suite**: Offline tests with frozen vectors ensure derivation logic, PGP fingerprinting, and seed generation remain stable across code changes.
|
||||
|
||||
---
|
||||
|
||||
@@ -45,6 +51,7 @@ python -m pip install -r requirements.txt
|
||||
- `PGPy` — encryption to OpenPGP public keys
|
||||
- `pynacl` + `base58` — Solana seed/key handling
|
||||
- `pyzipper` — AES-encrypted ZIP writing (only needed when using `--file`)
|
||||
- `pytest` — test framework (development only)
|
||||
|
||||
---
|
||||
|
||||
@@ -62,7 +69,7 @@ python ./src/pyhdwallet.py gen
|
||||
python ./src/pyhdwallet.py gen --file
|
||||
```
|
||||
|
||||
### 3) Generate with PGP encryption and ZIP (recommended for “at-rest” storage)
|
||||
### 3) Generate with PGP encryption and ZIP (recommended for "at-rest" storage)
|
||||
|
||||
```bash
|
||||
python ./src/pyhdwallet.py gen --pgp-pubkey-file pubkeys/mykey.asc --file
|
||||
@@ -71,7 +78,8 @@ python ./src/pyhdwallet.py gen --pgp-pubkey-file pubkeys/mykey.asc --file
|
||||
### 4) Recover (addresses) from mnemonic
|
||||
|
||||
```bash
|
||||
python ./src/pyhdwallet.py recover --mnemonic "abandon abandon ... about"
|
||||
python ./src/pyhdwallet.py recover --mnemonic-stdin
|
||||
# (then paste mnemonic and press Ctrl-D)
|
||||
```
|
||||
|
||||
### 5) Recover with interactive input + ZIP artifact
|
||||
@@ -80,12 +88,72 @@ python ./src/pyhdwallet.py recover --mnemonic "abandon abandon ... about"
|
||||
python ./src/pyhdwallet.py recover --interactive --file
|
||||
```
|
||||
|
||||
### 6) Run tests
|
||||
### 6) Run built-in smoke test
|
||||
|
||||
```bash
|
||||
python ./src/pyhdwallet.py test
|
||||
```
|
||||
|
||||
### 7) Run full regression test suite
|
||||
|
||||
```bash
|
||||
pytest -v tests/test_vectors.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing
|
||||
|
||||
### Regression Test Suite
|
||||
|
||||
The project includes a comprehensive offline test suite that validates critical functionality without requiring network access. All tests use committed test vectors to ensure deterministic, repeatable results.
|
||||
|
||||
#### Test Coverage
|
||||
|
||||
- **PGP fingerprint calculation**: Verifies that PGP key fingerprinting logic produces expected results and enforces fingerprint matching
|
||||
- **BIP39 seed derivation**: Ensures mnemonics (with and without passphrases) always produce the same seed hex
|
||||
- **Multi-chain address derivation**: Validates Ethereum, Bitcoin (3 address types), and Solana (3 profiles) derivation paths remain stable
|
||||
- **CLI integration**: Smoke tests for `recover` command with correct and incorrect fingerprints
|
||||
|
||||
#### Running Tests
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
pytest -v tests/test_vectors.py
|
||||
|
||||
# Run specific test
|
||||
pytest -v tests/test_vectors.py::test_address_derivation_integrity
|
||||
|
||||
# Run with detailed output
|
||||
pytest -vv tests/test_vectors.py
|
||||
```
|
||||
|
||||
#### Test Artifacts (Committed)
|
||||
|
||||
- `tests/vectors.json` — Frozen expected values for all test cases
|
||||
- `tests/data/recipient.asc` — Test PGP public key (safe to commit; public key only)
|
||||
- `tests/bootstrap_vectors.py` — One-time script to regenerate vectors (run only when intentionally updating expected behavior)
|
||||
|
||||
#### When to Regenerate Test Vectors
|
||||
|
||||
Only regenerate `tests/vectors.json` when you **intentionally** change:
|
||||
|
||||
- Derivation paths (e.g., switching from `m/44'/60'/0'/0/i` to a different path)
|
||||
- Solana profile logic
|
||||
- BIP39 seed generation
|
||||
|
||||
To regenerate:
|
||||
|
||||
```bash
|
||||
rm tests/vectors.json
|
||||
python3 tests/bootstrap_vectors.py
|
||||
pytest -v tests/test_vectors.py # Verify all pass
|
||||
git add tests/vectors.json
|
||||
git commit -m "test: update vectors after intentional derivation change"
|
||||
```
|
||||
|
||||
**Warning**: If tests fail after a code change and you didn't intend to change behavior, **do not regenerate vectors**. Fix the code regression instead.
|
||||
|
||||
---
|
||||
|
||||
## Outputs and file structure
|
||||
@@ -130,7 +198,7 @@ Naming uses UTC timestamps (e.g. `20260106_161830Z`):
|
||||
Download and verify a PGP public key from a URL.
|
||||
|
||||
```bash
|
||||
python ./src/pyhdwallet.py fetchkey <url> [--out FILE] [--timeout SECONDS] [--off-screen]
|
||||
python ./src/pyhdwallet.py fetchkey <url> [--out FILE] [--timeout SECONDS] [--off-screen] [--expected-fingerprint FINGERPRINT]
|
||||
```
|
||||
|
||||
Options:
|
||||
@@ -139,6 +207,7 @@ Options:
|
||||
- `--out FILE`: save key to file
|
||||
- `--timeout SECONDS`: request timeout (default 15)
|
||||
- `--off-screen`: reduced output / temp-file behavior
|
||||
- `--expected-fingerprint`: refuse if downloaded key fingerprint doesn't match (40 hex chars)
|
||||
|
||||
---
|
||||
|
||||
@@ -164,6 +233,7 @@ PGP options:
|
||||
|
||||
- `--pgp-pubkey-file FILE` (encrypt payload to pubkey; `.asc` content)
|
||||
- `--pgp-ignore-usage-flags`
|
||||
- `--expected-fingerprint FINGERPRINT` (refuse if PGP key fingerprint doesn't match)
|
||||
|
||||
Artifact options:
|
||||
|
||||
@@ -175,14 +245,14 @@ Artifact options:
|
||||
|
||||
Safety options:
|
||||
|
||||
- `--off-screen` (don’t print sensitive output)
|
||||
- `--off-screen` (don't print sensitive output)
|
||||
- `--force` (only for non-TTY stdout; see below)
|
||||
|
||||
---
|
||||
|
||||
### recover (offline)
|
||||
|
||||
Recover addresses from mnemonic or seed and optionally write a ZIP artifact.
|
||||
Recover addresses from mnemonic and optionally write a ZIP artifact.
|
||||
|
||||
```bash
|
||||
python ./src/pyhdwallet.py recover [options]
|
||||
@@ -190,18 +260,19 @@ python ./src/pyhdwallet.py recover [options]
|
||||
|
||||
Input options (choose one):
|
||||
|
||||
- `--mnemonic "..."` (not recommended due to shell history; prefer `--interactive`)
|
||||
- `--seed HEX` (128 hex chars / 64 bytes)
|
||||
- `--interactive`
|
||||
- `--mnemonic-stdin` (recommended; read from stdin to avoid shell history)
|
||||
- `--interactive` (word-by-word guided entry with validation)
|
||||
|
||||
Additional options:
|
||||
|
||||
- same chain/PGP/ZIP/off-screen/force options as `gen`
|
||||
- `--export-private` (requires `--pgp-pubkey-file`; includes private keys in encrypted payload)
|
||||
- `--include-source` (includes mnemonic in payload; requires `--pgp-pubkey-file`)
|
||||
|
||||
Notes:
|
||||
|
||||
- `--export-private` requires `--pgp-pubkey-file` (private keys only travel inside encrypted payload).
|
||||
- `--include-source` controls whether mnemonic/seed is included inside the encrypted payload (only meaningful when PGP is used).
|
||||
- `--include-source` controls whether mnemonic is included inside the encrypted payload (only meaningful when PGP is used).
|
||||
|
||||
---
|
||||
|
||||
@@ -213,6 +284,12 @@ Run minimal self-tests.
|
||||
python ./src/pyhdwallet.py test
|
||||
```
|
||||
|
||||
For comprehensive regression testing, use the pytest suite:
|
||||
|
||||
```bash
|
||||
pytest -v tests/test_vectors.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## When to use `--force`
|
||||
@@ -241,16 +318,17 @@ If running normally in your interactive terminal, stdout is a TTY and `--force`
|
||||
|
||||
## Security notes (practical)
|
||||
|
||||
- `gen` printing the mnemonic is intentionally “debug/test” behavior. Assume stdout can be recorded (scrollback, logging, screen recording, CI logs).
|
||||
- `gen` printing the mnemonic is intentionally "debug/test" behavior. Assume stdout can be recorded (scrollback, logging, screen recording, CI logs).
|
||||
- Prefer `--off-screen` for reduced exposure.
|
||||
- Prefer `--file` so artifacts go into `.wallet/` and are AES-encrypted via `pyzipper`.
|
||||
- For stronger at-rest security: combine `--pgp-pubkey-file` + `--file` so the ZIP contains only an encrypted `.asc` payload. PGPy encryption style follows `PGPMessage.new(...)` then `pubkey.encrypt(...)`.
|
||||
- Use `--expected-fingerprint` to enforce PGP key pinning and prevent key substitution attacks.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### “Permission denied” running `./src/pyhdwallet.py`
|
||||
### "Permission denied" running `./src/pyhdwallet.py`
|
||||
|
||||
Run via python, or set executable bit:
|
||||
|
||||
@@ -261,7 +339,7 @@ chmod +x ./src/pyhdwallet.py
|
||||
./src/pyhdwallet.py gen
|
||||
```
|
||||
|
||||
### “Missing dependency: pyzipper” but pip says installed
|
||||
### "Missing dependency: pyzipper" but pip says installed
|
||||
|
||||
You likely installed into a different venv. Use:
|
||||
|
||||
@@ -270,6 +348,14 @@ python -m pip install pyzipper
|
||||
python -c "import pyzipper; print(pyzipper.__version__)"
|
||||
```
|
||||
|
||||
### "Missing dependency: pytest" when running tests
|
||||
|
||||
Install pytest in your virtualenv:
|
||||
|
||||
```bash
|
||||
python -m pip install pytest
|
||||
```
|
||||
|
||||
### Unzipping AES ZIP issues
|
||||
|
||||
Some `unzip` tools may not support AES-encrypted ZIPs. Use Python + pyzipper to extract if needed:
|
||||
@@ -289,6 +375,17 @@ PY
|
||||
|
||||
AES ZIP support is the reason `pyzipper` is used.
|
||||
|
||||
### Test failures after code changes
|
||||
|
||||
If `pytest -v tests/test_vectors.py` fails after you modified code:
|
||||
|
||||
1. **Did you intentionally change derivation logic?**
|
||||
- Yes → Regenerate vectors: `rm tests/vectors.json && python3 tests/bootstrap_vectors.py`
|
||||
- No → You have a regression bug; revert your changes and fix the code
|
||||
|
||||
2. **Is only one test failing?**
|
||||
- Run with `-vv` for detailed diff: `pytest -vv tests/test_vectors.py::test_name`
|
||||
|
||||
---
|
||||
|
||||
## Changelog
|
||||
@@ -299,8 +396,24 @@ AES ZIP support is the reason `pyzipper` is used.
|
||||
- `--file` is now a boolean flag that writes **only** AES-encrypted ZIP artifacts (no raw output files).
|
||||
- Added: `--wallet-location`, `--zip-password-mode`, `--zip-password-len`, `--show-generated-password`.
|
||||
- Added: `--force` to override the non-TTY printing safety guard (use only when redirecting/piping).
|
||||
|
||||
- Added: `--expected-fingerprint` for PGP key pinning.
|
||||
- Added: Offline regression test suite with frozen vectors (`tests/test_vectors.py`).
|
||||
- Changed: `recover` now uses `--mnemonic-stdin` and `--interactive` instead of `--mnemonic` CLI arg.
|
||||
- **v1.0.3** Documentation and CLI behavior updates.
|
||||
- **v1.0.2** Added off-screen behaviors and security improvements.
|
||||
- **v1.0.1** Renamed to pyhdwallet and added version flag.
|
||||
- **v1.0.0** Initial release.
|
||||
|
||||
Key additions:
|
||||
|
||||
1. **Testing section** with full coverage explanation
|
||||
2. **Repository structure** updated to include `tests/` folder
|
||||
3. **Test artifacts** documentation
|
||||
4. **When to regenerate vectors** guidance
|
||||
5. **Troubleshooting** section for test failures
|
||||
6. **Changelog** updated with v1.0.5 test suite addition
|
||||
7. **Quick Start** updated with correct `recover` command syntax (`--mnemonic-stdin`)
|
||||
8. **Dependencies** updated to include pytest
|
||||
|
||||
[1](https://ppl-ai-file-upload.s3.amazonaws.com/web/direct-files/attachments/50321846/e5caab46-cb40-4f76-8ea9-60ed65bf4927/pyhdwallet.py)
|
||||
[2](https://ppl-ai-file-upload.s3.amazonaws.com/web/direct-files/attachments/images/50321846/085d5710-8637-45d5-8686-77c98df9dcdf/image.jpg)
|
||||
|
||||
Reference in New Issue
Block a user