Skip to main content

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 sendrawtransaction RPC

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

# From PyPI
pip install flux-delegate-starter

Or install from source:

# Clone the repository
git clone https://github.com/RunOnFlux/flux-delegate-starter.git
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):

  • --collateral-pubkey: P2PKH collateral public key (hex, e.g. Zelcore t1...), OR
  • --collateral-redeemscript: P2SH multisig collateral redeem script (hex, e.g. SSP t3...)
  • --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)

P2SH / SSP collateral:

For a multisig collateral created through SSP (an address starting t3...), pass its redeem script instead of a pubkey. SSP shows the Redeem Script in the node's details.

flux-delegate-starter start <collateral_txhash> \
  --collateral-redeemscript <redeem_script_hex> \
  --fluxnode-privkey <fluxnode_wif> \
  --delegate-key-file delegate.key.age

batch collaterals (via YAML) only support P2PKH (--collateral-pubkey) collateral.

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:

  1. Command-line option:
flux-delegate-starter batch --passphrase "mypassphrase" collaterals.yaml
  1. 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):

  • --collateral-pubkey: P2PKH collateral public key (hex), OR
  • --collateral-redeemscript: P2SH multisig collateral redeem script (hex)
  • --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 0 or 1 (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:

  1. Export the private key from Zelcore for your collateral address
  2. 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

  1. Serialize transaction without signature
  2. Compute transaction hash (double SHA256)
  3. Build message: "Zelcash Signed Message:\n" + tx_hash_hex
  4. Hash message with SHA256
  5. 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 ty check
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 .env files (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-run and --decode for testing
  • Double-check collateral amounts
  • Verify fluxnode public keys

Examples

See the examples/ directory for:

  • .env.example - Environment configuration
  • config.example.toml - TOML configuration
  • collaterals.example.yaml - Batch file format

License

MIT License - see LICENSE file for details

Contributing

Contributions welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new features
  4. Ensure all tests pass
  5. Submit a pull request

Support

Acknowledgments

  • Built for the Flux ecosystem
  • Uses secp256k1 cryptography via coincurve
  • Transaction format follows Bitcoin-style serialization
  • Inspired by fluxd's activefluxnode implementation

Metadata

Release files for flux-delegate-starter 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for flux-delegate-starter 0.2.0
File Size Uploaded
flux_delegate_starter-0.2.0.tar.gz 31.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for flux-delegate-starter 0.2.0
File Interpreter ABI Platform
flux_delegate_starter-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 67.9 kB

Release files / flux_delegate_starter-0.2.0.tar.gz

Download URL flux_delegate_starter-0.2.0.tar.gz
Size 31.8 kB
Tags Source
SHA-256 checksum
How to use checksums
546b15c6b5b82ea950d9392c13b0766d485931afef85cbdd4eac5ae71bc7f08f
BLAKE2b-256 checksum
How to use checksums
36d7ae85bf7f654a9ea31268d0c647330b5e3e5d7751538ec5298f9d8a71390a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / flux_delegate_starter-0.2.0-py3-none-any.whl

Download URL flux_delegate_starter-0.2.0-py3-none-any.whl
Size 36.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
194beba0c5ad4c99cb2e6f1c69a810ef02126422e79138801bdf3bcbd1be286a
BLAKE2b-256 checksum
How to use checksums
2e679c77414e4b3d5b3c25360303fe3f5e7a1db9ab208ba54d5c6fa32cea4d37
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page