Skip to content

fix(dash-spv): preserve already defined change addr in the tx builder - #920

Open
ZocoLini wants to merge 1 commit into
devfrom
fix/tx-builder-address
Open

fix(dash-spv): preserve already defined change addr in the tx builder#920
ZocoLini wants to merge 1 commit into
devfrom
fix/tx-builder-address

Conversation

@ZocoLini

@ZocoLini ZocoLini commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • Bug Fixes
    • Preserved explicitly configured change addresses during transaction funding.
    • Prevented unnecessary consumption of pooled change addresses when a change address is already set.
    • Added documentation and regression coverage for change-address behavior.

@ZocoLini
ZocoLini requested a review from xdustinface August 3, 2026 13:16
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Funding change address

Layer / File(s) Summary
Change address selection and regression coverage
key-wallet/src/wallet/managed_wallet_info/transaction_builder.rs
set_funding preserves an explicit change_addr and derives one only when it is unset. Documentation describes the precedence. A regression test verifies that the change-address pool is not consumed.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: xdustinface, quantumexplorer

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely describes preserving an explicitly configured change address in the transaction builder.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/tx-builder-address

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@key-wallet/src/wallet/managed_wallet_info/transaction_builder.rs`:
- Around line 127-129: Update TransactionBuilder::set_funding so it does not
call next_change_address or advance the change pool when initializing
change_addr; defer derivation until the build path has the final address, or
otherwise ensure set_change_address explicitly prevents and replaces any derived
address. Preserve the existing behavior for callers that do not provide a change
address, and add regression coverage for
set_funding(...).set_change_address(...).build_unsigned().
- Around line 127-129: Validate explicit change addresses in set_funding by
comparing the address network with funds_acc.network()/acc.network before
preserving change_addr, and return a new BuilderError variant on mismatch. Keep
matching explicit addresses unchanged, update
set_funding_keeps_an_explicit_change_address to use a matching-network
fixture/address, and add a regression test covering rejection of a cross-network
address.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 16ccc41c-dfd2-40f9-a50f-b927f63181a9

📥 Commits

Reviewing files that changed from the base of the PR and between 9cbe4e7 and 9c89c68.

📒 Files selected for processing (1)
  • key-wallet/src/wallet/managed_wallet_info/transaction_builder.rs

Comment on lines +127 to +129
if self.change_addr.is_none() {
self.change_addr = funds_acc.next_change_address(Some(&acc.account_xpub), true).ok();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

# Inspect builder chains and address-pool mutations.
rg -n -C 8 'set_funding|set_change_address|next_change_address' \
  key-wallet/src --glob '*.rs'

Repository: dashpay/rust-dashcore

Length of output: 50378


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

# Map the transaction_builder source and inspect relevant builder implementation plus tests.
wc -l key-wallet/src/wallet/managed_wallet_info/transaction_builder.rs
sed -n '1,170p' key-wallet/src/wallet/managed_wallet_info/transaction_builder.rs
sed -n '1210,1305p' key-wallet/src/wallet/managed_wallet_info/transaction_builder.rs

# Find all TransactionBuilder chain calls in key-wallet source without tests/context noise.
printf '\n--- builder chains in key-wallet src ---\n'
python3 - <<'PY'
from pathlib import Path
import re
root = Path('key-wallet/src')
pairs = set()
for path in root.rglob('*.rs'):
    text = path.read_text(errors='replace')
    for m in re.finditer(r'TransactionBuilder::new\(\)', text):
        start = m.start()
        line_start = text.rfind('\n', 0, start) + 1
        line_end = text.find('\n', start + 2000)
        text = text if line_end < 0 else text[:line_end] + text[line_end+1:]
        chain = text[start:start+2000]
        h1 = 'set_funding' in chain
        h2 = 'set_change_address' in chain
        if h1 or h2:
            # capture minimal surrounding block up to next semicolon/new call-ish boundary
            out = []
            for ch in chain:
                out.append(ch)
                if ch == '.':
                    break
            out.append('...')
            pairs.add((int(text[:start].count('\n')+1), path.as_posix(), out[:200]))
            # stop at first full builder chain block
            break
print('\n'.join(f"{ln:5} {path} {chain}" for ln, path, chain in sorted(pairs)))
PY

Repository: dashpay/rust-dashcore

Length of output: 11046


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- all TransactionBuilder builder chains in key-wallet/src ---'
python3 - <<'PY'
import re
from pathlib import Path
for path in Path('key-wallet/src').rglob('*.rs'):
    text = path.read_text(errors='replace')
    for m in re.finditer(r'TransactionBuilder::new\(\)', text):
        line = text[:m.start()].count('\n') + 1
        end = min(len(text), m.start() + 2000)
        chunk = text[m.start():end]
        if 'set_funding' in chunk or 'set_change_address' in chunk:
            print(f'{path}:{line}:')
            for i, block in enumerate(chunk.split('\n'), 1):
                print(f'  {block}')
            break
PY

printf '%s\n' '--- ManagedCoreFundsAccount next_change_address definitions/usages ---'
rg -n -C 5 'fn next_change_address|next_receive_address|advance.*change|change_addr|ChangeAddresses' key-wallet/src --glob '*.rs' | head -n 220

printf '%s\n' '--- focused builder chain occurrences ---'
rg -n -C 3 'set_funding|set_change_address' key-wallet/src/wallet/managed_wallet_info/transaction_builder.rs

Repository: dashpay/rust-dashcore

Length of output: 35390


Preserve next_change_address calls from advancing the change pool.

set_funding calls funds_acc.next_change_address(..., true), but later .set_change_address(...) can replace change_addr. Existing chains use this order, including production calls in TransactionBuilder and build_and_sign_transaction_with_signer, so one change address can be consumed and discarded. Defer change derivation until the build path has the final address, or make the setter order explicit and add a regression covering .set_funding(...).set_change_address(...).build_unsigned().

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@key-wallet/src/wallet/managed_wallet_info/transaction_builder.rs` around
lines 127 - 129, Update TransactionBuilder::set_funding so it does not call
next_change_address or advance the change pool when initializing change_addr;
defer derivation until the build path has the final address, or otherwise ensure
set_change_address explicitly prevents and replaces any derived address.
Preserve the existing behavior for callers that do not provide a change address,
and add regression coverage for
set_funding(...).set_change_address(...).build_unsigned().

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

# Locate the account network source and address-validation helpers.
rg -n -C 8 -P 'struct\s+ManagedCoreFundsAccount|network|is_valid_for_network|Network::' \
  key-wallet/src/managed_account key-wallet/src/wallet --glob '*.rs'

# Confirm the networks used by the regression fixture.
rg -n -C 6 -P 'dummy_bip44|Address::dummy\(Network::(Testnet|Regtest)' \
  key-wallet/src --glob '*.rs'

Repository: dashpay/rust-dashcore

Length of output: 50378


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

# Inspect the builder state initialization, network source methods, change address handling,
# and the specific regression test around lines 127 and 1218-1232.
sed -n '1,180p' key-wallet/src/wallet/managed_wallet_info/transaction_builder.rs
sed -n '118,200p' key-wallet/src/managed_account/managed_funds_account.rs
sed -n '55,95p' key-wallet/src/managed_account/managed_account_ref.rs
sed -n '1215,1235p' key-wallet/src/wallet/managed_wallet_info/transaction_builder.rs

# Find address validation helpers and error text related to network/address validation.
rg -n -C 4 'fn .*network|is_.+_network|NetworkMismatch|invalid.*network|invalid_address|validate.*address' key-wallet/src --glob '*.rs'

Repository: dashpay/rust-dashcore

Length of output: 7193


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

# Locate the managed funds account source and test for address network validation helpers.
fd -a '.*managed.*funds.*\.rs|.*account.*ref.*\.rs|.*funding.*account.*\.rs' key-wallet/src || true
rg -n -C 8 'ManagedCoreFundsAccount|dummy_bip44|fn network\(|is_.*network|validate_|Address::dummy\(Network::Regtest|set_change_address' key-wallet/src --glob '*.rs' | sed -n '1,220p'

# Inspect the exact regression test.
sed -n '1215,1235p' key-wallet/src/wallet/managed_wallet_info/transaction_builder.rs

# Locate where transaction builder obtains the funds account/network during build.
rg -n -C 6 'fn build_signed|fn build_unsigned|ManagedCoreFundsAccount|funding_account|set_funding|next_change_address|change_addr' key-wallet/src/wallet/managed_wallet_info/transaction_builder.rs

Repository: dashpay/rust-dashcore

Length of output: 41463


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

# Inspect the managed funds account implementation and network helpers.
sed -n '1,220p' key-wallet/src/managed_account/managed_core_funds_account.rs
sed -n '1,180p' key-wallet/src/managed_account/managed_account_ref.rs

# Search narrowly for network validation helpers in managed account types.
rg -n -C 4 'fn network|network\s*:\s*Network|is_.*network|is_valid_for_network|NetworkMismatch|invalid.*network|Address::dummy\(Network::Regtest' key-wallet/src/managed_account --glob '*.rs'

# Search narrowly for error kinds that the transaction builder should use.
rg -n -C 3 'enum BuilderError|BuilderError::|Change|change_addr|Network' key-wallet/src/wallet/managed_wallet_info/transaction_builder.rs ke

Repository: dashpay/rust-dashcore

Length of output: 50377


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

# Locate the transaction builder error enum and builder construction/entrypoints to see where funding/account network can be compared.
rg -n -C 5 'enum BuilderError|NetworkMismatch|Invalid.*Network|change_addr|set_funding\(.*Account|set_funding\(&mut funds_acc' key-wallet/src/wallet/managed_wallet_info/transaction_builder.rs

# Locate ManagedCoreFundsAccount constructor methods to confirm the dummy fixture network.
rg -n -C 6 'impl ManagedCoreFundsAccount|pub fn dummy_bip44|managed_account_type\(\)\.into\(\)|Network::Regtest|Network::Testnet' key-wallet/src/managed_account/managed_core_funds_account.rs

# Locate Account network source.
rg -n -C 4 'struct Account|impl AccountTrait for Account|fn network\(&self\)|network:' key-wallet/src/account --glob '*.rs'

Repository: dashpay/rust-dashcore

Length of output: 34445


Reject a cross-network explicit change address in set_change_address.

set_funding only preserves an already-set change_addr, while assemble_unsigned later emits change_addr.script_pubkey() without validating network membership. In the explicit-address path, builder.network() is unavailable, but set_funding can compare the provided change address against funds_acc.network()/acc.network and add a BuilderError variant for mismatch.

Also update set_funding_keeps_an_explicit_change_address to use the fixture network or construct a matching address explicitly, and add a regression test case that rejects a mismatched explicit change address.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@key-wallet/src/wallet/managed_wallet_info/transaction_builder.rs` around
lines 127 - 129, Validate explicit change addresses in set_funding by comparing
the address network with funds_acc.network()/acc.network before preserving
change_addr, and return a new BuilderError variant on mismatch. Keep matching
explicit addresses unchanged, update
set_funding_keeps_an_explicit_change_address to use a matching-network
fixture/address, and add a regression test covering rejection of a cross-network
address.

Source: Coding guidelines

@codecov

codecov Bot commented Aug 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 74.74%. Comparing base (9cbe4e7) to head (9c89c68).

Additional details and impacted files
@@            Coverage Diff             @@
##              dev     #920      +/-   ##
==========================================
- Coverage   74.76%   74.74%   -0.03%     
==========================================
  Files         328      328              
  Lines       76593    76613      +20     
==========================================
- Hits        57267    57266       -1     
- Misses      19326    19347      +21     
Flag Coverage Δ
core 77.29% <ø> (ø)
ffi 49.32% <ø> (-0.41%) ⬇️
rpc 20.00% <ø> (ø)
spv 91.06% <ø> (+0.11%) ⬆️
wallet 75.71% <100.00%> (+0.02%) ⬆️
Files with missing lines Coverage Δ
.../wallet/managed_wallet_info/transaction_builder.rs 87.39% <100.00%> (+0.34%) ⬆️

... and 21 files with indirect coverage changes

@ZocoLini ZocoLini changed the title fix(dash-spv): preserve already defined change addrin the tx builder fix(dash-spv): preserve already defined change addr in the tx builder Aug 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant