A generalized command-line interface for YadaCoin node management and configuration.
The YadaCoin CLI provides a hierarchical command structure for managing node operations, with a focus on configuration management. The interface is designed to be extensible, allowing new commands and subcommands to be added easily.
cli/
├── __init__.py # Package marker
├── cli.py # Main entry point and command router
├── README.md # This file
└── commands/
├── __init__.py # Package marker
└── config/
├── __init__.py # Package marker
├── config_base.py # Base class for config updaters
└── update_username.py # Username update handler
- Python 3.7+
- Virtual environment activated (if using
venv37) - Dependencies installed via
requirements.txt
Run the CLI from the project root:
python3 cli/cli.py --helpView available commands:
python3 cli/cli.py config --helpOperations related to modifying config.json settings.
Update the node's username and automatically recalculate the username signature.
Signature:
python3 cli/cli.py config set-username -u USERNAME [--config PATH] [--confirm-backup]
Arguments:
-u, --username USERNAME(required): New username for the node-c, --config PATH(optional): Path to config.json (default:config/config.json)--confirm-backup(optional): Skip interactive backup confirmation (assumes environment has backed up)
Examples:
Interactive mode (prompts for backup confirmation):
python3 cli/cli.py config set-username -u "my-node"Non-interactive mode (for scripts/automation):
python3 cli/cli.py config set-username -u "my-node" --confirm-backupCustom config file:
python3 cli/cli.py config set-username -u "my-node" -c /etc/yadacoin/config.json --confirm-backupWhat It Does:
- Displays a data-loss warning
- Prompts for backup confirmation (unless
--confirm-backupis set) - Loads the existing
config.json - Updates the
usernamefield - Recalculates
username_signatureusing the node's private key - Atomically writes the updated config back to disk
Important: The username_signature is a deterministic signature of the username using your node's private key. This signature is used for peer authentication on the YadaCoin network. Any time the username changes, the signature must be recalculated.
- Main Entry Point (
cli.py): Parses arguments and routes to command handlers - Command Handlers: Functions like
_handle_set_username()that orchestrate the update flow - Config Updaters: Classes that extend
ConfigUpdaterBaseto implement specific update logic
Provides common functionality for all config file updaters:
- load_config(path): Parse JSON config file
- confirm_backup(path, confirmed_flag): Prompt for backup confirmation with warnings
- write_config(path, config): Atomically write config using temp file + rename pattern
Subclasses implement a specific update method (e.g., update_username()).
Extends ConfigUpdaterBase to handle username updates with automatic signature recalculation.
- Mandatory Backup Confirmation: Before any modification, users must either:
- Provide
--confirm-backupflag, or - Type "yes" at the interactive prompt
- Provide
- Clear Warnings: Displays warning about potential data loss before requesting confirmation
- Atomic Writes: Uses temporary file + atomic rename to prevent corruption if write fails midway
- Validation: Checks that username is not empty and private key exists
- File Safety: Preserves original file permissions (mode) after write
- Exception Safety: Catches errors and returns non-zero exit codes
0: Success1: Command error or user declined backup confirmation2: No command provided (shows help)
To add a new config updater:
- Create a new file in
cli/commands/config/(e.g.,update_peer_host.py) - Subclass
ConfigUpdaterBaseand implement the update logic:
from cli.commands.config.config_base import ConfigUpdaterBase
class PeerHostConfigUpdater(ConfigUpdaterBase):
def update_peer_host(self, config, peer_host):
# Validate
if not peer_host:
raise ValueError("peer_host cannot be empty")
# Update
config["peer_host"] = peer_host- Create a handler in
cli/cli.py:
def _handle_set_peer_host(args):
updater = PeerHostConfigUpdater()
if not updater.confirm_backup(args.config, args.confirm_backup):
return 1
config = updater.load_config(args.config)
updater.update_peer_host(config, args.peer_host)
updater.write_config(args.config, config)
print("Updated peer_host in {}".format(args.config))
return 0- Register the command in
build_parser():
set_peer_host_parser = config_subparsers.add_parser(
"set-peer-host",
help="Update peer host in config.json",
)
set_peer_host_parser.add_argument("-p", "--peer-host", required=True)
set_peer_host_parser.set_defaults(handler=_handle_set_peer_host)The username_signature is computed using:
TU.generate_deterministic_signature(config, username, private_key=private_key)This creates a cryptographic signature of the username byte string using SECP256k1 curve with the node's private key. The signature is base64-encoded and stored in the config.
To prevent corruption, config writes follow this pattern:
- Write to temporary file (e.g.,
config.json.tmp) - Call
os.replace()to atomically move temp to target - Restore original file permissions
This ensures the original file is never partially written.
Error: ModuleNotFoundError: No module named 'coincurve'
Solution: Activate the virtual environment first:
source venv37/bin/activate
python3 cli/cli.py config set-username -u "mynode"Error: Python dependency module already loaded
Solution: Run the CLI in a fresh process; this typically occurs when running multiple tests in sequence in the same Python session.
Error: JSONDecodeError or similar
Solution: Ensure config/config.json is valid JSON. You can check with:
python3 -m json.tool config/config.jsonTests for the update_username feature are in tests/. Run with:
python3 -m pytest tests/ -vThe codebase follows PEP 8. Docstrings use Google style.