Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2052,7 +2052,7 @@ bool CConnman::AddConnection(const std::string& address, ConnectionType conn_typ

// Count existing connections
int existing_connections = WITH_READ_LOCK(m_nodes_mutex,
return std::count_if(m_nodes.begin(), m_nodes.end(), [conn_type](CNode* node) { return node->m_conn_type == conn_type; }););
return std::count_if(m_nodes.begin(), m_nodes.end(), [conn_type](CNode* node) { return node->m_conn_type == conn_type && !node->fDisconnect; }););

// Max connections of specified type already exist
if (max_connections != std::nullopt && existing_connections >= max_connections) return false;
Expand Down
39 changes: 26 additions & 13 deletions test/functional/test_framework/crypto/secp256k1.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* G: the secp256k1 generator point
"""

import functools

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Split the upstream backports into separate commits

This hunk belongs to bitcoin#36327, but this commit also contains bitcoin#36439's polling changes and bitcoin#36440's P2P/wallet changes. Combining three independent upstream PRs loses the 1:1 source history needed for later conflict/provenance checks and independent reverts, so split them into separate backport commits.

AGENTS.md reference: AGENTS.md:L181-L183

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not true, these changes belongs to 36327, validated; all changes from 36327 is backported 1:1, nothing missing and nothing extra added.


class FE:
"""Objects of this class represent elements of the field GF(2**256 - 2**32 - 977).
Expand Down Expand Up @@ -227,7 +228,7 @@ def mul(*aps):
def __rmul__(self, a):
"""Multiply an integer with a group element."""
if self == G:
return FAST_G.mul(a)
return mul_G(a % GE.ORDER)
return GE.mul((a, self))

def __neg__(self):
Expand Down Expand Up @@ -319,28 +320,40 @@ def __repr__(self):
class FastGEMul:
"""Table for fast multiplication with a constant group element.

Speed up scalar multiplication with a fixed point P by using a precomputed lookup table with
its powers of 2:
Speed up scalar multiplication with a fixed point P by using a precomputed lookup table.
The scalar is split into WINDOW-bit chunks, and for every chunk position i the table holds
a row [(0 * 2^(WINDOW*i)) * P, (1 * 2^(WINDOW*i)) * P, ..., ((2^WINDOW-1) * 2^(WINDOW*i)) * P]:

table = [P, 2*P, 4*P, (2^3)*P, (2^4)*P, ..., (2^255)*P]
table[i][j] = j * (2^(WINDOW*i)) * P

During multiplication, the points corresponding to each bit set in the scalar are added up,
i.e. on average ~128 point additions take place.
During multiplication, one table entry per chunk is added up, i.e. 256/WINDOW point
additions take place (instead of ~128 on average for a bit-by-bit approach).
"""

WINDOW = 4

def __init__(self, p):
self.table = [p] # table[i] = (2^i) * p
for _ in range(255):
p = p + p
self.table.append(p)
self.table = [] # table[i][j] = j * (2^(WINDOW*i)) * p
for _ in range((256 + self.WINDOW - 1) // self.WINDOW):
row = [GE()]
for _ in range((1 << self.WINDOW) - 1):
row.append(row[-1] + p)
self.table.append(row)
for _ in range(self.WINDOW):
p = p + p

def mul(self, a):
result = GE()
a = a % GE.ORDER
for bit in range(a.bit_length()):
if a & (1 << bit):
result += self.table[bit]
mask = (1 << self.WINDOW) - 1
for row in self.table:
result += row[a & mask]
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
a >>= self.WINDOW
return result

# Precomputed table with multiples of G for fast multiplication
FAST_G = FastGEMul(G)

@functools.lru_cache(maxsize=1024)
def mul_G(a):
return FAST_G.mul(a)
9 changes: 8 additions & 1 deletion test/functional/test_framework/p2p.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,8 @@ def connection_made(self, transport):
if self.p2p_connected_to_node and not self.supports_v2_p2p:
self.send_version()
self.on_open()
with p2p_lock:
p2p_lock.notify_all()

def connection_lost(self, exc):
"""asyncio callback when a connection is closed."""
Expand All @@ -305,6 +307,8 @@ def connection_lost(self, exc):
self._transport = None
self.recvbuf = b""
self.on_close()
with p2p_lock:
p2p_lock.notify_all()

# v2 handshake method
def _on_data_v2_handshake(self):
Expand Down Expand Up @@ -572,6 +576,7 @@ def on_message(self, message):
except Exception:
print("ERROR delivering %s (%s)" % (repr(message), sys.exc_info()[0]))
raise
p2p_lock.notify_all()

# Callback methods. Can be overridden by subclasses in individual test
# cases to provide custom message handling behaviour.
Expand Down Expand Up @@ -782,7 +787,9 @@ def test_function():
# P2PConnection acquires this lock whenever delivering a message to a P2PInterface.
# This lock should be acquired in the thread running the test logic to synchronize
# access to any data shared with the P2PInterface or P2PConnection.
p2p_lock = threading.Lock()
# It is a Condition so that waiters can be woken up as soon as a message is
# delivered or the connection state changes, instead of polling.
p2p_lock = threading.Condition(threading.Lock())


class NetworkThread(threading.Thread):
Expand Down
7 changes: 4 additions & 3 deletions test/functional/test_framework/test_framework.py
Original file line number Diff line number Diff line change
Expand Up @@ -826,7 +826,7 @@ def generatetodescriptor(self, generator, *args, sync_fun=None, **kwargs):
sync_fun() if sync_fun else self.sync_all()
return blocks

def sync_blocks(self, nodes=None, wait=1, timeout=60):
def sync_blocks(self, nodes=None, *, wait=0.05, timeout=60):
"""
Wait until everybody has the same tip.
sync_blocks needs to be called with an rpc_connections set that has least
Expand All @@ -848,7 +848,7 @@ def sync_blocks(self, nodes=None, wait=1, timeout=60):
"".join("\n {!r}".format(b) for b in best_hash),
))

def sync_mempools(self, nodes=None, wait=1, timeout=60, flush_scheduler=True, wait_func=None):
def sync_mempools(self, nodes=None, *, wait=0.05, timeout=60, flush_scheduler=True, wait_func=None):
"""
Wait until everybody has the same transactions in their memory
pools
Expand All @@ -857,7 +857,8 @@ def sync_mempools(self, nodes=None, wait=1, timeout=60, flush_scheduler=True, wa
timeout = int(timeout * self.options.timeout_factor)
stop_time = time.time() + timeout
if self.mocktime != 0 and wait_func is None:
wait_func = lambda: self.bump_mocktime(3, nodes=nodes)
# Polling is much faster than the mocktime bump was designed for; keep the bump at its original cadence
wait_func = throttled(lambda: self.bump_mocktime(3, nodes=nodes))
while time.time() <= stop_time:
pool = [set(r.getrawmempool()) for r in rpc_connections]
if pool.count(pool[0]) == len(rpc_connections):
Expand Down
7 changes: 4 additions & 3 deletions test/functional/test_framework/test_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,9 +288,10 @@ def start(self, extra_args=None, *, cwd=None, stdout=None, stderr=None, env=None

def wait_for_rpc_connection(self):
"""Sets up an RPC connection to the dashd process. Returns False if unable to connect."""
# Poll at a rate of four times per second
poll_per_s = 4
for _ in range(poll_per_s * self.rpc_timeout):
# Poll at a rate of twenty times per second
poll_per_s = 20
stop_time = time.monotonic() + self.rpc_timeout
while time.monotonic() <= stop_time:
if self.process.poll() is not None:
# Attach abrupt shutdown error/s to the exception message
self.stderr.seek(0)
Expand Down
6 changes: 6 additions & 0 deletions test/functional/test_framework/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import shutil
import re
import sys
import threading
import time
import urllib.parse

Expand Down Expand Up @@ -281,6 +282,11 @@ def wait_until_helper(predicate, *, attempts=float('inf'), timeout=float('inf'),
with lock:
if predicate():
return True
if isinstance(lock, threading.Condition):
# Wake up early when the lock owner signals a state change
lock.wait(sleep)
attempt += 1
continue
else:
if predicate():
return True
Expand Down
3 changes: 3 additions & 0 deletions test/functional/wallet_change_address.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ def set_test_params(self):
["-discardfee=1"],
["-avoidpartialspends", "-discardfee=1"]
]
# whitelist peers to speed up tx relay / mempool sync
for args in self.extra_args:
args.append("-whitelist=noban@127.0.0.1")
Comment on lines +29 to +31

@thepastaclaw thepastaclaw Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Resolved at f9141f57; see the replies below.

🟡 Suggestion: Missing prerequisite: upstream noban_tx_relay framework helper

The upstream relay-test changes rely on c985eb8, which adds the centralized noban_tx_relay framework attribute and injects the whitelist arguments in add_nodes(). Dash does not contain that helper; this backport instead hand-rolls -whitelist=noban@127.0.0.1 in each affected test. The direct Dash-specific flags are functionally appropriate, but the omitted prerequisite and intentional adaptation should be documented in the backport commit or PR, or implemented centrally so later backports do not have to rediscover this divergence.

source: backport-reviewer

@knst knst Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's true; but once noban_tx_relay is added, all whitelist could be replaced by script. Keep it as it is intentionally to achieve better performance at CI now and do bitcoin#27114: "p2p: Allow whitelisting manual connections" when it will be queued in the normal order.

The upstream bitcoin#36440 commit 10cfeb3 adds the relay-speedup setting to wallet_exported_watchonly.py, which was introduced upstream by a388076.

Marked as partial due missing changes for wallet_exported_watchonly.py

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved (re-reviewed at f9141f57): The direct Dash whitelist workaround is explicitly acknowledged in your PR reply as intentional until noban_tx_relay is added. That records the omitted-helper adaptation for future backports, so no source change is required.


def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
Expand Down
2 changes: 2 additions & 0 deletions test/functional/wallet_conflicts.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ def add_options(self, parser):

def set_test_params(self):
self.num_nodes = 3
# whitelist peers to speed up tx relay / mempool sync
self.extra_args = [["-whitelist=noban@127.0.0.1"]] * self.num_nodes

def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
Expand Down
2 changes: 2 additions & 0 deletions test/functional/wallet_importprunedfunds.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ def add_options(self, parser):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 2
# whitelist peers to speed up tx relay / mempool sync
self.extra_args = [["-whitelist=noban@127.0.0.1"]] * self.num_nodes

def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
Expand Down
2 changes: 2 additions & 0 deletions test/functional/wallet_orphanedreward.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ def add_options(self, parser):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 2
# whitelist peers to speed up tx relay / mempool sync
self.extra_args = [["-whitelist=noban@127.0.0.1"]] * self.num_nodes

def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
Expand Down
4 changes: 3 additions & 1 deletion test/functional/wallet_signer.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,13 @@ def mock_multi_signers_path(self):

def set_test_params(self):
self.num_nodes = 2

self.extra_args = [
[],
[f"-signer={self.mock_signer_path()}", '-keypool=10'],
]
# whitelist peers to speed up tx relay / mempool sync
for args in self.extra_args:
args.append("-whitelist=noban@127.0.0.1")

def skip_test_if_missing_module(self):
self.skip_if_no_external_signer()
Expand Down
Loading