• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

spesmilo / electrum / 6531208869314560

07 Mar 2025 09:41AM UTC coverage: 60.796% (+0.4%) from 60.38%
6531208869314560

Pull #9298

CirrusCI

ecdsa
batch payment manager:

The class TxBatcher handles the creation, broadcast and replacement
of replaceable transactions. Callers (LNWatcher, SwapManager) use
methods add_payment_output and add_sweep_info. Transactions
created by TxBatcher may combine sweeps and outgoing payments.

Transactions created by TxBatcher will have their fee bumped
automatically (this was only the case for sweeps before).

TxBatcher manages several TxBatches. TxBatches are created
dynamically when needed.

The GUI does not touch txbatcher transactions:
  - wallet.get_candidates_for_batching excludes txbatcher
    transactions
  - RBF dialogs do not work with txbatcher transactions

wallet:
  - instead of reading config variables, make_unsigned_transaction
    takes new parameters: base_tx, send_change_to_lighting

tests:
  - unit tests in test_txbatcher.py (replaces test_sswaps.py)
  - force all regtests to use MPP, so that we sweep transactions
    with several HTLCs. This forces the payment manager to aggregate
    first-stage HTLC tx inputs. second-stage are not batched for now.
Pull Request #9298: wallet: RBF batch payments manager

231 of 371 new or added lines in 7 files covered. (62.26%)

239 existing lines in 8 files now uncovered.

21092 of 34693 relevant lines covered (60.8%)

3.04 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/electrum/utils/stacktracer.py
1
#!/usr/bin/env python
2
#
3
# Copyright (C) 2010 Laszlo Nagy (nagylzs)
4
#
5
# Permission is hereby granted, free of charge, to any person
6
# obtaining a copy of this software and associated documentation files
7
# (the "Software"), to deal in the Software without restriction,
8
# including without limitation the rights to use, copy, modify, merge,
9
# publish, distribute, sublicense, and/or sell copies of the Software,
10
# and to permit persons to whom the Software is furnished to do so,
11
# subject to the following conditions:
12
#
13
# The above copyright notice and this permission notice shall be
14
# included in all copies or substantial portions of the Software.
15
#
16
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
# SOFTWARE.
24
#
25
# Taken from: https://code.activestate.com/recipes/577334-how-to-debug-deadlocked-multi-threaded-programs/
26

27

UNCOV
28
"""Stack tracer for multi-threaded applications.
29
Useful for debugging deadlocks and hangs.
30

31
Usage:
32
    import stacktracer
33
    stacktracer.trace_start("trace.html", interval=5)
34
    ...
35
    stacktracer.trace_stop()
36

37
This will create a file named "trace.html" showing the stack traces of all threads,
38
updated every 5 seconds.
39
"""
40

41
import os
×
42
import sys
×
43
import threading
×
44
import time
×
45
import traceback
×
46
from typing import Optional
×
47

48
# 3rd-party dependency:
49
from pygments import highlight
×
50
from pygments.lexers import PythonLexer
×
51
from pygments.formatters import HtmlFormatter
×
52

53

54
def _thread_from_id(ident) -> Optional[threading.Thread]:
×
55
    return threading._active.get(ident)
×
56

57

58
def stacktraces():
×
59
    """Taken from http://bzimmer.ziclix.com/2008/12/17/python-thread-dumps/"""
60
    code = []
×
61
    for thread_id, stack in sys._current_frames().items():
×
62
        thread = _thread_from_id(thread_id)
×
63
        code.append(f"\n# thread_id={thread_id}. thread={thread}")
×
64
        for filename, lineno, name, line in traceback.extract_stack(stack):
×
65
            code.append(f'File: "{filename}", line {lineno}, in {name}')
×
66
            if line:
×
67
                code.append("  %s" % (line.strip()))
×
68

69
    return highlight("\n".join(code), PythonLexer(), HtmlFormatter(
×
70
        full=False,
71
        # style="native",
72
        noclasses=True,
73
    ))
74

75

76
class TraceDumper(threading.Thread):
×
77
    """Dump stack traces into a given file periodically.
78

79
    # written by nagylzs
80
    """
81

82
    def __init__(self, fpath, interval, auto):
×
83
        """
84
        @param fpath: File path to output HTML (stack trace file)
85
        @param auto: Set flag (True) to update trace continuously.
86
            Clear flag (False) to update only if file not exists.
87
            (Then delete the file to force update.)
88
        @param interval: In seconds: how often to update the trace file.
89
        """
90
        assert (interval > 0.1)
×
91
        self.auto = auto
×
92
        self.interval = interval
×
93
        self.fpath = os.path.abspath(fpath)
×
94
        self.stop_requested = threading.Event()
×
95
        threading.Thread.__init__(self)
×
96

97
    def run(self):
×
98
        while not self.stop_requested.is_set():
×
99
            time.sleep(self.interval)
×
100
            if self.auto or not os.path.isfile(self.fpath):
×
101
                self.dump_stacktraces()
×
102

103
    def stop(self):
×
104
        self.stop_requested.set()
×
105
        self.join()
×
106
        try:
×
107
            if os.path.isfile(self.fpath):
×
108
                os.unlink(self.fpath)
×
109
        except OSError:
×
110
            pass
×
111

112
    def dump_stacktraces(self):
×
113
        with open(self.fpath, "w+") as fout:
×
114
            fout.write(stacktraces())
×
115

116

117
_tracer = None  # type: Optional[TraceDumper]
×
118

119

120
def trace_start(fpath, interval=5, *, auto=True):
×
121
    """Start tracing into the given file."""
122
    global _tracer
123
    if _tracer is None:
×
124
        _tracer = TraceDumper(fpath, interval, auto)
×
125
        _tracer.daemon = True
×
126
        _tracer.start()
×
127
    else:
128
        raise Exception("Already tracing to %s" % _tracer.fpath)
×
129

130

131
def trace_stop():
×
132
    """Stop tracing."""
133
    global _tracer
134
    if _tracer is None:
×
135
        raise Exception("Not tracing, cannot stop.")
×
136
    else:
137
        _tracer.stop()
×
138
        _tracer = None
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc