Build and send raw delegated fluxnode START transactions
Project description
flux-delegate-starter
Build and send raw delegated fluxnode START transactions from scratch in Python.
Overview
This tool builds raw fluxnode START transactions manually, bypassing the high-level RPC commands. It provides complete control over transaction building, signing, and broadcasting.
What it does:
- Builds raw fluxnode START transactions (version 6 with delegates)
- Serializes all transaction fields according to fluxnode protocol
- Signs transactions with secp256k1 delegate keys
- Sends transactions via
sendrawtransactionRPC
Why use this instead of startfluxnodeasdelegate?
- Complete control over transaction building
- No need for wallet access
- Can run anywhere with RPC access
- Educational: understand transaction format
- Batch operations with YAML files
- Better for automation and scripting
Features
- ✅ Raw transaction building from scratch
- ✅ Compact recoverable signature (secp256k1)
- ✅ Zelcash message signing format
- ✅ Batch mode with YAML files
- ✅ Dry-run mode for testing
- ✅ Transaction decoding for debugging
- ✅ Rich CLI output with progress tracking
- ✅ Comprehensive test suite
Installation
Prerequisites
- Python 3.13+
- uv package manager
- Access to a Flux RPC node
Install uv
curl -LsSf https://astral.sh/uv/install.sh | sh
Install flux-delegate-starter
# Clone the repository
git clone <repo-url>
cd flux-delegate-starter
# Install with uv
uv sync
# Or install in development mode
uv sync --dev
Verify Installation
# Activate the virtual environment
source .venv/bin/activate
# Test the CLI
flux-delegate-starter --help
Quick Start
1. Test RPC Connection
flux-delegate-starter test-connection --rpc-user <username> --rpc-password <password>
2. Start a Single Fluxnode
flux-delegate-starter start <collateral_txhash> \
--fluxnode-privkey <fluxnode_wif> \
--delegate-key <delegate_wif> \
--rpc-user <username> \
--rpc-password <password>
Example:
flux-delegate-starter start \
1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d \
--fluxnode-privkey KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn \
--delegate-key L3MnxQhwxBvKiJ5TKxHgCdeWPRzkYSdXqXT3qYjG8aE9XfC7vZqQ \
--rpc-user myuser \
--rpc-password mypassword
3. Dry Run (Build but Don't Send)
flux-delegate-starter start <args> --dry-run
4. Decode Transaction
flux-delegate-starter start <args> --decode --dry-run
5. Batch Start Multiple Fluxnodes
Create a YAML file with your fluxnodes:
# collaterals.yaml
fluxnodes:
# Minimal example (collateral_index defaults to 0)
- label: "node-1"
collateral_txhash: "1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d0"
delegate_key: "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn"
fluxnode_pubkey: "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
# Using fluxnode_privkey (pubkey will be derived)
- label: "node-2"
collateral_txhash: "2075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d0"
collateral_index: 1
delegate_key: "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn"
fluxnode_privkey: "L3MnQhBwxvKiJ5TKxHgCdeWPRzkYSdXqXT3qYjG8aE9XfC7vZqQ"
# Using encrypted delegate key file
- label: "node-3"
collateral_txhash: "3075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d0"
delegate_key_file: "delegate.key.age"
fluxnode_pubkey: "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
Then run:
# Using default API transport (no credentials needed)
flux-delegate-starter batch collaterals.yaml
# With encrypted key files (passphrase via stdin)
echo "mypassphrase" | flux-delegate-starter batch collaterals.yaml
# Or provide passphrase via option
flux-delegate-starter batch --passphrase "mypassphrase" collaterals.yaml
See examples/collaterals.example.yaml for more detailed examples.
CLI Commands
start
Start a single fluxnode.
flux-delegate-starter start [OPTIONS] COLLATERAL_TXHASH
Required Options (one of each):
--fluxnode-pubkey: Fluxnode public key (hex), OR--fluxnode-privkey: Fluxnode private key (WIF) - will derive pubkey--delegate-key: Delegate private key (WIF), OR--delegate-key-file: Path to delegate key file
RPC Options:
--rpc-user: RPC username (required)--rpc-password: RPC password (required)
Optional:
--index,-i: Collateral output index (default: 0)--dry-run,-n: Build transaction but don't send--decode: Decode transaction before sending--host: RPC host (default: 127.0.0.1)--port: RPC port (default: 16124)
batch
Start multiple fluxnodes from a YAML file.
flux-delegate-starter batch [OPTIONS] YAML_FILE
Options:
--dry-run,-n: Build transactions but don't send--continue-on-error: Continue if a transaction fails (default: true)--passphrase,-p: Passphrase for encrypted key files (applies to all files in batch)--transport,-t: Transport method: 'api' (default) or 'rpc'--host: RPC host (only for --transport rpc, default: 127.0.0.1)--port: RPC port (only for --transport rpc, default: 16124)--rpc-user: RPC username (only for --transport rpc)--rpc-password: RPC password (only for --transport rpc)
Encryption Support:
If your YAML file uses encrypted delegate key files (.key.age), provide the passphrase via:
- Command-line option:
flux-delegate-starter batch --passphrase "mypassphrase" collaterals.yaml
- Stdin (for scripting):
echo "mypassphrase" | flux-delegate-starter batch collaterals.yaml
Note: The same passphrase is used for ALL encrypted key files in the batch. If your files use different passphrases, run multiple batch commands.
build
Build transaction and output hex only (for scripting).
flux-delegate-starter build COLLATERAL_TXHASH [OPTIONS]
Required Options (one of each):
--fluxnode-pubkey: Fluxnode public key (hex), OR--fluxnode-privkey: Fluxnode private key (WIF) - will derive pubkey--delegate-key: Delegate private key (WIF), OR--delegate-key-file: Path to delegate key file
Optional:
--index,-i: Collateral output index (default: 0)
Outputs raw hex to stdout (no formatting).
derive-pubkey
Derive public key from a WIF private key.
flux-delegate-starter derive-pubkey WIF_PRIVATE_KEY
This utility helps you convert your private keys to public keys. Very useful when you have:
- Collateral private key → need collateral public key
- Fluxnode private key → need fluxnode public key
Example:
$ flux-delegate-starter derive-pubkey KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn
✓ Derived public key:
0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
test-connection
Test RPC connection to Flux daemon.
flux-delegate-starter test-connection [OPTIONS]
Getting Your Keys
This section explains how to get all the required parameters for the start command.
1. Collateral Transaction Hash & Index
From your wallet that sent the collateral:
# Find your collateral transaction
flux-cli listtransactions
# Get details
flux-cli gettransaction <txid>
Or from Zelcore: Check your transaction history for the 1000/12500/40000 FLUX send.
- collateral_txhash: The transaction ID (64 hex characters)
- collateral_index: Usually
0or1(which output in the transaction)
2. Collateral Public Key
You need the public key for the address that holds the collateral.
Option A: From Zelcore Zelcore doesn't easily show public keys, so you'll need the private key:
- Export the private key from Zelcore for your collateral address
- Use our utility:
flux-delegate-starter derive-pubkey <your_collateral_private_key_WIF>
Option B: From flux-cli If you have the collateral address in flux-cli wallet:
# Get the address that holds collateral
flux-cli getaddressesbyaccount ""
# Get the private key (KEEP THIS SECURE!)
flux-cli dumpprivkey <your_collateral_address>
# Derive public key
flux-delegate-starter derive-pubkey <WIF_from_above>
3. Delegate Private Key
This is a new key you generate just for signing START transactions. You can:
Option A: Generate with flux-cli
# Generate a new key pair
flux-cli getnewaddress
# Export the private key
flux-cli dumpprivkey <new_address>
Option B: Use your collateral private key (simpler but less secure) You can use the same WIF as your collateral private key. This is fine for testing.
The delegate_key parameter expects WIF format directly (e.g., KwDi...)
4. Fluxnode Public Key
Each fluxnode needs its own identity key.
Option A: From your fluxnode If you're running a fluxnode:
# On the fluxnode server
flux-cli getfluxnodestatus | grep pubkey
Option B: Generate new keys
# Generate a new address
flux-cli getnewaddress
# Dump the private key
flux-cli dumpprivkey <address>
# Derive the public key
flux-delegate-starter derive-pubkey <WIF_private_key>
For multi-node benchmarks, generate one key pair per node.
Summary: What You Actually Need
# 1. Find your collateral transaction
collateral_txhash="abc123..." # From wallet/blockchain
# 2. Get your RPC credentials
rpc_user=$(grep rpcuser ~/.flux/flux.conf | cut -d= -f2)
rpc_password=$(grep rpcpassword ~/.flux/flux.conf | cut -d= -f2)
# 3. Delegate key (can be same as collateral for simplicity)
delegate_key="L..." # Export from Zelcore or flux-cli
# 4. Fluxnode privkey (generate new for each node)
fluxnode_privkey="K..." # Generate with flux-cli getnewaddress
# 5. Build the transaction (pubkey will be derived automatically)
flux-delegate-starter start \
$collateral_txhash \
--fluxnode-privkey $fluxnode_privkey \
--delegate-key $delegate_key \
--rpc-user $rpc_user \
--rpc-password $rpc_password \
--dry-run
Configuration
Environment Variables
Create a .env file:
FLUX_RPC_HOST=127.0.0.1
FLUX_RPC_PORT=16124
FLUX_RPC_TIMEOUT=30
FLUX_OUTPUT_FORMAT=rich
See examples/.env.example for all options.
RPC Authentication
The tool requires username/password authentication for all RPC connections.
Get credentials from your flux.conf:
grep rpcuser ~/.flux/flux.conf
grep rpcpassword ~/.flux/flux.conf
Pass via CLI:
flux-delegate-starter start <args> --rpc-user <user> --rpc-password <pass>
Or use environment variables:
export FLUX_RPC_USERNAME=myuser
export FLUX_RPC_PASSWORD=mypassword
flux-delegate-starter start <args> --rpc-user $FLUX_RPC_USERNAME --rpc-password $FLUX_RPC_PASSWORD
Transaction Format
Structure
Header (4 bytes) # nVersion | (fOverwintered << 31)
├─ nType: int8_t (0x02) # FLUXNODE_START_TX_TYPE
├─ nFluxTxVersion: int32_t # 0x0101 (NORMAL | DELEGATES)
├─ collateralIn: COutPoint # 36 bytes (hash + index)
│ ├─ hash: uint256 # 32 bytes, little-endian
│ └─ n: uint32_t # 4 bytes, little-endian
├─ collateralPubkey: CPubKey # Compact size + 33 bytes
├─ pubKey: CPubKey # Fluxnode key, compact size + 33 bytes
├─ sigTime: uint32_t # 4 bytes, little-endian
├─ fUsingDelegates: bool # 1 byte (0x01)
├─ delegateData: # CFluxnodeDelegates
│ ├─ nDelegateVersion: int8_t (0x01)
│ ├─ nType: int8_t (0x02) # SIGNING type
│ └─ (no keys for SIGNING)
└─ sig: vector<unsigned char> # Compact size + 65 bytes
Byte Order
- All integers: Little-endian
- Transaction hash: Reversed (little-endian)
- Public keys: No reversal
Signing Process
- Serialize transaction without signature
- Compute transaction hash (double SHA256)
- Build message:
"Zelcash Signed Message:\n" + tx_hash_hex - Hash message with SHA256
- Sign with secp256k1 recoverable signature (65 bytes)
Development
Running Tests
# Run all tests
uv run pytest
# Run with coverage
uv run pytest --cov
# Run specific test file
uv run pytest tests/test_transaction.py
# Run specific test
uv run pytest tests/test_crypto.py::TestWIFDecoding::test_decode_mainnet_compressed
Linting and Formatting
# Format code
uv run ruff format
# Lint code
uv run ruff check
# Type checking
uv run pyright
Project Structure
flux-delegate-starter/
├── src/
│ └── flux_delegate_starter/
│ ├── __init__.py # Package exports
│ ├── __main__.py # CLI interface
│ ├── transaction.py # Transaction building
│ ├── crypto.py # Cryptographic operations
│ ├── rpc_client.py # RPC client
│ ├── models.py # Pydantic models
│ ├── config.py # Configuration
│ └── exceptions.py # Custom exceptions
├── tests/
│ ├── test_transaction.py # Transaction tests
│ ├── test_crypto.py # Crypto tests
│ └── test_rpc_client.py # RPC tests
├── examples/
│ ├── .env.example # Environment config
│ ├── config.example.toml # TOML config
│ └── collaterals.example.yaml # Batch file example
├── pyproject.toml # Project config
└── README.md # This file
Library Usage
You can also use the library programmatically in your Python code:
import asyncio
from flux_delegate_starter import (
FluxnodeStartTransaction,
FluxRPCClient,
FluxAPIClient,
decode_wif,
)
async def main():
# Decode delegate key
delegate_privkey, _ = decode_wif("KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn")
# Build transaction
tx = FluxnodeStartTransaction(
collateral_txhash="1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d0",
collateral_index=0,
fluxnode_pubkey=bytes.fromhex("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"),
)
# Sign transaction
tx.sign(delegate_privkey)
# Option 1: Send via public API (default, no credentials needed)
api_client = FluxAPIClient()
txid = await api_client.send_raw_transaction(tx.to_hex())
print(f"Transaction sent via API: {txid}")
# Option 2: Send via direct RPC (requires credentials)
rpc_client = FluxRPCClient(
host="127.0.0.1",
port=16124,
username="myuser",
password="mypassword"
)
txid = await rpc_client.send_raw_transaction(tx.to_hex())
print(f"Transaction sent via RPC: {txid}")
if __name__ == "__main__":
asyncio.run(main())
Troubleshooting
"RPC authentication required"
You must provide RPC credentials. Get them from your flux.conf:
grep rpcuser ~/.flux/flux.conf
grep rpcpassword ~/.flux/flux.conf
Then use:
flux-delegate-starter start <args> --rpc-user <user> --rpc-password <pass>
"TX decode failed"
This usually means:
- Incorrect byte order (transaction hash must be reversed)
- Invalid public key format (must be compressed, 33 bytes)
- Incorrect serialization
Use --decode --dry-run to debug:
flux-delegate-starter start <args> --decode --dry-run
"bad-txns-in-belowout"
The collateral transaction output doesn't exist or is already spent.
Verify with:
flux-cli getrawtransaction <txhash> 1
"Invalid signature"
Check:
- Delegate key is correct WIF format
- Public keys match the delegate key
- Transaction is signed before sending
Security Considerations
Private Key Handling
- Never commit private keys to version control
- Use
.envfiles (add to.gitignore) - Consider using encrypted storage
- Rotate keys periodically
RPC Security
- Use RPC over localhost when possible
- Enable RPC authentication
- Use TLS for remote connections
- Firewall RPC port
Transaction Validation
- Always verify transaction details before signing
- Use
--dry-runand--decodefor testing - Double-check collateral amounts
- Verify fluxnode public keys
Examples
See the examples/ directory for:
.env.example- Environment configurationconfig.example.toml- TOML configurationcollaterals.example.yaml- Batch file format
License
MIT License - see LICENSE file for details
Contributing
Contributions welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new features
- Ensure all tests pass
- Submit a pull request
Support
- GitHub Issues: [repo-url]/issues
- Documentation: [docs-url]
- Discord: [discord-url]
Acknowledgments
- Built for the Flux ecosystem
- Uses secp256k1 cryptography via coincurve
- Transaction format follows Bitcoin-style serialization
- Inspired by fluxd's activefluxnode implementation
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file flux_delegate_starter-0.1.0.tar.gz.
File metadata
- Download URL: flux_delegate_starter-0.1.0.tar.gz
- Upload date:
- Size: 28.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.6.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d16bf3f9f365d63d17c9abef93fd4f7aa0f0bd121b3ab2a8ad499287f9fcce1f
|
|
| MD5 |
557124454f251bd7be403521643d77e3
|
|
| BLAKE2b-256 |
8e3228bdc52882d1efccd2deebb8ecddc42a13bbe8b1a9e573cd43b6132cb150
|
File details
Details for the file flux_delegate_starter-0.1.0-py3-none-any.whl.
File metadata
- Download URL: flux_delegate_starter-0.1.0-py3-none-any.whl
- Upload date:
- Size: 32.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.6.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ef10fac8c2f4ac87e53dadaf5f8f6dc92428699849702c3a7812ebb4331c582b
|
|
| MD5 |
259ef208ffd2353e3f1247d4ba187455
|
|
| BLAKE2b-256 |
34a87550d1e3ffe8ecdd97b43a8418fb18d4fa82f40c6ad227ea86febfff5ed3
|