Compare commits
20 Commits
5c343c7944
...
v1.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6c84f81ee | ||
| 109829f1f5 | |||
|
|
2f7433b704 | ||
|
|
6457ec2cee | ||
|
|
0949fe9792 | ||
| 21b9389591 | |||
|
|
369c8595a1 | ||
|
|
2807982209 | ||
|
|
84953dbe5a | ||
|
|
6fd7cd4e79 | ||
|
|
5a52eb0954 | ||
|
|
28f01a613a | ||
| a9af9d33af | |||
| 129b09fcd9 | |||
|
|
d02e1d872e | ||
|
|
875fa17d6c | ||
|
|
ccd070dc56 | ||
|
|
ce26b3560a | ||
| 4cf32f9ba0 | |||
| 94fcb993db |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
.venv/
|
||||
.venv312/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
.vscode/
|
||||
@@ -23,3 +24,6 @@ build/
|
||||
*.sqlite3
|
||||
*.db
|
||||
*.asc
|
||||
.venv/
|
||||
.wallet/
|
||||
.potentialfix.md
|
||||
|
||||
18
Dockerfile
Normal file
18
Dockerfile
Normal file
@@ -0,0 +1,18 @@
|
||||
# Dockerfile
|
||||
# Build environment for hdwalletpy: Python 3.12 + build tools + venv support
|
||||
# Used for: (1) Building Linux x86_64 wheels, (2) Development container
|
||||
FROM python:3.12-slim
|
||||
|
||||
# Install build tools, headers, and venv module
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
python3-dev \
|
||||
libffi-dev \
|
||||
python3-venv \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Set working directory for bind mounts
|
||||
WORKDIR /app
|
||||
|
||||
# Default command (can be overridden)
|
||||
CMD ["/bin/bash"]
|
||||
169
Makefile
Normal file
169
Makefile
Normal file
@@ -0,0 +1,169 @@
|
||||
# Makefile for hdwalletpy workflow
|
||||
# - Build reusable Docker image (Python 3.12)
|
||||
# - Vendor multi-platform wheels for offline air-gapped use
|
||||
# - Compile wheels in container (wheelhouse)
|
||||
# - Create host venv and install from wheelhouse or vendor/
|
||||
# - Optionally run inside a warm container
|
||||
|
||||
# ---------- Config ----------
|
||||
IMAGE := hdwallet-build:3.12
|
||||
CONTAINER := hdwallet-dev
|
||||
WORKDIR := /app
|
||||
VENV_HOST := .venv
|
||||
VENV_CONTAINER := /opt/venv
|
||||
WHEELHOUSE := wheelhouse
|
||||
|
||||
# Vendor directories for air-gapped deployment
|
||||
VENDOR_MACOS := vendor/macos-arm64
|
||||
VENDOR_LINUX := vendor/linux-x86_64
|
||||
|
||||
# ---------- Help ----------
|
||||
.PHONY: help
|
||||
help:
|
||||
@echo "Vendoring (for offline/air-gapped use):"
|
||||
@echo " make vendor-macos - Build macOS ARM64 wheels (native)"
|
||||
@echo " make vendor-linux - Build Linux x86_64 wheels (Docker)"
|
||||
@echo " make vendor-all - Build wheels for both platforms"
|
||||
@echo " make verify-vendor - Test offline installation from vendor/"
|
||||
@echo ""
|
||||
@echo "Development workflow:"
|
||||
@echo " make build-image - Build Docker image (Python 3.12)"
|
||||
@echo " make wheels - Build wheels into ./$(WHEELHOUSE)"
|
||||
@echo " make install - Create venv and install dependencies"
|
||||
@echo " make test - Run test suite"
|
||||
@echo " make up - Start warm dev container"
|
||||
@echo " make shell - Open shell in warm container"
|
||||
@echo " make down - Stop and remove dev container"
|
||||
@echo ""
|
||||
@echo "Cleanup:"
|
||||
@echo " make clean - Remove venv, wheelhouse, vendor/"
|
||||
@echo " make clean-vendor - Remove vendor/ only"
|
||||
|
||||
# ---------- Build reusable image ----------
|
||||
.PHONY: build-image
|
||||
build-image:
|
||||
docker build -t $(IMAGE) .
|
||||
|
||||
# ---------- Vendoring for Air-Gapped Use ----------
|
||||
.PHONY: vendor-macos
|
||||
vendor-macos: requirements.txt
|
||||
@echo "Building macOS ARM64 wheels (native)..."
|
||||
@if [ ! -f ".venv312/bin/pip" ]; then \
|
||||
echo "ERROR: .venv312 not found. Create it first:"; \
|
||||
echo " python3.12 -m venv .venv312 && source .venv312/bin/activate"; \
|
||||
exit 1; \
|
||||
fi
|
||||
mkdir -p $(VENDOR_MACOS)
|
||||
.venv312/bin/pip download --dest $(VENDOR_MACOS) -r requirements.txt
|
||||
cd $(VENDOR_MACOS) && shasum -a 256 *.whl > SHA256SUMS
|
||||
@echo "✓ macOS ARM64 wheels: $(VENDOR_MACOS)/"
|
||||
|
||||
.PHONY: vendor-linux
|
||||
vendor-linux: requirements.txt build-image
|
||||
@echo "Building Linux x86_64 wheels (Docker)..."
|
||||
mkdir -p $(VENDOR_LINUX)
|
||||
docker run --rm \
|
||||
-v "$$PWD":$(WORKDIR) \
|
||||
-w $(WORKDIR) \
|
||||
$(IMAGE) \
|
||||
bash -c " \
|
||||
pip install --upgrade pip && \
|
||||
pip download --dest $(VENDOR_LINUX) -r requirements.txt && \
|
||||
pip wheel --wheel-dir $(VENDOR_LINUX) --no-deps $(VENDOR_LINUX)/*.tar.gz 2>/dev/null || true && \
|
||||
rm -f $(VENDOR_LINUX)/*.tar.gz && \
|
||||
cd $(VENDOR_LINUX) && sha256sum *.whl > SHA256SUMS \
|
||||
"
|
||||
@echo "✓ Linux x86_64 wheels: $(VENDOR_LINUX)/"
|
||||
|
||||
.PHONY: vendor-all
|
||||
vendor-all: vendor-macos vendor-linux
|
||||
@echo ""
|
||||
@echo "✓ All platforms vendored:"
|
||||
@echo " macOS ARM64: $(VENDOR_MACOS)/ ($$(ls $(VENDOR_MACOS)/*.whl 2>/dev/null | wc -l | xargs) wheels)"
|
||||
@echo " Linux x86_64: $(VENDOR_LINUX)/ ($$(ls $(VENDOR_LINUX)/*.whl 2>/dev/null | wc -l | xargs) wheels)"
|
||||
@echo ""
|
||||
@echo "Commit with: git add vendor/ && git commit -m 'vendor: update wheels'"
|
||||
|
||||
.PHONY: verify-vendor
|
||||
verify-vendor:
|
||||
@echo "Testing offline installation from vendor/..."
|
||||
@bash -c ' \
|
||||
if [[ "$$OSTYPE" == "darwin"* ]]; then \
|
||||
PLATFORM="macos-arm64"; \
|
||||
else \
|
||||
PLATFORM="linux-x86_64"; \
|
||||
fi; \
|
||||
echo "Platform: $$PLATFORM"; \
|
||||
python3.12 -m venv .venv-verify && \
|
||||
source .venv-verify/bin/activate && \
|
||||
pip install --no-index --find-links=vendor/$$PLATFORM -r requirements.txt && \
|
||||
pytest -v tests/test_vectors.py && \
|
||||
python src/pyhdwallet.py test && \
|
||||
echo "" && \
|
||||
echo "✅ Vendor installation verified!" && \
|
||||
deactivate && \
|
||||
rm -rf .venv-verify \
|
||||
'
|
||||
|
||||
# ---------- Development Workflow ----------
|
||||
.PHONY: wheels
|
||||
wheels: requirements.txt build-image
|
||||
docker run --rm \
|
||||
-v "$$PWD":$(WORKDIR) \
|
||||
-w $(WORKDIR) \
|
||||
$(IMAGE) \
|
||||
bash -c " \
|
||||
pip install --upgrade pip setuptools wheel && \
|
||||
mkdir -p $(WHEELHOUSE) && \
|
||||
pip wheel -r requirements.txt -w $(WHEELHOUSE) \
|
||||
"
|
||||
|
||||
.PHONY: install
|
||||
install: requirements.txt
|
||||
@if [ ! -d "$(VENV_HOST)" ]; then \
|
||||
echo "Creating venv: $(VENV_HOST)"; \
|
||||
python3.12 -m venv $(VENV_HOST); \
|
||||
fi
|
||||
. $(VENV_HOST)/bin/activate && \
|
||||
pip install --upgrade pip && \
|
||||
pip install -r requirements.txt && \
|
||||
echo "✓ Virtual environment ready: $(VENV_HOST)"
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
@if [ ! -d "$(VENV_HOST)" ]; then \
|
||||
echo "ERROR: Run 'make install' first"; \
|
||||
exit 1; \
|
||||
fi
|
||||
. $(VENV_HOST)/bin/activate && \
|
||||
pytest -v tests/test_vectors.py && \
|
||||
python src/pyhdwallet.py test
|
||||
|
||||
# ---------- Warm container lifecycle ----------
|
||||
.PHONY: up
|
||||
up: build-image
|
||||
docker run -dit \
|
||||
-v "$$PWD":$(WORKDIR) \
|
||||
-w $(WORKDIR) \
|
||||
--name $(CONTAINER) \
|
||||
$(IMAGE) \
|
||||
bash
|
||||
|
||||
.PHONY: shell
|
||||
shell:
|
||||
docker exec -it $(CONTAINER) bash
|
||||
|
||||
.PHONY: down
|
||||
down:
|
||||
- docker rm -f $(CONTAINER)
|
||||
|
||||
# ---------- Cleanup ----------
|
||||
.PHONY: clean-vendor
|
||||
clean-vendor:
|
||||
rm -rf vendor/
|
||||
|
||||
.PHONY: clean
|
||||
clean: down
|
||||
rm -rf $(VENV_HOST) $(WHEELHOUSE) vendor/ .venv-verify .venv-offline-test
|
||||
find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
|
||||
find . -type d -name "*.egg-info" -exec rm -rf {} + 2>/dev/null || true
|
||||
212
README.md
212
README.md
@@ -1,31 +1,203 @@
|
||||
# pyhdwallet v1.0.4
|
||||
# pyhdwallet – Secure HD Wallet Tool
|
||||
|
||||
A secure, offline command-line tool for generating and recovering BIP39 HD wallets with support for Ethereum, Solana, and Bitcoin.
|
||||
A Python command-line tool for generating and recovering BIP39 HD wallets with support for Ethereum, Solana, and Bitcoin. Includes **BIP85 deterministic child mnemonic derivation** for creating multiple isolated wallets from a single master seed. Designed for offline operation with optional PGP encryption and AES-encrypted ZIP artifacts.
|
||||
|
||||
## Purpose
|
||||
---
|
||||
|
||||
pyhdwallet helps you create new wallets or recover from existing mnemonics/seeds, with built-in PGP encryption for secure storage. It's designed for privacy-conscious users who want offline, auditable wallet management.
|
||||
## 📦 Installation
|
||||
|
||||
## Installation
|
||||
### **Quick Start (macOS/Linux with Internet)**
|
||||
|
||||
1. Ensure Python 3.11+ is installed.
|
||||
2. Clone/download the repo.
|
||||
3. Create a virtual environment: `python -m venv .venv && source .venv/bin/activate`
|
||||
4. Install dependencies: `pip install -r requirements.txt`
|
||||
```bash
|
||||
# Clone repository
|
||||
git clone https://github.com/<your-username>/hdwalletpy.git
|
||||
cd hdwalletpy
|
||||
|
||||
## Basic Usage
|
||||
# Install using automated script
|
||||
./install_offline.sh
|
||||
```
|
||||
|
||||
Run the tool with a subcommand. For help, use `-h`.
|
||||
The script automatically:
|
||||
|
||||
- Generate a wallet: `python ./src/pyhdwallet.py gen --chains ethereum --addresses 3`
|
||||
- Recover from mnemonic: `python ./src/pyhdwallet.py recover --mnemonic "your words" --chains bitcoin`
|
||||
- Fetch PGP key: `python ./src/pyhdwallet.py fetchkey "https://example.com/key.asc"`
|
||||
- Run tests: `python ./src/pyhdwallet.py test`
|
||||
- Creates Python 3.12 virtual environment
|
||||
- Installs from vendored wheels (offline-capable)
|
||||
- Verifies installation with test suite
|
||||
- Leaves you in activated venv
|
||||
|
||||
For detailed examples and security tips, see `playbook.md`.
|
||||
---
|
||||
|
||||
## Security
|
||||
### **Air-Gapped Installation (No Internet)**
|
||||
|
||||
- Operates offline by default.
|
||||
- Use `--secure-mode` for high-security operations.
|
||||
- Always verify PGP keys and run on trusted systems.
|
||||
This repository includes pre-built Python wheels for offline use.
|
||||
|
||||
**Supported platforms:**
|
||||
|
||||
- macOS ARM64 (M1/M2/M3/M4) - Python 3.12
|
||||
- Linux x86_64 (Ubuntu/Tails) - Python 3.12
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. On an online machine, clone and verify:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/<your-username>/hdwalletpy.git
|
||||
cd hdwalletpy
|
||||
|
||||
# Verify checksums
|
||||
cd vendor/linux-x86_64 # or macos-arm64
|
||||
sha256sum -c SHA256SUMS # Linux
|
||||
shasum -a 256 -c SHA256SUMS # macOS
|
||||
```
|
||||
|
||||
2. Transfer entire repo to USB drive
|
||||
|
||||
3. On air-gapped machine:
|
||||
|
||||
```bash
|
||||
cd hdwalletpy
|
||||
./install_offline.sh
|
||||
```
|
||||
|
||||
4. Generate wallet:
|
||||
|
||||
```bash
|
||||
python src/pyhdwallet.py gen --off-screen --file
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **Developer Installation (with Docker)**
|
||||
|
||||
For development or building wheels for other platforms:
|
||||
|
||||
```bash
|
||||
# Build Docker image
|
||||
make build-image
|
||||
|
||||
# Build wheels for all platforms
|
||||
make vendor-all
|
||||
|
||||
# Install development environment
|
||||
make install
|
||||
|
||||
# Run tests
|
||||
make test
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ Basic Usage
|
||||
|
||||
```bash
|
||||
# Generate wallet (prints mnemonic - debug mode)
|
||||
python src/pyhdwallet.py gen
|
||||
|
||||
# Generate with off-screen mode + encrypted ZIP
|
||||
python src/pyhdwallet.py gen --off-screen --file
|
||||
|
||||
# Generate with PGP encryption + ZIP
|
||||
python src/pyhdwallet.py gen \
|
||||
--pgp-pubkey-file pubkeys/mykey.asc \
|
||||
--expected-fingerprint A27B96F2B169B5491013D2DA892B822C14A9AA18 \
|
||||
--off-screen \
|
||||
--file
|
||||
|
||||
# Derive BIP85 child mnemonic (12/15/18/21/24 words)
|
||||
python src/pyhdwallet.py gen-child \
|
||||
--interactive \
|
||||
--words 12 \
|
||||
--index 0
|
||||
|
||||
# Derive BIP85 child with passphrase + encrypted output
|
||||
python src/pyhdwallet.py gen-child \
|
||||
--mnemonic-stdin \
|
||||
--passphrase \
|
||||
--words 24 \
|
||||
--index 0 \
|
||||
--pgp-pubkey-file pubkeys/mykey.asc \
|
||||
--off-screen \
|
||||
--file
|
||||
|
||||
# Recover wallet from mnemonic
|
||||
python src/pyhdwallet.py recover --interactive --file
|
||||
|
||||
# Fetch PGP public key (requires internet)
|
||||
python src/pyhdwallet.py fetchkey "https://example.com/key.asc" --out mykey.asc
|
||||
|
||||
# Run tests
|
||||
python src/pyhdwallet.py test
|
||||
pytest -v tests/test_vectors.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Security Features
|
||||
|
||||
- **Offline-first**: Network access blocked during key generation/recovery
|
||||
- **BIP85 deterministic entropy**: Derive unlimited child mnemonics from master seed
|
||||
- **Test suite**: Regression tests with frozen vectors ensure derivation logic integrity
|
||||
- **PGP fingerprint pinning**: Prevents key substitution attacks
|
||||
- **TTY safety guard**: Refuses to print secrets when stdout is piped/redirected
|
||||
- **AES-encrypted outputs**: Wallet artifacts encrypted with `pyzipper`
|
||||
- **No shell history leaks**: Use `--interactive` or `--mnemonic-stdin` for recovery
|
||||
- **Interoperable**: BIP85 children compatible with Coldcard, Ian Coleman tool, etc.
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Makefile Targets
|
||||
|
||||
**Vendoring (for air-gapped deployment):**
|
||||
|
||||
```bash
|
||||
make vendor-macos # Build macOS ARM64 wheels
|
||||
make vendor-linux # Build Linux x86_64 wheels (Docker)
|
||||
make vendor-all # Build for both platforms
|
||||
make verify-vendor # Test offline installation
|
||||
```
|
||||
|
||||
**Development:**
|
||||
|
||||
```bash
|
||||
make install # Create venv and install dependencies
|
||||
make test # Run test suite
|
||||
make build-image # Build Docker image
|
||||
make shell # Open shell in Docker container
|
||||
make clean # Remove venvs and build artifacts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📖 Full Documentation
|
||||
|
||||
- **[playbook.md](playbook.md)** - Complete command reference and operational guide
|
||||
- **[tests/](tests/)** - Regression test suite documentation
|
||||
|
||||
---
|
||||
|
||||
## 🔒 Recommended Air-Gapped Setup
|
||||
|
||||
For maximum security when generating production wallets:
|
||||
|
||||
1. Use fresh Ubuntu Live USB or Tails OS
|
||||
2. Never connect to network after booting
|
||||
3. Transfer this repository via separate USB
|
||||
4. Run `./install_offline.sh`
|
||||
5. Generate wallet: `python src/pyhdwallet.py gen --off-screen --file`
|
||||
6. Write mnemonic to paper/metal backup
|
||||
7. Transfer encrypted ZIP to secure storage
|
||||
8. Wipe USB drives securely
|
||||
|
||||
See [playbook.md](playbook.md) for detailed air-gapped procedures.
|
||||
|
||||
---
|
||||
|
||||
## 🆕 What's New in v1.1.0
|
||||
|
||||
- **BIP85 child mnemonic derivation** via new `gen-child` command
|
||||
- Derive 12/15/18/21/24-word child mnemonics from master seed
|
||||
- Full interoperability with BIP85-compatible wallets (Coldcard, etc.)
|
||||
- Optional master BIP39 passphrase support
|
||||
- Verified against official BIP85 test vectors
|
||||
- No new dependencies required (uses existing bip-utils + stdlib)
|
||||
|
||||
---
|
||||
|
||||
115
build-all-platforms.sh
Executable file
115
build-all-platforms.sh
Executable file
@@ -0,0 +1,115 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "Building wheels for all platforms using Python 3.12..."
|
||||
echo ""
|
||||
|
||||
# Check Docker is running
|
||||
if ! docker info > /dev/null 2>&1; then
|
||||
echo "ERROR: Docker is not running. Please start Docker Desktop."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 1. macOS ARM64 (via Docker with Python 3.12)
|
||||
echo "==> Building macOS ARM64 wheels (Docker + Python 3.12)..."
|
||||
docker run --rm \
|
||||
--platform linux/arm64 \
|
||||
-v $(pwd):/work \
|
||||
-w /work \
|
||||
python:3.12-slim \
|
||||
bash -c "
|
||||
apt-get update -qq && apt-get install -y -qq gcc g++ make libffi-dev > /dev/null
|
||||
pip install --upgrade pip > /dev/null
|
||||
mkdir -p vendor/macos-arm64-docker
|
||||
pip download --dest vendor/macos-arm64-docker -r requirements.txt
|
||||
pip wheel --wheel-dir vendor/macos-arm64-docker --no-deps vendor/macos-arm64-docker/*.tar.gz 2>/dev/null || true
|
||||
rm -f vendor/macos-arm64-docker/*.tar.gz
|
||||
"
|
||||
echo "✓ macOS ARM64: $(ls vendor/macos-arm64-docker/*.whl 2>/dev/null | wc -l | xargs) wheels"
|
||||
|
||||
# Actually, let's use native Mac Python 3.12 if available, otherwise use what we have
|
||||
echo ""
|
||||
echo "==> Building macOS ARM64 wheels (native - using your .venv312)..."
|
||||
mkdir -p vendor/macos-arm64
|
||||
|
||||
# Use the Python from your working venv
|
||||
if [ -f ".venv312/bin/pip" ]; then
|
||||
echo "Using Python from .venv312..."
|
||||
.venv312/bin/pip download --dest vendor/macos-arm64 -r requirements.txt
|
||||
echo "✓ macOS ARM64: $(ls vendor/macos-arm64/*.whl 2>/dev/null | wc -l | xargs) wheels"
|
||||
else
|
||||
echo "ERROR: .venv312 not found!"
|
||||
echo "Please activate your working venv first:"
|
||||
echo " source .venv312/bin/activate"
|
||||
echo " pip download --dest vendor/macos-arm64 -r requirements.txt"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 2. Linux x86_64 (via Docker)
|
||||
echo ""
|
||||
echo "==> Building Linux x86_64 wheels (Docker + Python 3.12)..."
|
||||
docker run --rm \
|
||||
-v $(pwd):/work \
|
||||
-w /work \
|
||||
python:3.12-slim \
|
||||
bash -c "
|
||||
apt-get update -qq && apt-get install -y -qq gcc g++ make libffi-dev > /dev/null
|
||||
pip install --upgrade pip > /dev/null
|
||||
mkdir -p vendor/linux-x86_64
|
||||
pip download --dest vendor/linux-x86_64 -r requirements.txt
|
||||
pip wheel --wheel-dir vendor/linux-x86_64 --no-deps vendor/linux-x86_64/*.tar.gz 2>/dev/null || true
|
||||
rm -f vendor/linux-x86_64/*.tar.gz
|
||||
"
|
||||
echo "✓ Linux x86_64: $(ls vendor/linux-x86_64/*.whl 2>/dev/null | wc -l | xargs) wheels"
|
||||
|
||||
# 3. Generate SHA256 checksums
|
||||
echo ""
|
||||
echo "==> Generating checksums..."
|
||||
for platform in macos-arm64 linux-x86_64; do
|
||||
if [ -d "vendor/$platform" ] && [ "$(ls -A vendor/$platform/*.whl 2>/dev/null)" ]; then
|
||||
(cd vendor/$platform && shasum -a 256 *.whl > SHA256SUMS)
|
||||
echo "✓ Checksums for $platform"
|
||||
fi
|
||||
done
|
||||
|
||||
# 4. Create provenance file
|
||||
echo ""
|
||||
echo "==> Creating provenance documentation..."
|
||||
cat > vendor/PROVENANCE.md << EOF
|
||||
# Dependency Provenance
|
||||
|
||||
All wheels generated on: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
|
||||
Python version: 3.12
|
||||
Build machine: $(uname -s) $(uname -m)
|
||||
Docker used: Yes (python:3.12-slim)
|
||||
|
||||
## Platforms
|
||||
- macOS ARM64: Built using .venv312 (Python 3.12)
|
||||
- Linux x86_64: Built using Docker python:3.12-slim
|
||||
|
||||
## Package Versions
|
||||
$(cat requirements.txt | head -20)
|
||||
|
||||
## Integrity Verification
|
||||
Each platform directory contains SHA256SUMS for verification:
|
||||
\`\`\`bash
|
||||
cd vendor/linux-x86_64
|
||||
shasum -a 256 -c SHA256SUMS
|
||||
\`\`\`
|
||||
|
||||
Last updated: $(date -u +"%Y-%m-%d")
|
||||
Built by: $(git config user.name) <$(git config user.email)>
|
||||
Commit: $(git rev-parse --short HEAD 2>/dev/null || echo "not in git")
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo "✓ All platforms built successfully!"
|
||||
echo ""
|
||||
echo "Summary:"
|
||||
echo " macOS ARM64: vendor/macos-arm64/ ($(ls vendor/macos-arm64/*.whl 2>/dev/null | wc -l | xargs) wheels)"
|
||||
echo " Linux x86_64: vendor/linux-x86_64/ ($(ls vendor/linux-x86_64/*.whl 2>/dev/null | wc -l | xargs) wheels)"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Verify: ls vendor/*/SHA256SUMS"
|
||||
echo " 2. Test: ./install_offline.sh"
|
||||
echo " 3. Commit: git add vendor/ && git commit -m 'vendor: add multi-platform wheels'"
|
||||
117
install_offline.sh
Executable file
117
install_offline.sh
Executable file
@@ -0,0 +1,117 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Detect platform
|
||||
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
||||
PLATFORM="linux-x86_64"
|
||||
elif [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
ARCH=$(uname -m)
|
||||
if [[ "$ARCH" == "arm64" ]]; then
|
||||
PLATFORM="macos-arm64"
|
||||
else
|
||||
echo "ERROR: Only macOS ARM64 is supported"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "ERROR: Unsupported platform: $OSTYPE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Detected platform: $PLATFORM"
|
||||
|
||||
# Check if vendor directory exists
|
||||
if [ ! -d "vendor/$PLATFORM" ]; then
|
||||
echo "ERROR: vendor/$PLATFORM not found!"
|
||||
echo "Run ./build-vendors-simple.sh to generate wheels"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for Python 3.12
|
||||
if ! command -v python3.12 &> /dev/null; then
|
||||
echo "ERROR: Python 3.12 not found!"
|
||||
echo "Please install Python 3.12:"
|
||||
echo " macOS: brew install python@3.12"
|
||||
echo " Linux: apt-get install python3.12 python3.12-venv"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if already in a venv
|
||||
if [ -n "$VIRTUAL_ENV" ]; then
|
||||
CURRENT_PYTHON=$(python --version 2>&1 | grep -oE '3\.[0-9]+')
|
||||
if [[ "$CURRENT_PYTHON" != "3.12" ]]; then
|
||||
echo "WARNING: You are in a venv with Python $CURRENT_PYTHON"
|
||||
echo "Deactivating and creating new Python 3.12 venv..."
|
||||
deactivate || true
|
||||
else
|
||||
echo "Already in Python 3.12 venv, using it..."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Create or use existing venv
|
||||
VENV_DIR=".venv"
|
||||
if [ ! -d "$VENV_DIR" ] || [ ! -f "$VENV_DIR/bin/python3.12" ]; then
|
||||
echo "Creating Python 3.12 virtual environment..."
|
||||
python3.12 -m venv "$VENV_DIR"
|
||||
echo "✓ Virtual environment created: $VENV_DIR"
|
||||
else
|
||||
echo "Using existing venv: $VENV_DIR"
|
||||
fi
|
||||
|
||||
# Activate venv
|
||||
source "$VENV_DIR/bin/activate"
|
||||
|
||||
# Verify Python version
|
||||
PYTHON_VERSION=$(python --version 2>&1 | grep -oE '3\.[0-9]+')
|
||||
echo "Using Python $PYTHON_VERSION from: $(which python)"
|
||||
|
||||
if [[ "$PYTHON_VERSION" != "3.12" ]]; then
|
||||
echo "ERROR: Expected Python 3.12, got $PYTHON_VERSION"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify checksums
|
||||
if [ -f "vendor/$PLATFORM/SHA256SUMS" ]; then
|
||||
echo ""
|
||||
echo "Verifying checksums..."
|
||||
(cd vendor/$PLATFORM && shasum -a 256 -c SHA256SUMS --quiet) || {
|
||||
echo "ERROR: Checksum verification failed!"
|
||||
exit 1
|
||||
}
|
||||
echo "✓ Checksums verified"
|
||||
fi
|
||||
|
||||
# Install
|
||||
echo ""
|
||||
echo "Installing from vendor/$PLATFORM..."
|
||||
pip install --upgrade pip --quiet
|
||||
pip install --no-index --find-links=vendor/$PLATFORM -r requirements.txt
|
||||
|
||||
# Test
|
||||
echo ""
|
||||
echo "Running tests..."
|
||||
python -m pytest -v tests/test_vectors.py || {
|
||||
echo "ERROR: Tests failed!"
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo ""
|
||||
echo "Running built-in smoke test..."
|
||||
python src/pyhdwallet.py test || {
|
||||
echo "ERROR: Smoke test failed!"
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "✓ Installation complete and verified!"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Virtual environment: $VENV_DIR"
|
||||
echo "Python version: $(python --version)"
|
||||
echo ""
|
||||
echo "To activate this environment:"
|
||||
echo " source $VENV_DIR/bin/activate"
|
||||
echo ""
|
||||
echo "Generate a wallet with:"
|
||||
echo " python src/pyhdwallet.py gen --off-screen --file"
|
||||
echo ""
|
||||
1000
playbook.md
1000
playbook.md
File diff suppressed because it is too large
Load Diff
7
pytest.ini
Normal file
7
pytest.ini
Normal file
@@ -0,0 +1,7 @@
|
||||
[pytest]
|
||||
filterwarnings =
|
||||
ignore::DeprecationWarning:pgpy.constants
|
||||
testpaths = tests
|
||||
python_files = test_*.py
|
||||
python_classes = Test*
|
||||
python_functions = test_*
|
||||
2
requirements-dev.in
Normal file
2
requirements-dev.in
Normal file
@@ -0,0 +1,2 @@
|
||||
-r requirements.txt # Include production deps
|
||||
pytest>=9.0.0
|
||||
81
requirements-dev.txt
Normal file
81
requirements-dev.txt
Normal file
@@ -0,0 +1,81 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.12
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile requirements-dev.in
|
||||
#
|
||||
base58==2.1.1
|
||||
# via -r requirements.txt
|
||||
bip-utils==2.10.0
|
||||
# via -r requirements.txt
|
||||
cbor2==5.8.0
|
||||
# via
|
||||
# -r requirements.txt
|
||||
# bip-utils
|
||||
cffi==2.0.0
|
||||
# via
|
||||
# -r requirements.txt
|
||||
# cryptography
|
||||
# pynacl
|
||||
coincurve==21.0.0
|
||||
# via
|
||||
# -r requirements.txt
|
||||
# bip-utils
|
||||
crcmod==1.7
|
||||
# via
|
||||
# -r requirements.txt
|
||||
# bip-utils
|
||||
cryptography==46.0.3
|
||||
# via
|
||||
# -r requirements.txt
|
||||
# pgpy
|
||||
ecdsa==0.19.1
|
||||
# via
|
||||
# -r requirements.txt
|
||||
# bip-utils
|
||||
ed25519-blake2b==1.4.1
|
||||
# via
|
||||
# -r requirements.txt
|
||||
# bip-utils
|
||||
iniconfig==2.3.0
|
||||
# via pytest
|
||||
packaging==25.0
|
||||
# via pytest
|
||||
pgpy==0.6.0
|
||||
# via -r requirements.txt
|
||||
pluggy==1.6.0
|
||||
# via pytest
|
||||
py-sr25519-bindings==0.2.3
|
||||
# via
|
||||
# -r requirements.txt
|
||||
# bip-utils
|
||||
pyasn1==0.6.1
|
||||
# via
|
||||
# -r requirements.txt
|
||||
# pgpy
|
||||
pycparser==2.23
|
||||
# via
|
||||
# -r requirements.txt
|
||||
# cffi
|
||||
pycryptodome==3.23.0
|
||||
# via
|
||||
# -r requirements.txt
|
||||
# bip-utils
|
||||
pycryptodomex==3.23.0
|
||||
# via
|
||||
# -r requirements.txt
|
||||
# pyzipper
|
||||
pygments==2.19.2
|
||||
# via pytest
|
||||
pynacl==1.6.2
|
||||
# via
|
||||
# -r requirements.txt
|
||||
# bip-utils
|
||||
pytest==9.0.2
|
||||
# via -r requirements-dev.in
|
||||
pyzipper==0.3.6
|
||||
# via -r requirements.txt
|
||||
six==1.17.0
|
||||
# via
|
||||
# -r requirements.txt
|
||||
# ecdsa
|
||||
@@ -1,2 +1,8 @@
|
||||
PGPy
|
||||
bip-utils
|
||||
# Core crypto libraries
|
||||
bip-utils>=2.9.0
|
||||
PGPy>=0.6.0
|
||||
pynacl>=1.5.0
|
||||
base58>=2.1.1
|
||||
|
||||
# ZIP encryption
|
||||
pyzipper>=0.3.6
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.11
|
||||
# This file is autogenerated by pip-compile with Python 3.12
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile
|
||||
# pip-compile requirements.in
|
||||
#
|
||||
base58==2.1.1
|
||||
# via -r requirements.in
|
||||
bip-utils==2.10.0
|
||||
# via -r requirements.in
|
||||
cbor2==5.8.0
|
||||
@@ -32,7 +34,13 @@ pycparser==2.23
|
||||
# via cffi
|
||||
pycryptodome==3.23.0
|
||||
# via bip-utils
|
||||
pycryptodomex==3.23.0
|
||||
# via pyzipper
|
||||
pynacl==1.6.2
|
||||
# via bip-utils
|
||||
# via
|
||||
# -r requirements.in
|
||||
# bip-utils
|
||||
pyzipper==0.3.6
|
||||
# via -r requirements.in
|
||||
six==1.17.0
|
||||
# via ecdsa
|
||||
|
||||
1215
src/pyhdwallet.py
1215
src/pyhdwallet.py
File diff suppressed because it is too large
Load Diff
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"master_fingerprint": "DD1449B7",
|
||||
"passphrase_used": false,
|
||||
"passphrase_hint": "",
|
||||
"dice_rolls_used": false,
|
||||
"solana_profile": "phantom_bip44change",
|
||||
"addresses": {
|
||||
"ethereum": [
|
||||
{
|
||||
"index": 0,
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"address": "0x9d3e3540f4C507ca992035607326798130051e03"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
104
tests/bootstrap_vectors.py
Normal file
104
tests/bootstrap_vectors.py
Normal file
@@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Bootstrap script to generate tests/vectors.json.
|
||||
Run this ONCE (online allowed if needed, though this logic is offline)
|
||||
to freeze the expected outputs of the current pyhdwallet implementation.
|
||||
"""
|
||||
import sys
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# Add src to path so we can import pyhdwallet
|
||||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../src")))
|
||||
|
||||
import pyhdwallet
|
||||
from bip_utils import Bip39SeedGenerator
|
||||
|
||||
def main():
|
||||
print("Bootstrapping test vectors...")
|
||||
vectors = {}
|
||||
|
||||
# 1. PGP Fingerprint Vector
|
||||
recipient_path = Path(__file__).parent / "data" / "recipient.asc"
|
||||
if not recipient_path.exists():
|
||||
print(f"Error: {recipient_path} not found. Please create it first.")
|
||||
sys.exit(1)
|
||||
|
||||
with open(recipient_path, "r", encoding="utf-8") as f:
|
||||
armored = f.read()
|
||||
|
||||
# Calculate expected fingerprint using current code logic
|
||||
expected_fpr = pyhdwallet.pgp_fingerprint(armored)
|
||||
|
||||
vectors["pgp"] = {
|
||||
"recipient_file": "recipient.asc",
|
||||
"expected_fingerprint": expected_fpr
|
||||
}
|
||||
print(f"PGP: Pinned fingerprint {expected_fpr}")
|
||||
|
||||
# 2. BIP39 & Derivation Vectors
|
||||
mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
|
||||
|
||||
vectors["bip39"] = []
|
||||
|
||||
# Case A: No Passphrase
|
||||
seed_bytes = Bip39SeedGenerator(mnemonic).Generate("")
|
||||
seed_hex = seed_bytes.hex()
|
||||
|
||||
# Generate addresses for all supported chains/profiles
|
||||
chains = ["ethereum", "bitcoin", "solana"]
|
||||
# Fixed profile names to use underscores instead of hyphens
|
||||
sol_profiles = ["phantom_bip44change", "phantom_bip44", "solana_bip39_first32"]
|
||||
|
||||
derived_data = {}
|
||||
|
||||
# Run derivation for each sol profile logic
|
||||
for profile in sol_profiles:
|
||||
res = pyhdwallet.derive_all(
|
||||
seed_bytes,
|
||||
chains,
|
||||
count=5,
|
||||
sol_profile=profile,
|
||||
export_private=False
|
||||
)
|
||||
derived_data[profile] = res["addresses"]
|
||||
|
||||
vectors["bip39"].append({
|
||||
"mnemonic": mnemonic,
|
||||
"passphrase": "",
|
||||
"expected_seed_hex": seed_hex,
|
||||
"derived_addresses": derived_data
|
||||
})
|
||||
|
||||
# Case B: With Passphrase (Regression test)
|
||||
passphrase = "TREZOR"
|
||||
seed_bytes_p = Bip39SeedGenerator(mnemonic).Generate(passphrase)
|
||||
seed_hex_p = seed_bytes_p.hex()
|
||||
|
||||
res_p = pyhdwallet.derive_all(
|
||||
seed_bytes_p,
|
||||
chains,
|
||||
count=1,
|
||||
sol_profile="phantom_bip44change",
|
||||
export_private=False
|
||||
)
|
||||
|
||||
vectors["bip39"].append({
|
||||
"mnemonic": mnemonic,
|
||||
"passphrase": passphrase,
|
||||
"expected_seed_hex": seed_hex_p,
|
||||
"derived_addresses": {"phantom_bip44change": res_p["addresses"]}
|
||||
})
|
||||
|
||||
print("Derivation: Generated vectors for empty and non-empty passphrases.")
|
||||
|
||||
# 3. Save
|
||||
out_path = Path(__file__).parent / "vectors.json"
|
||||
with open(out_path, "w", encoding="utf-8") as f:
|
||||
json.dump(vectors, f, indent=2)
|
||||
|
||||
print(f"Success! Vectors written to {out_path}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
91
tests/generate_bip85_vectors.py
Executable file
91
tests/generate_bip85_vectors.py
Executable file
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Generate BIP85 test vectors for vectors.json
|
||||
|
||||
Run this from the repo root:
|
||||
python3 tests/generate_bip85_vectors.py
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Add src to path
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
||||
|
||||
import pyhdwallet
|
||||
from bip_utils import Bip39SeedGenerator
|
||||
import json
|
||||
|
||||
# Test cases to generate
|
||||
test_cases = [
|
||||
{
|
||||
"description": "BIP85 test case 1: 12-word child, no passphrase",
|
||||
"master_mnemonic": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
"master_passphrase": "",
|
||||
"child_words": 12,
|
||||
"index": 0,
|
||||
},
|
||||
{
|
||||
"description": "BIP85 test case 2: 18-word child, no passphrase",
|
||||
"master_mnemonic": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
"master_passphrase": "",
|
||||
"child_words": 18,
|
||||
"index": 0,
|
||||
},
|
||||
{
|
||||
"description": "BIP85 test case 3: 24-word child, no passphrase",
|
||||
"master_mnemonic": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
"master_passphrase": "",
|
||||
"child_words": 24,
|
||||
"index": 0,
|
||||
},
|
||||
{
|
||||
"description": "BIP85 test case 4: 12-word child, WITH passphrase",
|
||||
"master_mnemonic": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
"master_passphrase": "TREZOR",
|
||||
"child_words": 12,
|
||||
"index": 0,
|
||||
},
|
||||
]
|
||||
|
||||
print("Generating BIP85 test vectors...\n")
|
||||
print("=" * 80)
|
||||
|
||||
vectors = []
|
||||
|
||||
for case in test_cases:
|
||||
print(f"\n{case['description']}")
|
||||
print("-" * 80)
|
||||
|
||||
# Generate master seed
|
||||
master_seed = Bip39SeedGenerator(case["master_mnemonic"]).Generate(case["master_passphrase"])
|
||||
|
||||
# Derive child using BIP85
|
||||
path, child_mnemonic, entropy64, truncated_entropy = pyhdwallet.bip85_derive_child_mnemonic(
|
||||
master_seed,
|
||||
case["child_words"],
|
||||
case["index"]
|
||||
)
|
||||
|
||||
vector = {
|
||||
"description": case["description"],
|
||||
"master_mnemonic": case["master_mnemonic"],
|
||||
"master_passphrase": case["master_passphrase"],
|
||||
"child_words": case["child_words"],
|
||||
"index": case["index"],
|
||||
"bip85_path": path,
|
||||
"expected_entropy64_hex": entropy64.hex(),
|
||||
"expected_entropy_truncated_hex": truncated_entropy.hex(),
|
||||
"expected_child_mnemonic": child_mnemonic,
|
||||
}
|
||||
|
||||
vectors.append(vector)
|
||||
|
||||
print(f"Path: {path}")
|
||||
print(f"Entropy64: {entropy64.hex()}")
|
||||
print(f"Truncated: {truncated_entropy.hex()}")
|
||||
print(f"Child mnemonic: {child_mnemonic}")
|
||||
|
||||
print("\n" + "=" * 80)
|
||||
print("\nJSON output for vectors.json:\n")
|
||||
print(json.dumps(vectors, indent=2))
|
||||
221
tests/test_vectors.py
Normal file
221
tests/test_vectors.py
Normal file
@@ -0,0 +1,221 @@
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
import pytest
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
# Add src to path at the very top
|
||||
TEST_DIR = Path(__file__).parent
|
||||
SRC_DIR = TEST_DIR.parent / "src"
|
||||
sys.path.insert(0, str(SRC_DIR))
|
||||
|
||||
import pyhdwallet
|
||||
from bip_utils import Bip39SeedGenerator
|
||||
|
||||
DATA_DIR = TEST_DIR / "data"
|
||||
VECTORS_FILE = TEST_DIR / "vectors.json"
|
||||
|
||||
@pytest.fixture
|
||||
def vectors():
|
||||
if not VECTORS_FILE.exists():
|
||||
pytest.fail("tests/vectors.json missing. Run tests/bootstrap_vectors.py first.")
|
||||
with open(VECTORS_FILE, "r") as f:
|
||||
return json.load(f)
|
||||
|
||||
@pytest.fixture
|
||||
def recipient_key_content():
|
||||
path = DATA_DIR / "recipient.asc"
|
||||
if not path.exists():
|
||||
pytest.fail("tests/data/recipient.asc missing.")
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
return f.read()
|
||||
|
||||
def test_pgp_fingerprint_calculation(vectors, recipient_key_content):
|
||||
"""
|
||||
Verifies that pgp_fingerprint computes the expected fingerprint
|
||||
for the stored recipient key.
|
||||
"""
|
||||
expected = vectors["pgp"]["expected_fingerprint"]
|
||||
actual = pyhdwallet.pgp_fingerprint(recipient_key_content)
|
||||
assert actual == expected, "Fingerprint calculation logic has changed!"
|
||||
|
||||
def test_pgp_require_fingerprint_match(vectors):
|
||||
"""
|
||||
Verifies the safety check require_fingerprint_match enforces exact matches.
|
||||
"""
|
||||
expected = vectors["pgp"]["expected_fingerprint"]
|
||||
|
||||
# Should not raise
|
||||
pyhdwallet.require_fingerprint_match(expected, expected, "test")
|
||||
|
||||
# Should raise on mismatch
|
||||
wrong = expected.replace("A", "B") if "A" in expected else expected.replace("0", "1")
|
||||
with pytest.raises(ValueError, match="test: PGP fingerprint mismatch"):
|
||||
pyhdwallet.require_fingerprint_match(wrong, expected, "test")
|
||||
|
||||
def test_bip39_seed_derivation(vectors):
|
||||
"""
|
||||
Verifies that mnemonics convert to seeds exactly as they did at bootstrap time.
|
||||
"""
|
||||
for case in vectors["bip39"]:
|
||||
mnemonic = case["mnemonic"]
|
||||
passphrase = case["passphrase"]
|
||||
expected_hex = case["expected_seed_hex"]
|
||||
|
||||
# Verify internal Bip39SeedGenerator usage matches expected hex
|
||||
actual_bytes = Bip39SeedGenerator(mnemonic).Generate(passphrase)
|
||||
assert actual_bytes.hex() == expected_hex
|
||||
|
||||
def test_address_derivation_integrity(vectors):
|
||||
"""
|
||||
Verifies derive_all produces the exact same addresses for supported chains.
|
||||
"""
|
||||
for case in vectors["bip39"]:
|
||||
seed_hex = case["expected_seed_hex"]
|
||||
seed_bytes = bytes.fromhex(seed_hex)
|
||||
|
||||
for profile, expected_addresses in case["derived_addresses"].items():
|
||||
# Infer count from ethereum addresses in the expected data
|
||||
# (Ethereum always generates `count` addresses, unlike Solana which depends on profile)
|
||||
count = len(expected_addresses.get("ethereum", []))
|
||||
|
||||
result = pyhdwallet.derive_all(
|
||||
seed_bytes,
|
||||
chains=["ethereum", "bitcoin", "solana"],
|
||||
count=count,
|
||||
sol_profile=profile,
|
||||
export_private=False
|
||||
)
|
||||
assert result["addresses"] == expected_addresses, f"Mismatch for profile {profile}"
|
||||
|
||||
def test_bip85_derivation(vectors):
|
||||
"""
|
||||
Verifies BIP85 child mnemonic derivation matches expected test vectors.
|
||||
|
||||
Tests:
|
||||
- Path construction
|
||||
- Entropy64 derivation (HMAC-SHA512)
|
||||
- Entropy truncation
|
||||
- Child mnemonic generation
|
||||
"""
|
||||
from bip_utils import Bip39SeedGenerator
|
||||
|
||||
for case in vectors["bip85"]:
|
||||
master_mnemonic = case["master_mnemonic"]
|
||||
master_passphrase = case["master_passphrase"]
|
||||
child_words = case["child_words"]
|
||||
index = case["index"]
|
||||
|
||||
# Generate master seed
|
||||
master_seed = Bip39SeedGenerator(master_mnemonic).Generate(master_passphrase)
|
||||
|
||||
# Derive child using BIP85
|
||||
path, child_mnemonic, entropy64, truncated_entropy = pyhdwallet.bip85_derive_child_mnemonic(
|
||||
master_seed,
|
||||
child_words,
|
||||
index
|
||||
)
|
||||
|
||||
# Assert path
|
||||
assert path == case["bip85_path"], f"Path mismatch: {path} != {case['bip85_path']}"
|
||||
|
||||
# Assert entropy64 (full 64-byte HMAC output)
|
||||
assert entropy64.hex() == case["expected_entropy64_hex"], \
|
||||
f"Entropy64 mismatch for {case['description']}"
|
||||
|
||||
# Assert truncated entropy
|
||||
assert truncated_entropy.hex() == case["expected_entropy_truncated_hex"], \
|
||||
f"Truncated entropy mismatch for {case['description']}"
|
||||
|
||||
# Assert child mnemonic
|
||||
assert child_mnemonic == case["expected_child_mnemonic"], \
|
||||
f"Child mnemonic mismatch for {case['description']}"
|
||||
|
||||
|
||||
def test_cli_gen_child_smoke(tmp_path, vectors):
|
||||
"""
|
||||
CLI smoke test for gen-child command.
|
||||
|
||||
Verifies:
|
||||
- Command runs without error
|
||||
- ZIP file is created
|
||||
- Off-screen mode works
|
||||
- File output works
|
||||
"""
|
||||
vector = vectors["bip85"][0] # Use first BIP85 test case
|
||||
master_mnemonic = vector["master_mnemonic"]
|
||||
|
||||
cmd = [
|
||||
sys.executable, "src/pyhdwallet.py", "gen-child",
|
||||
"--mnemonic-stdin",
|
||||
"--index", "0",
|
||||
"--words", "12",
|
||||
"--off-screen",
|
||||
"--file",
|
||||
"--zip-password-mode", "auto",
|
||||
"--wallet-location", str(tmp_path)
|
||||
]
|
||||
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
input=master_mnemonic.encode("utf-8"),
|
||||
capture_output=True
|
||||
)
|
||||
|
||||
assert result.returncode == 0, f"CLI gen-child failed: {result.stderr.decode()}"
|
||||
|
||||
# Check ZIP file created
|
||||
zips = list(tmp_path.glob("*.zip"))
|
||||
assert len(zips) == 1, f"Expected exactly one zip file, found {len(zips)}"
|
||||
|
||||
# Check filename pattern
|
||||
zip_name = zips[0].name
|
||||
assert zip_name.startswith("bip85_child_"), f"Unexpected zip name: {zip_name}"
|
||||
|
||||
|
||||
def test_cli_recover_smoke(tmp_path, vectors):
|
||||
"""
|
||||
Runs the CLI in a subprocess to verify end-to-end wiring
|
||||
without network (recover mode).
|
||||
"""
|
||||
vector = vectors["bip39"][0]
|
||||
mnemonic = vector["mnemonic"]
|
||||
expected_fp = vectors["pgp"]["expected_fingerprint"]
|
||||
recipient_file = DATA_DIR / "recipient.asc"
|
||||
|
||||
# 1. Successful Recovery
|
||||
cmd = [
|
||||
sys.executable, "src/pyhdwallet.py", "recover",
|
||||
"--mnemonic-stdin",
|
||||
"--pgp-pubkey-file", str(recipient_file),
|
||||
"--expected-fingerprint", expected_fp,
|
||||
"--file",
|
||||
"--wallet-location", str(tmp_path),
|
||||
"--off-screen",
|
||||
"--zip-password-mode", "auto"
|
||||
]
|
||||
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
input=mnemonic.encode("utf-8"),
|
||||
capture_output=True
|
||||
)
|
||||
|
||||
assert result.returncode == 0, f"CLI failed: {result.stderr.decode()}"
|
||||
zips = list(tmp_path.glob("*.zip"))
|
||||
assert len(zips) == 1, "Expected exactly one zip file output"
|
||||
|
||||
# 2. Failed Recovery (Wrong Fingerprint)
|
||||
wrong_fp = expected_fp.replace("A", "B")
|
||||
cmd_fail = list(cmd)
|
||||
cmd_fail[cmd_fail.index(expected_fp)] = wrong_fp
|
||||
|
||||
result_fail = subprocess.run(
|
||||
cmd_fail,
|
||||
input=mnemonic.encode("utf-8"),
|
||||
capture_output=True
|
||||
)
|
||||
|
||||
assert result_fail.returncode != 0
|
||||
assert b"fingerprint mismatch" in result_fail.stderr or b"fingerprint mismatch" in result_fail.stdout
|
||||
528
tests/vectors.json
Normal file
528
tests/vectors.json
Normal file
@@ -0,0 +1,528 @@
|
||||
{
|
||||
"pgp": {
|
||||
"recipient_file": "recipient.asc",
|
||||
"expected_fingerprint": "A27B96F2B169B5491013D2DA892B822C14A9AA18"
|
||||
},
|
||||
"bip39": [
|
||||
{
|
||||
"mnemonic": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
"passphrase": "",
|
||||
"expected_seed_hex": "5eb00bbddcf069084889a8ab9155568165f5c453ccb85e70811aaed6f6da5fc19a5ac40b389cd370d086206dec8aa6c43daea6690f20ad3d8d48b2d2ce9e38e4",
|
||||
"derived_addresses": {
|
||||
"phantom_bip44change": {
|
||||
"ethereum": [
|
||||
{
|
||||
"index": 0,
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"address": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94"
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"path": "m/44'/60'/0'/0/1",
|
||||
"address": "0x6Fac4D18c912343BF86fa7049364Dd4E424Ab9C0"
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"path": "m/44'/60'/0'/0/2",
|
||||
"address": "0xb6716976A3ebe8D39aCEB04372f22Ff8e6802D7A"
|
||||
},
|
||||
{
|
||||
"index": 3,
|
||||
"path": "m/44'/60'/0'/0/3",
|
||||
"address": "0xF3f50213C1d2e255e4B2bAD430F8A38EEF8D718E"
|
||||
},
|
||||
{
|
||||
"index": 4,
|
||||
"path": "m/44'/60'/0'/0/4",
|
||||
"address": "0x51cA8ff9f1C0a99f88E86B8112eA3237F55374cA"
|
||||
}
|
||||
],
|
||||
"solana": [
|
||||
{
|
||||
"index": 0,
|
||||
"path": "m/44'/501'/0'/0'",
|
||||
"address": "HAgk14JpMQLgt6rVgv7cBQFJWFto5Dqxi472uT3DKpqk"
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"path": "m/44'/501'/1'/0'",
|
||||
"address": "Hh8QwFUA6MtVu1qAoq12ucvFHNwCcVTV7hpWjeY1Hztb"
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"path": "m/44'/501'/2'/0'",
|
||||
"address": "7WktogJEd2wQ9eH2oWusmcoFTgeYi6rS632UviTBJ2jm"
|
||||
},
|
||||
{
|
||||
"index": 3,
|
||||
"path": "m/44'/501'/3'/0'",
|
||||
"address": "3YqEpfo3c818GhvbQ1UmVY1nJxw16vtu4JB9peJXT94k"
|
||||
},
|
||||
{
|
||||
"index": 4,
|
||||
"path": "m/44'/501'/4'/0'",
|
||||
"address": "6nod592sTfEWD3VSVPdQndLMVBCNmMc6ngt7MyGBK21j"
|
||||
}
|
||||
],
|
||||
"bitcoin": [
|
||||
{
|
||||
"index": 0,
|
||||
"path": "m/84'/0'/0'/0/0",
|
||||
"address_type": "native_segwit",
|
||||
"address": "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu"
|
||||
},
|
||||
{
|
||||
"index": 0,
|
||||
"path": "m/49'/0'/0'/0/0",
|
||||
"address_type": "segwit",
|
||||
"address": "37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf"
|
||||
},
|
||||
{
|
||||
"index": 0,
|
||||
"path": "m/44'/0'/0'/0/0",
|
||||
"address_type": "legacy",
|
||||
"address": "1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA"
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"path": "m/84'/0'/0'/0/1",
|
||||
"address_type": "native_segwit",
|
||||
"address": "bc1qnjg0jd8228aq7egyzacy8cys3knf9xvrerkf9g"
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"path": "m/49'/0'/0'/0/1",
|
||||
"address_type": "segwit",
|
||||
"address": "3LtMnn87fqUeHBUG414p9CWwnoV6E2pNKS"
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"path": "m/44'/0'/0'/0/1",
|
||||
"address_type": "legacy",
|
||||
"address": "1Ak8PffB2meyfYnbXZR9EGfLfFZVpzJvQP"
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"path": "m/84'/0'/0'/0/2",
|
||||
"address_type": "native_segwit",
|
||||
"address": "bc1qp59yckz4ae5c4efgw2s5wfyvrz0ala7rgvuz8z"
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"path": "m/49'/0'/0'/0/2",
|
||||
"address_type": "segwit",
|
||||
"address": "3B4cvWGR8X6Xs8nvTxVUoMJV77E4f7oaia"
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"path": "m/44'/0'/0'/0/2",
|
||||
"address_type": "legacy",
|
||||
"address": "1MNF5RSaabFwcbtJirJwKnDytsXXEsVsNb"
|
||||
},
|
||||
{
|
||||
"index": 3,
|
||||
"path": "m/84'/0'/0'/0/3",
|
||||
"address_type": "native_segwit",
|
||||
"address": "bc1qgl5vlg0zdl7yvprgxj9fevsc6q6x5dmcyk3cn3"
|
||||
},
|
||||
{
|
||||
"index": 3,
|
||||
"path": "m/49'/0'/0'/0/3",
|
||||
"address_type": "segwit",
|
||||
"address": "38CahkVftQneLonbWtfWxiiaT2fdnzsEAN"
|
||||
},
|
||||
{
|
||||
"index": 3,
|
||||
"path": "m/44'/0'/0'/0/3",
|
||||
"address_type": "legacy",
|
||||
"address": "1MVGa13XFvvpKGZdX389iU8b3qwtmAyrsJ"
|
||||
},
|
||||
{
|
||||
"index": 4,
|
||||
"path": "m/84'/0'/0'/0/4",
|
||||
"address_type": "native_segwit",
|
||||
"address": "bc1qm97vqzgj934vnaq9s53ynkyf9dgr05rargr04n"
|
||||
},
|
||||
{
|
||||
"index": 4,
|
||||
"path": "m/49'/0'/0'/0/4",
|
||||
"address_type": "segwit",
|
||||
"address": "37mbeJptxfQC6SNNLJ9a8efCY4BwBh5Kak"
|
||||
},
|
||||
{
|
||||
"index": 4,
|
||||
"path": "m/44'/0'/0'/0/4",
|
||||
"address_type": "legacy",
|
||||
"address": "1Gka4JdwhLxRwXaC6oLNH4YuEogeeSwqW7"
|
||||
}
|
||||
]
|
||||
},
|
||||
"phantom_bip44": {
|
||||
"ethereum": [
|
||||
{
|
||||
"index": 0,
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"address": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94"
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"path": "m/44'/60'/0'/0/1",
|
||||
"address": "0x6Fac4D18c912343BF86fa7049364Dd4E424Ab9C0"
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"path": "m/44'/60'/0'/0/2",
|
||||
"address": "0xb6716976A3ebe8D39aCEB04372f22Ff8e6802D7A"
|
||||
},
|
||||
{
|
||||
"index": 3,
|
||||
"path": "m/44'/60'/0'/0/3",
|
||||
"address": "0xF3f50213C1d2e255e4B2bAD430F8A38EEF8D718E"
|
||||
},
|
||||
{
|
||||
"index": 4,
|
||||
"path": "m/44'/60'/0'/0/4",
|
||||
"address": "0x51cA8ff9f1C0a99f88E86B8112eA3237F55374cA"
|
||||
}
|
||||
],
|
||||
"solana": [
|
||||
{
|
||||
"index": 0,
|
||||
"path": "m/44'/501'/0'",
|
||||
"address": "GjJyeC1r2RgkuoCWMyPYkCWSGSGLcz266EaAkLA27AhL"
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"path": "m/44'/501'/1'",
|
||||
"address": "ANf3TEKFL6jPWjzkndo4CbnNdUNkBk4KHPggJs2nu8Xi"
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"path": "m/44'/501'/2'",
|
||||
"address": "Ag74i82rUZBTgMGLacCA1ZLnotvAca8CLscXcrG6Nwem"
|
||||
},
|
||||
{
|
||||
"index": 3,
|
||||
"path": "m/44'/501'/3'",
|
||||
"address": "weCFpgyyyrzum6nA8XdmJXjDGDTXmG5P2DdgHv59hgQ"
|
||||
},
|
||||
{
|
||||
"index": 4,
|
||||
"path": "m/44'/501'/4'",
|
||||
"address": "4w6V162fV7HJQNma7vZvxjunqmkie8hM2x1DqaFQqxqL"
|
||||
}
|
||||
],
|
||||
"bitcoin": [
|
||||
{
|
||||
"index": 0,
|
||||
"path": "m/84'/0'/0'/0/0",
|
||||
"address_type": "native_segwit",
|
||||
"address": "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu"
|
||||
},
|
||||
{
|
||||
"index": 0,
|
||||
"path": "m/49'/0'/0'/0/0",
|
||||
"address_type": "segwit",
|
||||
"address": "37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf"
|
||||
},
|
||||
{
|
||||
"index": 0,
|
||||
"path": "m/44'/0'/0'/0/0",
|
||||
"address_type": "legacy",
|
||||
"address": "1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA"
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"path": "m/84'/0'/0'/0/1",
|
||||
"address_type": "native_segwit",
|
||||
"address": "bc1qnjg0jd8228aq7egyzacy8cys3knf9xvrerkf9g"
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"path": "m/49'/0'/0'/0/1",
|
||||
"address_type": "segwit",
|
||||
"address": "3LtMnn87fqUeHBUG414p9CWwnoV6E2pNKS"
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"path": "m/44'/0'/0'/0/1",
|
||||
"address_type": "legacy",
|
||||
"address": "1Ak8PffB2meyfYnbXZR9EGfLfFZVpzJvQP"
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"path": "m/84'/0'/0'/0/2",
|
||||
"address_type": "native_segwit",
|
||||
"address": "bc1qp59yckz4ae5c4efgw2s5wfyvrz0ala7rgvuz8z"
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"path": "m/49'/0'/0'/0/2",
|
||||
"address_type": "segwit",
|
||||
"address": "3B4cvWGR8X6Xs8nvTxVUoMJV77E4f7oaia"
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"path": "m/44'/0'/0'/0/2",
|
||||
"address_type": "legacy",
|
||||
"address": "1MNF5RSaabFwcbtJirJwKnDytsXXEsVsNb"
|
||||
},
|
||||
{
|
||||
"index": 3,
|
||||
"path": "m/84'/0'/0'/0/3",
|
||||
"address_type": "native_segwit",
|
||||
"address": "bc1qgl5vlg0zdl7yvprgxj9fevsc6q6x5dmcyk3cn3"
|
||||
},
|
||||
{
|
||||
"index": 3,
|
||||
"path": "m/49'/0'/0'/0/3",
|
||||
"address_type": "segwit",
|
||||
"address": "38CahkVftQneLonbWtfWxiiaT2fdnzsEAN"
|
||||
},
|
||||
{
|
||||
"index": 3,
|
||||
"path": "m/44'/0'/0'/0/3",
|
||||
"address_type": "legacy",
|
||||
"address": "1MVGa13XFvvpKGZdX389iU8b3qwtmAyrsJ"
|
||||
},
|
||||
{
|
||||
"index": 4,
|
||||
"path": "m/84'/0'/0'/0/4",
|
||||
"address_type": "native_segwit",
|
||||
"address": "bc1qm97vqzgj934vnaq9s53ynkyf9dgr05rargr04n"
|
||||
},
|
||||
{
|
||||
"index": 4,
|
||||
"path": "m/49'/0'/0'/0/4",
|
||||
"address_type": "segwit",
|
||||
"address": "37mbeJptxfQC6SNNLJ9a8efCY4BwBh5Kak"
|
||||
},
|
||||
{
|
||||
"index": 4,
|
||||
"path": "m/44'/0'/0'/0/4",
|
||||
"address_type": "legacy",
|
||||
"address": "1Gka4JdwhLxRwXaC6oLNH4YuEogeeSwqW7"
|
||||
}
|
||||
]
|
||||
},
|
||||
"solana_bip39_first32": {
|
||||
"ethereum": [
|
||||
{
|
||||
"index": 0,
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"address": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94"
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"path": "m/44'/60'/0'/0/1",
|
||||
"address": "0x6Fac4D18c912343BF86fa7049364Dd4E424Ab9C0"
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"path": "m/44'/60'/0'/0/2",
|
||||
"address": "0xb6716976A3ebe8D39aCEB04372f22Ff8e6802D7A"
|
||||
},
|
||||
{
|
||||
"index": 3,
|
||||
"path": "m/44'/60'/0'/0/3",
|
||||
"address": "0xF3f50213C1d2e255e4B2bAD430F8A38EEF8D718E"
|
||||
},
|
||||
{
|
||||
"index": 4,
|
||||
"path": "m/44'/60'/0'/0/4",
|
||||
"address": "0x51cA8ff9f1C0a99f88E86B8112eA3237F55374cA"
|
||||
}
|
||||
],
|
||||
"solana": [
|
||||
{
|
||||
"index": 0,
|
||||
"path": "BIP39 seed[0:32]",
|
||||
"address": "EHqmfkN89RJ7Y33CXM6uCzhVeuywHoJXZZLszBHHZy7o"
|
||||
}
|
||||
],
|
||||
"bitcoin": [
|
||||
{
|
||||
"index": 0,
|
||||
"path": "m/84'/0'/0'/0/0",
|
||||
"address_type": "native_segwit",
|
||||
"address": "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu"
|
||||
},
|
||||
{
|
||||
"index": 0,
|
||||
"path": "m/49'/0'/0'/0/0",
|
||||
"address_type": "segwit",
|
||||
"address": "37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf"
|
||||
},
|
||||
{
|
||||
"index": 0,
|
||||
"path": "m/44'/0'/0'/0/0",
|
||||
"address_type": "legacy",
|
||||
"address": "1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA"
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"path": "m/84'/0'/0'/0/1",
|
||||
"address_type": "native_segwit",
|
||||
"address": "bc1qnjg0jd8228aq7egyzacy8cys3knf9xvrerkf9g"
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"path": "m/49'/0'/0'/0/1",
|
||||
"address_type": "segwit",
|
||||
"address": "3LtMnn87fqUeHBUG414p9CWwnoV6E2pNKS"
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"path": "m/44'/0'/0'/0/1",
|
||||
"address_type": "legacy",
|
||||
"address": "1Ak8PffB2meyfYnbXZR9EGfLfFZVpzJvQP"
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"path": "m/84'/0'/0'/0/2",
|
||||
"address_type": "native_segwit",
|
||||
"address": "bc1qp59yckz4ae5c4efgw2s5wfyvrz0ala7rgvuz8z"
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"path": "m/49'/0'/0'/0/2",
|
||||
"address_type": "segwit",
|
||||
"address": "3B4cvWGR8X6Xs8nvTxVUoMJV77E4f7oaia"
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"path": "m/44'/0'/0'/0/2",
|
||||
"address_type": "legacy",
|
||||
"address": "1MNF5RSaabFwcbtJirJwKnDytsXXEsVsNb"
|
||||
},
|
||||
{
|
||||
"index": 3,
|
||||
"path": "m/84'/0'/0'/0/3",
|
||||
"address_type": "native_segwit",
|
||||
"address": "bc1qgl5vlg0zdl7yvprgxj9fevsc6q6x5dmcyk3cn3"
|
||||
},
|
||||
{
|
||||
"index": 3,
|
||||
"path": "m/49'/0'/0'/0/3",
|
||||
"address_type": "segwit",
|
||||
"address": "38CahkVftQneLonbWtfWxiiaT2fdnzsEAN"
|
||||
},
|
||||
{
|
||||
"index": 3,
|
||||
"path": "m/44'/0'/0'/0/3",
|
||||
"address_type": "legacy",
|
||||
"address": "1MVGa13XFvvpKGZdX389iU8b3qwtmAyrsJ"
|
||||
},
|
||||
{
|
||||
"index": 4,
|
||||
"path": "m/84'/0'/0'/0/4",
|
||||
"address_type": "native_segwit",
|
||||
"address": "bc1qm97vqzgj934vnaq9s53ynkyf9dgr05rargr04n"
|
||||
},
|
||||
{
|
||||
"index": 4,
|
||||
"path": "m/49'/0'/0'/0/4",
|
||||
"address_type": "segwit",
|
||||
"address": "37mbeJptxfQC6SNNLJ9a8efCY4BwBh5Kak"
|
||||
},
|
||||
{
|
||||
"index": 4,
|
||||
"path": "m/44'/0'/0'/0/4",
|
||||
"address_type": "legacy",
|
||||
"address": "1Gka4JdwhLxRwXaC6oLNH4YuEogeeSwqW7"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"mnemonic": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
"passphrase": "TREZOR",
|
||||
"expected_seed_hex": "c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04",
|
||||
"derived_addresses": {
|
||||
"phantom_bip44change": {
|
||||
"ethereum": [
|
||||
{
|
||||
"index": 0,
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"address": "0x9c32F71D4DB8Fb9e1A58B0a80dF79935e7256FA6"
|
||||
}
|
||||
],
|
||||
"solana": [
|
||||
{
|
||||
"index": 0,
|
||||
"path": "m/44'/501'/0'/0'",
|
||||
"address": "7zSmbu6gKkb6HB7UDPtHYjwCWuBHU1D4TpNZFm4sndQe"
|
||||
}
|
||||
],
|
||||
"bitcoin": [
|
||||
{
|
||||
"index": 0,
|
||||
"path": "m/84'/0'/0'/0/0",
|
||||
"address_type": "native_segwit",
|
||||
"address": "bc1qv5rmq0kt9yz3pm36wvzct7p3x6mtgehjul0feu"
|
||||
},
|
||||
{
|
||||
"index": 0,
|
||||
"path": "m/49'/0'/0'/0/0",
|
||||
"address_type": "segwit",
|
||||
"address": "3Aho3kS7vgVWKTpRHjcqBoPXiCujiSuTaZ"
|
||||
},
|
||||
{
|
||||
"index": 0,
|
||||
"path": "m/44'/0'/0'/0/0",
|
||||
"address_type": "legacy",
|
||||
"address": "1PEha8dk5Me5J1rZWpgqSt5F4BroTBLS5y"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"bip85": [
|
||||
{
|
||||
"description": "BIP85 test case 1: 12-word child, no passphrase",
|
||||
"master_mnemonic": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
"master_passphrase": "",
|
||||
"child_words": 12,
|
||||
"index": 0,
|
||||
"bip85_path": "m/83696968'/39'/0'/12'/0'",
|
||||
"expected_entropy64_hex": "ac98dac5d4f4ebad6056682ac95eb9ad9ba94fb68e96848264dad0b4357d002e41b3dd7a4c6f4ebc234be6938495840a73f59e9ba0e8e5c5208c94e6df2d7709",
|
||||
"expected_entropy_truncated_hex": "ac98dac5d4f4ebad6056682ac95eb9ad",
|
||||
"expected_child_mnemonic": "prosper short ramp prepare exchange stove life snack client enough purpose fold"
|
||||
},
|
||||
{
|
||||
"description": "BIP85 test case 2: 18-word child, no passphrase",
|
||||
"master_mnemonic": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
"master_passphrase": "",
|
||||
"child_words": 18,
|
||||
"index": 0,
|
||||
"bip85_path": "m/83696968'/39'/0'/18'/0'",
|
||||
"expected_entropy64_hex": "fc039f51d67ed7dfd01552f27de28887cf3e58655153e44b023d37578321f7083241970730e522d3f20b38a5296c5e51e57e0429546629704a09c6d1e2d10829",
|
||||
"expected_entropy_truncated_hex": "fc039f51d67ed7dfd01552f27de28887cf3e58655153e44b",
|
||||
"expected_child_mnemonic": "winter brother stamp provide uniform useful doctor prevent venue upper peasant auto view club next clerk tone fox"
|
||||
},
|
||||
{
|
||||
"description": "BIP85 test case 3: 24-word child, no passphrase",
|
||||
"master_mnemonic": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
"master_passphrase": "",
|
||||
"child_words": 24,
|
||||
"index": 0,
|
||||
"bip85_path": "m/83696968'/39'/0'/24'/0'",
|
||||
"expected_entropy64_hex": "d5a9cb46670566c4246b6e7af22e1dfc3668744ed831afea7ce2beea44e34e23e348e86091f24394f4be6253a7d5d24b91b1c4e0863b296e9e541e8018288897",
|
||||
"expected_entropy_truncated_hex": "d5a9cb46670566c4246b6e7af22e1dfc3668744ed831afea7ce2beea44e34e23",
|
||||
"expected_child_mnemonic": "stick exact spice sock filter ginger museum horse kit multiply manual wear grief demand derive alert quiz fault december lava picture immune decade jaguar"
|
||||
},
|
||||
{
|
||||
"description": "BIP85 test case 4: 12-word child, WITH passphrase",
|
||||
"master_mnemonic": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
"master_passphrase": "TREZOR",
|
||||
"child_words": 12,
|
||||
"index": 0,
|
||||
"bip85_path": "m/83696968'/39'/0'/12'/0'",
|
||||
"expected_entropy64_hex": "2b1d7c4f311137fa95f6302e64cdb88584d52b51b57d0430ee68e148b82baa3f8c40316397eb404f4573bc0c8e5c4bc14e4aa5f0f472a9d3587f494f1f7b3684",
|
||||
"expected_entropy_truncated_hex": "2b1d7c4f311137fa95f6302e64cdb885",
|
||||
"expected_child_mnemonic": "climb typical because giraffe beach wool fit ship common chapter hotel arm"
|
||||
}
|
||||
]
|
||||
}
|
||||
43
vendor/PROVENANCE.md
vendored
Normal file
43
vendor/PROVENANCE.md
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
# Dependency Provenance
|
||||
|
||||
All wheels generated on: 2026-01-07 17:06:51 UTC
|
||||
Python version: 3.12
|
||||
Build machine: Darwin arm64
|
||||
Docker used: Yes (python:3.12-slim)
|
||||
|
||||
## Platforms
|
||||
- macOS ARM64: Built using .venv312 (Python 3.12)
|
||||
- Linux x86_64: Built using Docker python:3.12-slim
|
||||
|
||||
## Package Versions
|
||||
base58==2.1.1
|
||||
bip_utils==2.10.0
|
||||
build==1.3.0
|
||||
cbor2==5.8.0
|
||||
cffi==2.0.0
|
||||
click==8.3.1
|
||||
coincurve==21.0.0
|
||||
crcmod==1.7
|
||||
cryptography==46.0.3
|
||||
ecdsa==0.19.1
|
||||
ed25519-blake2b==1.4.1
|
||||
iniconfig==2.3.0
|
||||
packaging==25.0
|
||||
PGPy==0.6.0
|
||||
pip-chill==1.0.3
|
||||
pip-tools==7.5.2
|
||||
pluggy==1.6.0
|
||||
py-sr25519-bindings==0.2.3
|
||||
pyasn1==0.6.1
|
||||
pycparser==2.23
|
||||
|
||||
## Integrity Verification
|
||||
Each platform directory contains SHA256SUMS for verification:
|
||||
```bash
|
||||
cd vendor/linux-x86_64
|
||||
shasum -a 256 -c SHA256SUMS
|
||||
```
|
||||
|
||||
Last updated: 2026-01-07
|
||||
Built by: LC mac <leochan@hkjin.com>
|
||||
Commit: 2807982
|
||||
31
vendor/linux-aarch64/SHA256SUMS
vendored
Normal file
31
vendor/linux-aarch64/SHA256SUMS
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
11a36f4d3ce51dfc1043f3218591ac4eb1ceb172919cebe05b52a5bcc8d245c2 base58-2.1.1-py3-none-any.whl
|
||||
33792674bda552a071a539b6590b2986aa8c08d0c9c30c2566d7cb323173310d bip_utils-2.10.0-py3-none-any.whl
|
||||
7145f0b5061ba90a1500d60bd1b13ca0a8a4cebdd0cc16ed8adf1c0e739f43b4 build-1.3.0-py3-none-any.whl
|
||||
518c118a5e00001854adb51f3164e647aa99b6a9877d2a733a28cb5c0a4d6857 cbor2-5.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
|
||||
b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062 cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
|
||||
981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6 click-8.3.1-py3-none-any.whl
|
||||
5a366c314df7217e3357bb8c7d2cda540b0bce180705f7a0ce2d1d9e28f62ad4 coincurve-21.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
|
||||
6a6b11d6c42a450359f0d7f2312cb1fe69493ae8314dc3f6674b95cefed469a2 crcmod-1.7-cp312-cp312-linux_aarch64.whl
|
||||
549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91 cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl
|
||||
30638e27cf77b7e15c4c4cc1973720149e1033827cfd00661ca5c8cc0cdb24c3 ecdsa-0.19.1-py2.py3-none-any.whl
|
||||
75c8d36691348abdd395b22f2c2ac5dd5c153c653550e6d18da5b0e433d7ce84 ed25519_blake2b-1.4.1-cp312-cp312-linux_aarch64.whl
|
||||
f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12 iniconfig-2.3.0-py3-none-any.whl
|
||||
29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484 packaging-25.0-py3-none-any.whl
|
||||
8d6f2b1a217ccefd933c7ec8bf69927e90d4630093d0af2404fe4e33703dcf0f pgpy-0.6.0-py3-none-any.whl
|
||||
9655943313a94722b7774661c21049070f6bbb0a1516bf02f7c8d5d9201514cd pip-25.3-py3-none-any.whl
|
||||
452a38edbcdfc333301c438c26ba00a0762d2034fe26a235d8587134453ccdb1 pip_chill-1.0.3-py2.py3-none-any.whl
|
||||
2fe16db727bbe5bf28765aeb581e792e61be51fc275545ef6725374ad720a1ce pip_tools-7.5.2-py3-none-any.whl
|
||||
e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 pluggy-1.6.0-py3-none-any.whl
|
||||
a3929c291408e67a1a11566f251b9f7d06c3fb3ae240caec44b9181de09e3fc9 py_sr25519_bindings-0.2.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
|
||||
0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629 pyasn1-0.6.1-py3-none-any.whl
|
||||
e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934 pycparser-2.23-py3-none-any.whl
|
||||
67bd81fcbe34f43ad9422ee8fd4843c8e7198dd88dd3d40e6de42ee65fbe1490 pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
|
||||
43c446e2ba8df8889e0e16f02211c25b4934898384c1ec1ec04d7889c0333587 pycryptodomex-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
|
||||
86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b pygments-2.19.2-py3-none-any.whl
|
||||
26bfcd00dcf2cf160f122186af731ae30ab120c18e8375684ec2670dccd28130 pynacl-1.6.2-cp38-abi3-manylinux_2_34_aarch64.whl
|
||||
9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913 pyproject_hooks-1.2.0-py3-none-any.whl
|
||||
711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b pytest-9.0.2-py3-none-any.whl
|
||||
6d097f465bfa47796b1494e12ea65d1478107d38e13bc56f6e58eedc4f6c1a87 pyzipper-0.3.6-py2.py3-none-any.whl
|
||||
062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922 setuptools-80.9.0-py3-none-any.whl
|
||||
4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 six-1.17.0-py2.py3-none-any.whl
|
||||
708e7481cc80179af0e556bbf0cc00b8444c7321e2700b8d8580231d13017248 wheel-0.45.1-py3-none-any.whl
|
||||
BIN
vendor/linux-aarch64/base58-2.1.1-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-aarch64/base58-2.1.1-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-aarch64/bip_utils-2.10.0-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-aarch64/bip_utils-2.10.0-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-aarch64/build-1.3.0-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-aarch64/build-1.3.0-py3-none-any.whl
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
vendor/linux-aarch64/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
vendored
Normal file
BIN
vendor/linux-aarch64/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-aarch64/click-8.3.1-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-aarch64/click-8.3.1-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-aarch64/coincurve-21.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
vendored
Normal file
BIN
vendor/linux-aarch64/coincurve-21.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-aarch64/crcmod-1.7-cp312-cp312-linux_aarch64.whl
vendored
Normal file
BIN
vendor/linux-aarch64/crcmod-1.7-cp312-cp312-linux_aarch64.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-aarch64/cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl
vendored
Normal file
BIN
vendor/linux-aarch64/cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-aarch64/ecdsa-0.19.1-py2.py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-aarch64/ecdsa-0.19.1-py2.py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-aarch64/ed25519_blake2b-1.4.1-cp312-cp312-linux_aarch64.whl
vendored
Normal file
BIN
vendor/linux-aarch64/ed25519_blake2b-1.4.1-cp312-cp312-linux_aarch64.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-aarch64/iniconfig-2.3.0-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-aarch64/iniconfig-2.3.0-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-aarch64/packaging-25.0-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-aarch64/packaging-25.0-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-aarch64/pgpy-0.6.0-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-aarch64/pgpy-0.6.0-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-aarch64/pip-25.3-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-aarch64/pip-25.3-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-aarch64/pip_chill-1.0.3-py2.py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-aarch64/pip_chill-1.0.3-py2.py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-aarch64/pip_tools-7.5.2-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-aarch64/pip_tools-7.5.2-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-aarch64/pluggy-1.6.0-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-aarch64/pluggy-1.6.0-py3-none-any.whl
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
vendor/linux-aarch64/pyasn1-0.6.1-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-aarch64/pyasn1-0.6.1-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-aarch64/pycparser-2.23-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-aarch64/pycparser-2.23-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-aarch64/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
vendored
Normal file
BIN
vendor/linux-aarch64/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-aarch64/pycryptodomex-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
vendored
Normal file
BIN
vendor/linux-aarch64/pycryptodomex-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-aarch64/pygments-2.19.2-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-aarch64/pygments-2.19.2-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-aarch64/pynacl-1.6.2-cp38-abi3-manylinux_2_34_aarch64.whl
vendored
Normal file
BIN
vendor/linux-aarch64/pynacl-1.6.2-cp38-abi3-manylinux_2_34_aarch64.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-aarch64/pyproject_hooks-1.2.0-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-aarch64/pyproject_hooks-1.2.0-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-aarch64/pytest-9.0.2-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-aarch64/pytest-9.0.2-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-aarch64/pyzipper-0.3.6-py2.py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-aarch64/pyzipper-0.3.6-py2.py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-aarch64/setuptools-80.9.0-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-aarch64/setuptools-80.9.0-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-aarch64/six-1.17.0-py2.py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-aarch64/six-1.17.0-py2.py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-aarch64/wheel-0.45.1-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-aarch64/wheel-0.45.1-py3-none-any.whl
vendored
Normal file
Binary file not shown.
31
vendor/linux-x86_64/SHA256SUMS
vendored
Normal file
31
vendor/linux-x86_64/SHA256SUMS
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
11a36f4d3ce51dfc1043f3218591ac4eb1ceb172919cebe05b52a5bcc8d245c2 base58-2.1.1-py3-none-any.whl
|
||||
33792674bda552a071a539b6590b2986aa8c08d0c9c30c2566d7cb323173310d bip_utils-2.10.0-py3-none-any.whl
|
||||
7145f0b5061ba90a1500d60bd1b13ca0a8a4cebdd0cc16ed8adf1c0e739f43b4 build-1.3.0-py3-none-any.whl
|
||||
518c118a5e00001854adb51f3164e647aa99b6a9877d2a733a28cb5c0a4d6857 cbor2-5.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
|
||||
b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062 cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
|
||||
981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6 click-8.3.1-py3-none-any.whl
|
||||
5a366c314df7217e3357bb8c7d2cda540b0bce180705f7a0ce2d1d9e28f62ad4 coincurve-21.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
|
||||
82d740f4e923b88981b6ca8b854dceb270f319c81153211ccbec6ee523c7ccee crcmod-1.7-cp312-cp312-linux_aarch64.whl
|
||||
549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91 cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl
|
||||
30638e27cf77b7e15c4c4cc1973720149e1033827cfd00661ca5c8cc0cdb24c3 ecdsa-0.19.1-py2.py3-none-any.whl
|
||||
96427209d130354f399ef20acc444c2ac76dd05b13cd270dea79909c39cdf6ec ed25519_blake2b-1.4.1-cp312-cp312-linux_aarch64.whl
|
||||
f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12 iniconfig-2.3.0-py3-none-any.whl
|
||||
29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484 packaging-25.0-py3-none-any.whl
|
||||
52a584525a9032726a8e28fc02ced9cec896f54aa5c6f5552035000821525f2d pgpy-0.6.0-py3-none-any.whl
|
||||
452a38edbcdfc333301c438c26ba00a0762d2034fe26a235d8587134453ccdb1 pip_chill-1.0.3-py2.py3-none-any.whl
|
||||
2fe16db727bbe5bf28765aeb581e792e61be51fc275545ef6725374ad720a1ce pip_tools-7.5.2-py3-none-any.whl
|
||||
9655943313a94722b7774661c21049070f6bbb0a1516bf02f7c8d5d9201514cd pip-25.3-py3-none-any.whl
|
||||
e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 pluggy-1.6.0-py3-none-any.whl
|
||||
a3929c291408e67a1a11566f251b9f7d06c3fb3ae240caec44b9181de09e3fc9 py_sr25519_bindings-0.2.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
|
||||
0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629 pyasn1-0.6.1-py3-none-any.whl
|
||||
e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934 pycparser-2.23-py3-none-any.whl
|
||||
67bd81fcbe34f43ad9422ee8fd4843c8e7198dd88dd3d40e6de42ee65fbe1490 pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
|
||||
43c446e2ba8df8889e0e16f02211c25b4934898384c1ec1ec04d7889c0333587 pycryptodomex-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
|
||||
86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b pygments-2.19.2-py3-none-any.whl
|
||||
26bfcd00dcf2cf160f122186af731ae30ab120c18e8375684ec2670dccd28130 pynacl-1.6.2-cp38-abi3-manylinux_2_34_aarch64.whl
|
||||
9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913 pyproject_hooks-1.2.0-py3-none-any.whl
|
||||
711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b pytest-9.0.2-py3-none-any.whl
|
||||
6d097f465bfa47796b1494e12ea65d1478107d38e13bc56f6e58eedc4f6c1a87 pyzipper-0.3.6-py2.py3-none-any.whl
|
||||
062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922 setuptools-80.9.0-py3-none-any.whl
|
||||
4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 six-1.17.0-py2.py3-none-any.whl
|
||||
708e7481cc80179af0e556bbf0cc00b8444c7321e2700b8d8580231d13017248 wheel-0.45.1-py3-none-any.whl
|
||||
BIN
vendor/linux-x86_64/base58-2.1.1-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-x86_64/base58-2.1.1-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-x86_64/bip_utils-2.10.0-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-x86_64/bip_utils-2.10.0-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-x86_64/build-1.3.0-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-x86_64/build-1.3.0-py3-none-any.whl
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
vendor/linux-x86_64/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
vendored
Normal file
BIN
vendor/linux-x86_64/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-x86_64/click-8.3.1-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-x86_64/click-8.3.1-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-x86_64/coincurve-21.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
vendored
Normal file
BIN
vendor/linux-x86_64/coincurve-21.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-x86_64/crcmod-1.7-cp312-cp312-linux_aarch64.whl
vendored
Normal file
BIN
vendor/linux-x86_64/crcmod-1.7-cp312-cp312-linux_aarch64.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-x86_64/cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl
vendored
Normal file
BIN
vendor/linux-x86_64/cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-x86_64/ecdsa-0.19.1-py2.py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-x86_64/ecdsa-0.19.1-py2.py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-x86_64/ed25519_blake2b-1.4.1-cp312-cp312-linux_aarch64.whl
vendored
Normal file
BIN
vendor/linux-x86_64/ed25519_blake2b-1.4.1-cp312-cp312-linux_aarch64.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-x86_64/iniconfig-2.3.0-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-x86_64/iniconfig-2.3.0-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-x86_64/packaging-25.0-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-x86_64/packaging-25.0-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-x86_64/pgpy-0.6.0-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-x86_64/pgpy-0.6.0-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-x86_64/pip-25.3-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-x86_64/pip-25.3-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-x86_64/pip_chill-1.0.3-py2.py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-x86_64/pip_chill-1.0.3-py2.py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-x86_64/pip_tools-7.5.2-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-x86_64/pip_tools-7.5.2-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-x86_64/pluggy-1.6.0-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-x86_64/pluggy-1.6.0-py3-none-any.whl
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
vendor/linux-x86_64/pyasn1-0.6.1-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-x86_64/pyasn1-0.6.1-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-x86_64/pycparser-2.23-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-x86_64/pycparser-2.23-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-x86_64/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
vendored
Normal file
BIN
vendor/linux-x86_64/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-x86_64/pycryptodomex-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
vendored
Normal file
BIN
vendor/linux-x86_64/pycryptodomex-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-x86_64/pygments-2.19.2-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-x86_64/pygments-2.19.2-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-x86_64/pynacl-1.6.2-cp38-abi3-manylinux_2_34_aarch64.whl
vendored
Normal file
BIN
vendor/linux-x86_64/pynacl-1.6.2-cp38-abi3-manylinux_2_34_aarch64.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-x86_64/pyproject_hooks-1.2.0-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-x86_64/pyproject_hooks-1.2.0-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-x86_64/pytest-9.0.2-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-x86_64/pytest-9.0.2-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-x86_64/pyzipper-0.3.6-py2.py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-x86_64/pyzipper-0.3.6-py2.py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-x86_64/setuptools-80.9.0-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-x86_64/setuptools-80.9.0-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-x86_64/six-1.17.0-py2.py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-x86_64/six-1.17.0-py2.py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/linux-x86_64/wheel-0.45.1-py3-none-any.whl
vendored
Normal file
BIN
vendor/linux-x86_64/wheel-0.45.1-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/macos-arm64-docker/base58-2.1.1-py3-none-any.whl
vendored
Normal file
BIN
vendor/macos-arm64-docker/base58-2.1.1-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/macos-arm64-docker/bip_utils-2.10.0-py3-none-any.whl
vendored
Normal file
BIN
vendor/macos-arm64-docker/bip_utils-2.10.0-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/macos-arm64-docker/build-1.3.0-py3-none-any.whl
vendored
Normal file
BIN
vendor/macos-arm64-docker/build-1.3.0-py3-none-any.whl
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
vendor/macos-arm64-docker/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
vendored
Normal file
BIN
vendor/macos-arm64-docker/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/macos-arm64-docker/click-8.3.1-py3-none-any.whl
vendored
Normal file
BIN
vendor/macos-arm64-docker/click-8.3.1-py3-none-any.whl
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
vendor/macos-arm64-docker/crcmod-1.7-cp312-cp312-linux_aarch64.whl
vendored
Normal file
BIN
vendor/macos-arm64-docker/crcmod-1.7-cp312-cp312-linux_aarch64.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/macos-arm64-docker/cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl
vendored
Normal file
BIN
vendor/macos-arm64-docker/cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/macos-arm64-docker/ecdsa-0.19.1-py2.py3-none-any.whl
vendored
Normal file
BIN
vendor/macos-arm64-docker/ecdsa-0.19.1-py2.py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/macos-arm64-docker/ed25519_blake2b-1.4.1-cp312-cp312-linux_aarch64.whl
vendored
Normal file
BIN
vendor/macos-arm64-docker/ed25519_blake2b-1.4.1-cp312-cp312-linux_aarch64.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/macos-arm64-docker/iniconfig-2.3.0-py3-none-any.whl
vendored
Normal file
BIN
vendor/macos-arm64-docker/iniconfig-2.3.0-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/macos-arm64-docker/packaging-25.0-py3-none-any.whl
vendored
Normal file
BIN
vendor/macos-arm64-docker/packaging-25.0-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/macos-arm64-docker/pgpy-0.6.0-py3-none-any.whl
vendored
Normal file
BIN
vendor/macos-arm64-docker/pgpy-0.6.0-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/macos-arm64-docker/pip-25.3-py3-none-any.whl
vendored
Normal file
BIN
vendor/macos-arm64-docker/pip-25.3-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/macos-arm64-docker/pip_chill-1.0.3-py2.py3-none-any.whl
vendored
Normal file
BIN
vendor/macos-arm64-docker/pip_chill-1.0.3-py2.py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
vendor/macos-arm64-docker/pip_tools-7.5.2-py3-none-any.whl
vendored
Normal file
BIN
vendor/macos-arm64-docker/pip_tools-7.5.2-py3-none-any.whl
vendored
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user