Skip to content

Conversation

@codeflash-ai
Copy link

@codeflash-ai codeflash-ai bot commented Nov 5, 2025

📄 11% (0.11x) speedup for KeystoreWizard.on_hardware_device in electrum/wizard.py

⏱️ Runtime : 132 microseconds 118 microseconds (best of 175 runs)

📝 Explanation and details

The optimized code achieves an 11% speedup by eliminating function call overhead through inlining the current_cosigner method directly into on_hardware_device.

What was optimized:

  • Function call elimination: The original code called self.current_cosigner(wizard_data) which added significant overhead (537μs out of 1288μs total time). The optimized version inlines this logic directly, reducing overhead to just 70μs for the multisig check.
  • Safer dictionary access: Both versions now use .get() with default values instead of in checks followed by direct key access, reducing potential KeyError exceptions and multiple dictionary lookups.

Why this leads to speedup:

  • Python function calls have significant overhead - argument passing, stack frame creation, and method resolution. The line profiler shows the current_cosigner call consumed 41.7% of total execution time.
  • Inlining eliminates this overhead entirely while preserving the exact same logic flow.
  • The .get() method calls are slightly more expensive per operation but provide better safety and avoid the compound condition checking that was happening before.

Test case performance:
The optimization shows consistent 3-16% improvements across all test cases, with the largest gains in basic single hardware device scenarios (16.7% faster) and good improvements in multisig scenarios (3-6% faster). This suggests the optimization benefits both simple and complex wallet configurations.

Impact assessment:
Since this appears to be part of a wallet setup wizard, even an 11% improvement in hardware device initialization could provide noticeable user experience improvements, especially when users are setting up multiple cosigners in multisig wallets or switching between different hardware devices.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 178 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 100.0%
🌀 Generated Regression Tests and Runtime
import pytest
from electrum.wizard import KeystoreWizard


# Stub for Plugins and plugin instance
class DummyPlugin:
    # Simulates a plugin with wizard_entry_for_device method
    def __init__(self, return_value):
        self.return_value = return_value

    def wizard_entry_for_device(self, info, new_wallet=True):
        # Returns a string based on info and new_wallet for testing
        return f"entry:{info}:{new_wallet}"

# --- Unit tests for on_hardware_device ---

# ----------- BASIC TEST CASES -----------















#------------------------------------------------
import pytest
from electrum.wizard import KeystoreWizard

# --- Minimal stubs and mocks for dependencies ---

class DummyPlugin:
    """A dummy plugin to simulate hardware device plugins."""
    def __init__(self, entry_return=None):
        self.entry_return = entry_return

    def wizard_entry_for_device(self, info, new_wallet=True):
        # Simulate plugin behavior, return a string based on info and new_wallet
        return f"entry:{info}:{new_wallet}" if self.entry_return is None else self.entry_return

class DummyPlugins:
    """A dummy Plugins registry to simulate plugin lookup."""
    def __init__(self, plugins_dict):
        self.plugins_dict = plugins_dict

    def get_plugin(self, plugin_type):
        # Return the plugin for the given type, or raise if not found
        if plugin_type not in self.plugins_dict:
            raise KeyError(f"Plugin '{plugin_type}' not found")
        return self.plugins_dict[plugin_type]

# --- Unit tests for on_hardware_device ---

# 1. Basic Test Cases

def test_single_hardware_device_basic():
    """Test with a single hardware device, normal input."""
    plugins = DummyPlugins({'ledger': DummyPlugin()})
    wizard = KeystoreWizard(plugins)
    wizard_data = {
        'hardware_device': ('ledger', 'usb:1234')
    }
    codeflash_output = wizard.on_hardware_device(wizard_data); result = codeflash_output # 3.77μs -> 3.23μs (16.7% faster)

def test_single_hardware_device_false_new_wallet():
    """Test with new_wallet=False, should reflect in output."""
    plugins = DummyPlugins({'trezor': DummyPlugin()})
    wizard = KeystoreWizard(plugins)
    wizard_data = {
        'hardware_device': ('trezor', 'hid:abcd')
    }
    codeflash_output = wizard.on_hardware_device(wizard_data, new_wallet=False); result = codeflash_output # 2.71μs -> 2.60μs (4.22% faster)

def test_plugin_returns_custom_entry():
    """Test plugin returns a custom string."""
    plugins = DummyPlugins({'keepkey': DummyPlugin(entry_return='custom-entry')})
    wizard = KeystoreWizard(plugins)
    wizard_data = {
        'hardware_device': ('keepkey', 'hid:xyz')
    }
    codeflash_output = wizard.on_hardware_device(wizard_data); result = codeflash_output # 2.05μs -> 1.81μs (13.4% faster)

# 2. Edge Test Cases

def test_missing_hardware_device_key():
    """Test wizard_data missing 'hardware_device' key (should raise KeyError)."""
    plugins = DummyPlugins({'ledger': DummyPlugin()})
    wizard = KeystoreWizard(plugins)
    wizard_data = {}
    with pytest.raises(KeyError):
        wizard.on_hardware_device(wizard_data) # 1.13μs -> 1.00μs (13.3% faster)

def test_hardware_device_tuple_wrong_length():
    """Test hardware_device tuple with wrong length (should raise ValueError or IndexError)."""
    plugins = DummyPlugins({'ledger': DummyPlugin()})
    wizard = KeystoreWizard(plugins)
    wizard_data = {
        'hardware_device': ('ledger',)  # missing info
    }
    with pytest.raises(ValueError):
        # This will fail when unpacking _type, _info
        wizard.on_hardware_device(wizard_data) # 2.21μs -> 1.95μs (13.1% faster)

def test_plugin_not_found():
    """Test with unknown plugin type (should raise KeyError from DummyPlugins)."""
    plugins = DummyPlugins({'ledger': DummyPlugin()})
    wizard = KeystoreWizard(plugins)
    wizard_data = {
        'hardware_device': ('unknown', 'usb:9999')
    }
    with pytest.raises(KeyError):
        wizard.on_hardware_device(wizard_data) # 1.69μs -> 1.56μs (8.35% faster)

def test_multisig_cosigner_data():
    """Test multisig cosigner data path."""
    plugins = DummyPlugins({'trezor': DummyPlugin()})
    wizard = KeystoreWizard(plugins)
    wizard_data = {
        'wallet_type': 'multisig',
        'multisig_current_cosigner': 2,
        'multisig_cosigner_data': {
            '2': {'hardware_device': ('trezor', 'hid:abcd')}
        }
    }
    codeflash_output = wizard.on_hardware_device(wizard_data); result = codeflash_output # 3.36μs -> 3.26μs (3.19% faster)

def test_multisig_missing_cosigner():
    """Test multisig with missing cosigner data (should raise KeyError)."""
    plugins = DummyPlugins({'trezor': DummyPlugin()})
    wizard = KeystoreWizard(plugins)
    wizard_data = {
        'wallet_type': 'multisig',
        'multisig_current_cosigner': 1,
        'multisig_cosigner_data': {}
    }
    with pytest.raises(KeyError):
        wizard.on_hardware_device(wizard_data) # 1.47μs -> 1.43μs (2.66% faster)

def test_hardware_device_type_is_none():
    """Test hardware_device type is None (should raise KeyError from plugin lookup)."""
    plugins = DummyPlugins({'ledger': DummyPlugin()})
    wizard = KeystoreWizard(plugins)
    wizard_data = {
        'hardware_device': (None, 'usb:1234')
    }
    with pytest.raises(KeyError):
        wizard.on_hardware_device(wizard_data) # 1.97μs -> 1.77μs (11.4% faster)

def test_hardware_device_info_is_none():
    """Test hardware_device info is None (should still work and be passed to plugin)."""
    plugins = DummyPlugins({'ledger': DummyPlugin()})
    wizard = KeystoreWizard(plugins)
    wizard_data = {
        'hardware_device': ('ledger', None)
    }
    codeflash_output = wizard.on_hardware_device(wizard_data); result = codeflash_output # 3.06μs -> 2.80μs (9.44% faster)

def test_plugin_raises_exception():
    """Test plugin wizard_entry_for_device raises exception (should propagate)."""
    class ErrorPlugin(DummyPlugin):
        def wizard_entry_for_device(self, info, new_wallet=True):
            raise RuntimeError("Plugin error!")
    plugins = DummyPlugins({'ledger': ErrorPlugin()})
    wizard = KeystoreWizard(plugins)
    wizard_data = {
        'hardware_device': ('ledger', 'usb:fail')
    }
    with pytest.raises(RuntimeError):
        wizard.on_hardware_device(wizard_data) # 2.52μs -> 2.47μs (2.15% faster)

# 3. Large Scale Test Cases

def test_many_plugins_and_devices():
    """Test with many plugins and devices to check scalability."""
    plugins_dict = {f"plugin{i}": DummyPlugin(entry_return=f"entry{i}") for i in range(100)}
    plugins = DummyPlugins(plugins_dict)
    wizard = KeystoreWizard(plugins)
    for i in range(100):
        wizard_data = {'hardware_device': (f"plugin{i}", f"info{i}")}
        codeflash_output = wizard.on_hardware_device(wizard_data); result = codeflash_output # 62.6μs -> 54.0μs (16.0% faster)

def test_many_multisig_cosigners():
    """Test with many multisig cosigners in wizard_data."""
    plugins_dict = {f"p{i}": DummyPlugin(entry_return=f"multisig-entry{i}") for i in range(50)}
    plugins = DummyPlugins(plugins_dict)
    wizard = KeystoreWizard(plugins)
    cosigner_data = {str(i): {'hardware_device': (f"p{i}", f"cosigner{i}")} for i in range(50)}
    for i in range(50):
        wizard_data = {
            'wallet_type': 'multisig',
            'multisig_current_cosigner': i,
            'multisig_cosigner_data': cosigner_data
        }
        codeflash_output = wizard.on_hardware_device(wizard_data); result = codeflash_output # 39.1μs -> 37.0μs (5.67% faster)

def test_large_plugin_registry_lookup_performance():
    """Test that plugin lookup is correct and fast for large registry."""
    plugins_dict = {f"plugin_{i}": DummyPlugin(entry_return=f"entry_{i}") for i in range(999)}
    plugins = DummyPlugins(plugins_dict)
    wizard = KeystoreWizard(plugins)
    wizard_data = {'hardware_device': ('plugin_998', 'biginfo')}
    codeflash_output = wizard.on_hardware_device(wizard_data); result = codeflash_output # 2.33μs -> 2.12μs (9.75% faster)

def test_large_cosigner_data_missing_entry():
    """Test large cosigner data with missing entry (should raise KeyError)."""
    plugins_dict = {f"p{i}": DummyPlugin() for i in range(100)}
    plugins = DummyPlugins(plugins_dict)
    wizard = KeystoreWizard(plugins)
    cosigner_data = {str(i): {'hardware_device': (f"p{i}", f"cosigner{i}")} for i in range(99)}
    wizard_data = {
        'wallet_type': 'multisig',
        'multisig_current_cosigner': 99,
        'multisig_cosigner_data': cosigner_data
    }
    with pytest.raises(KeyError):
        wizard.on_hardware_device(wizard_data) # 1.62μs -> 1.46μs (10.4% faster)
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.

To edit these changes git checkout codeflash/optimize-KeystoreWizard.on_hardware_device-mhlanls5 and push.

Codeflash Static Badge

The optimized code achieves an 11% speedup by eliminating function call overhead through **inlining** the `current_cosigner` method directly into `on_hardware_device`.

**What was optimized:**
- **Function call elimination**: The original code called `self.current_cosigner(wizard_data)` which added significant overhead (537μs out of 1288μs total time). The optimized version inlines this logic directly, reducing overhead to just 70μs for the multisig check.
- **Safer dictionary access**: Both versions now use `.get()` with default values instead of `in` checks followed by direct key access, reducing potential KeyError exceptions and multiple dictionary lookups.

**Why this leads to speedup:**
- Python function calls have significant overhead - argument passing, stack frame creation, and method resolution. The line profiler shows the `current_cosigner` call consumed 41.7% of total execution time.
- Inlining eliminates this overhead entirely while preserving the exact same logic flow.
- The `.get()` method calls are slightly more expensive per operation but provide better safety and avoid the compound condition checking that was happening before.

**Test case performance:**
The optimization shows consistent 3-16% improvements across all test cases, with the largest gains in basic single hardware device scenarios (16.7% faster) and good improvements in multisig scenarios (3-6% faster). This suggests the optimization benefits both simple and complex wallet configurations.

**Impact assessment:**
Since this appears to be part of a wallet setup wizard, even an 11% improvement in hardware device initialization could provide noticeable user experience improvements, especially when users are setting up multiple cosigners in multisig wallets or switching between different hardware devices.
@codeflash-ai codeflash-ai bot requested a review from mashraf-222 November 5, 2025 01:03
@codeflash-ai codeflash-ai bot added ⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: Medium Optimization Quality according to Codeflash labels Nov 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: Medium Optimization Quality according to Codeflash

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant