Skip to content

Conversation

@knst
Copy link
Collaborator

@knst knst commented Dec 8, 2025

Issue being fixed or feature implemented

When funding a transaction with external inputs, instead of providing solving data, a user may want to just provide the maximum signed size of that input. This is particularly useful in cases where the input is nonstandard as our dummy signer is unable to handle those inputs.

The input size can be provided to any input regardless of whether it belongs to the wallet and the provided size will always be used regardless of any calculated input size. This allows the user to override the calculated input size which may overestimate in some circumstances due to missing information (e.g. if the private key is not known, a maximum size signature will be used, but the actual signer may be doing additional work which reduces the size of the signature).

For send and walletcreatefundedpsbt, the input size is specified in a size field in an input object. For fundrawtransaction, a new input_sizes field is added to the options object. This is an array of objects consisting of a txid, vout, and size.

What was done?

Bitcoin backport bitcoin#23201 and related fixes.
This backport is not trivial to do, not trivial to dashify, and not trivial to review, so, it has own PR.

Please note, that implementation of FillInputToWeight is different with Bitcoin Core. Due missing stack of "segregated data" we can't get exactly the same size, it maybe different up to 2 bytes.

 /*
// We will want to subtract the size of the Compact Size UInt that will also be serialized.
// However doing so when the size is near a boundary can result in a problem where it is not
// possible to have a stack element size and combination to exactly equal a target.
// So we allow to fullfill transaction 2 byte less in this situation.
*/

How Has This Been Tested?

Run unit & functional tests

Breaking Changes

N/A

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have made corresponding changes to the documentation
  • I have assigned this pull request to a milestone

@knst knst added this to the 23.1 milestone Dec 8, 2025
@knst knst changed the title backport: bitcoin/bitcoin#23201, bitcoin/bitcoin#25551 backport: bitcoin/bitcoin#23201, bitcoin/bitcoin#25551 (external signer, user provided size of tx) Dec 8, 2025
@github-actions
Copy link

github-actions bot commented Dec 8, 2025

✅ No Merge Conflicts Detected

This PR currently has no conflicts with other open PRs.

@coderabbitai
Copy link

coderabbitai bot commented Dec 8, 2025

Walkthrough

This change adds per-input weight support: CCoinControl gains SetInputWeight/HasInputWeight/GetInputWeight and an internal map for weights. RPCs (fundrawtransaction, send, walletcreatefundedpsbt) accept an inputs->input_sizes parameter; a helper extracts per-input sizes into options and applies them to coin control. Coin selection uses provided input weights when estimating input bytes. A FillInputToWeight helper and integration in DummySignTx pre-fills inputs to target serialized weight. New unit and functional tests exercise sizing, boundaries, and fee effects.

Sequence Diagram

sequenceDiagram
    participant User
    participant RPC as RPC Layer (spend.cpp)
    participant CC as CCoinControl
    participant CS as Coin Selection (spend.cpp)
    participant Sign as Signing (wallet.cpp)

    User->>RPC: Call fund/send/walletcreatefundedpsbt with input_sizes
    RPC->>RPC: SetOptionsInputWeights(inputs, options)
    RPC->>CC: SetInputWeight(outpoint, weight) for each input
    RPC->>CS: FundTransaction(..., coin_control)
    CS->>CC: HasInputWeight(outpoint)?
    alt weight present
        CC-->>CS: true
        CS->>CC: GetInputWeight(outpoint)
        CS->>CS: Use provided weight for input_bytes
    else no weight
        CC-->>CS: false
        CS->>CS: Use estimated input size
    end
    CS-->>RPC: Built transaction/psbt
    RPC->>Sign: DummySignTx(tx, coin_control)
    Sign->>CC: HasInputWeight(outpoint)?
    alt weight present
        CC-->>Sign: true
        Sign->>Sign: FillInputToWeight(txin, target_weight)
        Sign->>Sign: Skip normal signing for that input
    else no weight
        CC-->>Sign: false
        Sign->>Sign: Normal signing
    end
    Sign-->>RPC: Signed/funded tx or PSBT
    RPC-->>User: Result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Review CCoinControl additions (m_input_weights): correctness of key type, constness, and any concurrency considerations.
  • Validate SetOptionsInputWeights parsing/validation: vout presence and bounds, minimum (41) and maximum size checks, and error messages.
  • Inspect coin selection changes in SelectCoins path to ensure solvability logic remains correct when input_bytes are overridden.
  • Audit FillInputToWeight behavior and its use in DummySignTx for off-by-one/compact-size boundary conditions.
  • Thoroughly review expanded tests (unit and functional) for correctness, brittle assumptions, and mocked weight derivation logic.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.83% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main change: backporting Bitcoin features bitcoin#23201 and bitcoin#25551 for user-provided transaction input sizing.
Description check ✅ Passed The description clearly explains the feature being backported, the motivation (external/nonstandard inputs), and the implementation details across different RPC methods.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
test/functional/wallet_send.py (1)

542-542: Use string literal for Decimal to avoid float precision issues.

"Unfortunately, most decimal fractions cannot be represented exactly as binary fractions." Using Decimal(0.0001) with a float literal may introduce precision errors.

-        assert_fee_amount(testres["fees"]["base"], testres["vsize"], Decimal(0.0001))
+        assert_fee_amount(testres["fees"]["base"], testres["vsize"], Decimal("0.0001"))
test/functional/rpc_fundrawtransaction.py (2)

10-10: Remove unused import.

Static analysis correctly identifies that ceil from math is imported but never used in this file.

-from math import ceil

1028-1028: Use string literal for Decimal to avoid float precision issues.

Same as in wallet_send.py, using Decimal(0.0001) with a float literal may introduce precision errors.

-        assert_fee_amount(funded_tx4["fee"], tx4_size, Decimal(0.0001))
+        assert_fee_amount(funded_tx4["fee"], tx4_size, Decimal("0.0001"))
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e3d61f2 and 151c1a5.

📒 Files selected for processing (9)
  • src/wallet/coincontrol.h (2 hunks)
  • src/wallet/rpc/spend.cpp (9 hunks)
  • src/wallet/spend.cpp (1 hunks)
  • src/wallet/test/spend_tests.cpp (1 hunks)
  • src/wallet/wallet.cpp (2 hunks)
  • src/wallet/wallet.h (1 hunks)
  • test/functional/rpc_fundrawtransaction.py (3 hunks)
  • test/functional/rpc_psbt.py (3 hunks)
  • test/functional/wallet_send.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
src/**/*.{cpp,h,hpp,cc}

📄 CodeRabbit inference engine (CLAUDE.md)

Dash Core implementation must be written in C++20, requiring at least Clang 16 or GCC 11.1

Files:

  • src/wallet/wallet.h
  • src/wallet/test/spend_tests.cpp
  • src/wallet/spend.cpp
  • src/wallet/coincontrol.h
  • src/wallet/rpc/spend.cpp
  • src/wallet/wallet.cpp
src/wallet/**/*.{cpp,h}

📄 CodeRabbit inference engine (CLAUDE.md)

Wallet implementation must use Berkeley DB and SQLite

Files:

  • src/wallet/wallet.h
  • src/wallet/test/spend_tests.cpp
  • src/wallet/spend.cpp
  • src/wallet/coincontrol.h
  • src/wallet/rpc/spend.cpp
  • src/wallet/wallet.cpp
src/{test,wallet/test}/**/*.{cpp,h}

📄 CodeRabbit inference engine (CLAUDE.md)

Unit tests in src/test/ and src/wallet/test/ must use Boost::Test framework

Files:

  • src/wallet/test/spend_tests.cpp
test/functional/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Functional tests in test/functional/ must be written in Python (minimum version specified in .python-version) and depend on dashd and dash-node

Files:

  • test/functional/wallet_send.py
  • test/functional/rpc_fundrawtransaction.py
  • test/functional/rpc_psbt.py
🧠 Learnings (7)
📓 Common learnings
Learnt from: kwvg
Repo: dashpay/dash PR: 6543
File: src/wallet/receive.cpp:240-251
Timestamp: 2025-02-06T14:34:30.466Z
Learning: Pull request #6543 is focused on move-only changes and refactoring, specifically backporting from Bitcoin. Behavior changes should be proposed in separate PRs.
Learnt from: knst
Repo: dashpay/dash PR: 6916
File: src/univalue/include/univalue.h:81-88
Timestamp: 2025-10-25T07:08:51.918Z
Learning: For backport PRs from bitcoin/bitcoin, bitcoin-core/gui, etc., backported changes should match the original upstream PRs even if they appear strange, modify vendored code, or seem to violate coding guidelines. Still flag genuine issues like bugs, undefined behavior, crashes, compilation errors, or linter failures.
Learnt from: knst
Repo: dashpay/dash PR: 6883
File: src/rpc/rawtransaction.cpp:1088-1125
Timestamp: 2025-10-13T12:37:12.357Z
Learning: In backport pull requests (especially from Bitcoin Core), treat "moved" or refactored code as out-of-scope for content-level review. Focus validation on verifying that code is moved correctly: no fields added, no fields removed, no fields reordered, and no unexpected changes beyond whitespace adjustments. Pre-existing issues in the upstream code should be preserved to maintain fidelity to the original implementation.
Learnt from: knst
Repo: dashpay/dash PR: 6871
File: contrib/guix/libexec/build.sh:358-360
Timestamp: 2025-10-05T20:38:28.457Z
Learning: In the Dash repository, when backporting code from Bitcoin Core, typos and minor issues in comments should be kept as-is to reduce merge conflicts in future backports, even if they remain unfixed in Bitcoin Core's master branch.
Learnt from: PastaPastaPasta
Repo: dashpay/dash PR: 6804
File: src/qt/proposalwizard.cpp:40-42
Timestamp: 2025-08-11T17:16:36.654Z
Learning: In the Dash repository, when a PR adds new files that are not from Bitcoin backports, these files must be added to the list in test/util/data/non-backported.txt. This applies to newly created files like qt/proposalwizard.{h,cpp} and forms/proposalwizard.ui. Limited exemptions may exist for subtrees and similar cases.
📚 Learning: 2025-08-08T07:01:47.332Z
Learnt from: knst
Repo: dashpay/dash PR: 6805
File: src/wallet/rpc/wallet.cpp:357-357
Timestamp: 2025-08-08T07:01:47.332Z
Learning: In src/wallet/rpc/wallet.cpp, the upgradetohd RPC now returns a UniValue string message (RPCResult::Type::STR) instead of a boolean, including guidance about mnemonic backup and null-character passphrase handling; functional tests have been updated to assert returned strings in several cases.

Applied to files:

  • src/wallet/test/spend_tests.cpp
  • src/wallet/rpc/spend.cpp
  • test/functional/rpc_fundrawtransaction.py
  • test/functional/rpc_psbt.py
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{validation,txmempool}/**/*.{cpp,h} : Block validation and mempool handling must use extensions to Bitcoin Core mechanisms for special transaction validation and enhanced transaction relay

Applied to files:

  • src/wallet/test/spend_tests.cpp
  • src/wallet/rpc/spend.cpp
  • src/wallet/wallet.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/coinjoin/**/*.{cpp,h} : CoinJoin implementation must use masternode-coordinated mixing sessions with uniform denomination outputs

Applied to files:

  • src/wallet/spend.cpp
  • src/wallet/coincontrol.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{validation,consensus,net_processing}/**/*.{cpp,h} : ValidationInterface callbacks must be used for event-driven architecture to coordinate subsystems during block/transaction processing

Applied to files:

  • src/wallet/rpc/spend.cpp
📚 Learning: 2025-01-14T08:37:16.955Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6530
File: src/validation.cpp:360-362
Timestamp: 2025-01-14T08:37:16.955Z
Learning: The UpdateTransactionsFromBlock() method in txmempool.cpp takes parameters in the order: vHashUpdate, ancestor_size_limit, ancestor_count_limit. The size limit comes before the count limit.

Applied to files:

  • src/wallet/rpc/spend.cpp
📚 Learning: 2025-06-09T16:43:20.996Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6718
File: test/functional/test_framework/test_framework.py:2102-2102
Timestamp: 2025-06-09T16:43:20.996Z
Learning: In the test framework consolidation PR (#6718), user kwvg prefers to limit functional changes to those directly related to MasternodeInfo, avoiding scope creep even for minor improvements like error handling consistency.

Applied to files:

  • test/functional/rpc_fundrawtransaction.py
  • test/functional/rpc_psbt.py
🧬 Code graph analysis (9)
src/wallet/wallet.h (1)
src/wallet/wallet.cpp (2)
  • FillInputToWeight (1661-1694)
  • FillInputToWeight (1661-1661)
src/wallet/test/spend_tests.cpp (1)
src/wallet/wallet.cpp (2)
  • FillInputToWeight (1661-1694)
  • FillInputToWeight (1661-1661)
test/functional/wallet_send.py (2)
test/functional/test_framework/util.py (3)
  • assert_raises_rpc_error (132-148)
  • assert_equal (69-74)
  • assert_fee_amount (44-52)
src/wallet/rpc/spend.cpp (3)
  • send (797-994)
  • send (797-797)
  • tx (981-981)
src/wallet/spend.cpp (1)
src/policy/policy.h (1)
  • GetVirtualTransactionSize (118-121)
src/wallet/coincontrol.h (4)
src/wallet/spend.cpp (1)
  • outpoint (150-150)
src/wallet/wallet.cpp (1)
  • outpoint (1093-1093)
src/wallet/interfaces.cpp (4)
  • outpoint (399-399)
  • outpoint (399-399)
  • outpoint (400-400)
  • outpoint (400-400)
src/wallet/coinjoin.cpp (2)
  • outpoint (510-510)
  • outpoint (560-560)
src/wallet/rpc/spend.cpp (2)
src/rpc/util.cpp (2)
  • ParseHashO (106-109)
  • ParseHashO (106-106)
src/rpc/rawtransaction_util.cpp (2)
  • ConstructTransaction (24-132)
  • ConstructTransaction (24-24)
test/functional/rpc_fundrawtransaction.py (2)
test/functional/test_framework/util.py (4)
  • assert_raises_rpc_error (132-148)
  • assert_equal (69-74)
  • assert_greater_than (77-79)
  • assert_fee_amount (44-52)
src/wallet/rpc/spend.cpp (4)
  • fundrawtransaction (603-704)
  • fundrawtransaction (603-603)
  • signrawtransactionwithwallet (706-795)
  • signrawtransactionwithwallet (706-706)
src/wallet/wallet.cpp (1)
src/serialize.h (1)
  • GetSizeOfCompactSize (245-251)
test/functional/rpc_psbt.py (3)
test/functional/test_framework/util.py (2)
  • assert_greater_than (77-79)
  • assert_raises_rpc_error (132-148)
test/functional/test_framework/test_node.py (1)
  • get_wallet_rpc (363-369)
src/wallet/rpc/spend.cpp (2)
  • walletcreatefundedpsbt (1075-1208)
  • walletcreatefundedpsbt (1075-1075)
🪛 Flake8 (7.3.0)
test/functional/rpc_fundrawtransaction.py

[error] 10-10: 'math.ceil' imported but unused

(F401)

🪛 Ruff (0.14.7)
test/functional/wallet_send.py

542-542: Decimal() called with float literal argument

Replace with string literal

(RUF032)

test/functional/rpc_fundrawtransaction.py

1028-1028: Decimal() called with float literal argument

Replace with string literal

(RUF032)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: arm-linux-build / Build source
  • GitHub Check: linux64_nowallet-build / Build source
  • GitHub Check: mac-build / Build source
  • GitHub Check: linux64_ubsan-build / Build source
  • GitHub Check: linux64_sqlite-build / Build source
  • GitHub Check: linux64-build / Build source
  • GitHub Check: linux64_fuzz-build / Build source
  • GitHub Check: linux64_tsan-build / Build source
  • GitHub Check: win64-build / Build source
  • GitHub Check: Lint / Run linters
🔇 Additional comments (17)
src/wallet/spend.cpp (1)

553-556: Preset-input size override via CCoinControl weight is consistent and correct

Using HasInputWeight/GetInputWeight here to override input_bytes with GetVirtualTransactionSize(...) cleanly integrates caller‑supplied weights into coin selection and solvability checks, and preserves the existing -1 error path for inputs without a weight. This matches the intended behavior for external/nonstandard inputs.

src/wallet/wallet.h (1)

1134-1134: New FillInputToWeight API declaration is well-placed and consistent

The FillInputToWeight declaration in the wallet namespace matches its implementation and test usage, and sits logically next to DummySignInput as another input‑sizing helper.

src/wallet/wallet.cpp (2)

1661-1694: FillInputToWeight correctly pads inputs to a bounded target size

The helper cleanly enforces its preconditions (empty scriptSig, nondecreasing size) and uses compact‑size accounting so that the final serialized input size is guaranteed within [target_weight-2, target_weight]. This matches the documented 2‑byte tolerance and is exercised by the new unit tests.


1704-1712: DummySignTx integration with per-input weights behaves as intended

Short‑circuiting to FillInputToWeight when HasInputWeight(txin.prevout) is true lets callers fully control fee estimation for those inputs, and intentionally skips producing dummy signatures afterward. On failure, the function exits early, allowing higher‑level code to surface “missing solving data / bad weight” conditions as before.

src/wallet/coincontrol.h (1)

132-147: Per-input weight tracking in CCoinControl is coherent and minimally scoped

The new m_input_weights map and its SetInputWeight / HasInputWeight / GetInputWeight accessors integrate cleanly with existing COutPoint-keyed state, and match how the weights are consumed in coin selection and dummy signing. The assert in GetInputWeight is appropriate given current usage patterns.

Also applies to: 162-163

src/wallet/test/spend_tests.cpp (1)

61-106: FillInputToWeight tests thoroughly exercise edge cases and compact-size boundaries

The new helper and FillInputToWeightTest cover negative/too‑small targets, the exact empty‑input size, and multiple compact‑size transitions, verifying both overall serialized size and scriptSig length. This gives good confidence that FillInputToWeight behaves correctly across its range of expected inputs.

src/wallet/rpc/spend.cpp (4)

531-560: LGTM - Input size parsing and validation is thorough.

The validation logic correctly enforces:

  • vout must be present and non-negative
  • size must be present, at least 41 bytes (minimum input size), and at most MAX_STANDARD_TX_SIZE
  • The CHECK_NONFATAL(min_input_size == 41) assertion documents the expected minimum

The use of size in the RPC API (vs. weight in the internal API) is appropriate for Dash's non-SegWit transaction format where size and weight are equivalent.


586-601: Prevent options conflict for input sizes specified in inputs.

The helper correctly:

  1. Rejects input_sizes in options when inputs specify sizes (preventing ambiguity)
  2. Early-returns if no inputs
  3. Extracts only inputs that have a size field

One consideration: When inputs has elements but none contain size, this still pushes an empty input_sizes array to options. This is harmless but slightly redundant.


946-947: LGTM - Input weights correctly propagated in send RPC.

The call to SetOptionsInputWeights properly extracts size hints from options["inputs"] before passing to FundTransaction.


1174-1184: LGTM - Input weights correctly propagated in walletcreatefundedpsbt.

The options object is properly made mutable, SetOptionsInputWeights extracts size hints from inputs, and the modified options are passed to FundTransaction.

test/functional/wallet_send.py (1)

506-542: LGTM - Comprehensive test coverage for input weight functionality.

The test correctly:

  1. Computes input weight from the finalized PSBT
  2. Tests error condition when input_sizes is in options instead of inputs
  3. Validates funding works with explicit input weights
  4. Verifies mempool acceptance and fee calculation
test/functional/rpc_fundrawtransaction.py (2)

981-986: LGTM - Comprehensive error condition coverage for input_sizes validation.

Tests correctly verify all validation paths:

  • Missing vout key
  • Negative vout value
  • Missing size key
  • Size below minimum (40 bytes, which is less than 41)
  • Negative size
  • Size exceeding maximum standard tx size

1010-1033: LGTM - Weight-based funding tests are thorough.

The tests correctly verify:

  • Funding with explicit input weights works and passes mempool acceptance
  • Lower weight results in lower fee
  • Higher weight results in higher fee
  • Explicit weight overrides calculated weight when solving data is provided
  • Fee rate calculation is correct with explicit weight
  • Boundary values at csuint thresholds (255, 65539) don't cause issues
test/functional/rpc_psbt.py (4)

467-475: LGTM - Test setup for external input funding with PSBT.

The test correctly:

  1. Logs the test section
  2. Creates a new wallet "extfund" on node 1
  3. Sets up the necessary infrastructure for external input testing

507-521: Input weight calculation includes buffer for potential scriptwitness variation.

The formula input_weight = ((41 + len_scriptsig + 2) * 4) + len_scriptwitness includes a 2-byte buffer. This is appropriate for weight calculation in a SegWit-aware context, though Dash doesn't use SegWit. The calculation appears adapted from Bitcoin Core's approach.

For Dash's non-SegWit context, the simpler formula might be 41 + len_scriptsig, but the current approach is conservative and aligns with the backported code's comments about allowing up to 2-byte differences.

Please verify that this weight calculation formula aligns with the FillInputToWeight implementation mentioned in the PR notes, which permits up to 2-byte differences due to missing segregated-data stack handling.


523-563: LGTM - Comprehensive input weight testing via walletcreatefundedpsbt.

The tests correctly verify:

  1. Error when input_sizes is specified in options instead of inputs
  2. Funding with explicit input weight succeeds and passes mempool acceptance
  3. Lower weight produces lower fee
  4. Higher weight produces higher fee
  5. Explicit weight overrides calculated weight when solving data is provided

565-577: LGTM - Tests weight override for wallet inputs after descriptor import.

After importing the descriptor into the wallet, the test verifies that explicitly provided weights still override the wallet's calculated weight for inputs it can solve. This ensures the weight override behavior is consistent for both external and wallet inputs.

Comment on lines +643 to +653
{"input_sizes", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "Inputs and their corresponding sizes",
{
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
{"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output index"},
{"size", RPCArg::Type::NUM, RPCArg::Optional::NO, "The maximum size for this input, "
"including the size of the outpoint and sequence number. "
"Note that serialized signature sizes are not guaranteed to be consistent, "
"so the maximum DER signatures size of 73 bytes should be used when considering ECDSA signatures."
"Remember to convert serialized sizes to weight units when necessary."},
},
},
Copy link

@coderabbitai coderabbitai bot Dec 8, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

RPC documentation structure issue: input_sizes elements are not properly nested.

The input_sizes array documentation declares txid, vout, and size as direct children, but they should be wrapped in an object. The current structure doesn't match the expected input format [{"txid": ..., "vout": ..., "size": ...}, ...].

The input_sizes array should document its elements as objects. Consider applying this fix:

 {"input_sizes", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "Inputs and their corresponding sizes",
     {
-        {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
-        {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output index"},
-        {"size", RPCArg::Type::NUM, RPCArg::Optional::NO, "The maximum size for this input, "
+        {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
+            {
+                {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
+                {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output index"},
+                {"size", RPCArg::Type::NUM, RPCArg::Optional::NO, "The maximum size for this input, "
             "including the size of the outpoint and sequence number. "
             "Note that serialized signature sizes are not guaranteed to be consistent, "
             "so the maximum DER signatures size of 73 bytes should be used when considering ECDSA signatures."
             "Remember to convert serialized sizes to weight units when necessary."},
+            },
+        },
     },
  },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{"input_sizes", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "Inputs and their corresponding sizes",
{
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
{"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output index"},
{"size", RPCArg::Type::NUM, RPCArg::Optional::NO, "The maximum size for this input, "
"including the size of the outpoint and sequence number. "
"Note that serialized signature sizes are not guaranteed to be consistent, "
"so the maximum DER signatures size of 73 bytes should be used when considering ECDSA signatures."
"Remember to convert serialized sizes to weight units when necessary."},
},
},
{"input_sizes", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "Inputs and their corresponding sizes",
{
{"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
{
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
{"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output index"},
{"size", RPCArg::Type::NUM, RPCArg::Optional::NO, "The maximum size for this input, "
"including the size of the outpoint and sequence number. "
"Note that serialized signature sizes are not guaranteed to be consistent, "
"so the maximum DER signatures size of 73 bytes should be used when considering ECDSA signatures."
"Remember to convert serialized sizes to weight units when necessary."},
},
},
},
},
🤖 Prompt for AI Agents
In src/wallet/rpc/spend.cpp around lines 643 to 653, the RPCArg for
"input_sizes" currently lists "txid", "vout" and "size" as direct children of
the array but they must be defined as members of an object because the expected
input is an array of objects like [{"txid":...,"vout":...,"size":...},...].
Change the array element spec to be an object (RPCArg::Type::OBJ) and move
"txid", "vout" and "size" into that object's children with their current types
and descriptions, keeping the same optional/required flags and text.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@coderabbitai, compare this code as backported - minimal improvisation. Is there indeed bug or you propose an improvement?

Copy link

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for the upstream Bitcoin Core PR #23201 and #25551 to check the input_sizes documentation structure
# Let's search in the repository's git history for the relevant commits

# First, let's check if there are any references to these Bitcoin PRs in the commit messages
git log --all --grep="23201\|25551" --oneline | head -20

# Let's also search for the input_sizes RPC argument in the codebase to see how it's structured
rg -n "input_sizes.*RPCArg::Type::ARR" --type=cpp -A 15

Length of output: 3620


🌐 Web query:

Bitcoin Core PR 23201 input_sizes RPC documentation structure

💡 Result:

I can look that up — can you share a link to PR bitcoin#23201 (or tell me which repository/org it's in)? I couldn't find a matching GitHub PR or an "input_sizes" RPC with the information given.


🌐 Web query:

site:github.com bitcoin/bitcoin pull 23201 input_sizes

💡 Result:

Found Pull Request bitcoin#23201 on the bitcoin/bitcoin repo:

  • Title: "wallet: Allow users to specify input weights when funding a transaction" — author achow101. [1][2]

Sources:
[1] bitcoin-irc log referencing the PR. [2] Bitcoin Core PR Review Club notes for bitcoin#23201.


🏁 Script executed:

#!/bin/bash
# Let's fetch the Bitcoin Core repository to compare the exact structure
# First, check if we can find the upstream commit that added input_sizes

# Look for the commit that added input_sizes in the Dash repository
git log --all --grep="23201" --format="%H %s" | head -5

# Get one of the commits that mentions this PR
COMMIT_HASH=$(git log --all --grep="23201" --oneline | grep "Allow users to specify input weights" | head -1 | cut -d' ' -f1)

echo "Examining commit: $COMMIT_HASH"

# Show the diff for the fundrawtransaction RPC documentation in that commit
git show $COMMIT_HASH -- src/wallet/rpc/spend.cpp | grep -A 20 "input_sizes.*RPCArg::Type::ARR" || echo "Not found in this commit"

# Let's also check the current Bitcoin Core master branch structure
# We'll use curl to fetch the file from GitHub
curl -s "https://raw.githubusercontent.com/bitcoin/bitcoin/master/src/wallet/rpc/spend.cpp" | grep -A 20 "input_weights.*RPCArg::Type::ARR" || echo "Checking input_weights pattern"

Length of output: 4316


🏁 Script executed:

#!/bin/bash
# Let's check the actual upstream Bitcoin Core PR #23201 to see the original structure
# Fetch the specific file from the merge commit or the PR branch

# Let's get the Bitcoin Core commit from their repo
curl -s "https://raw.githubusercontent.com/bitcoin/bitcoin/b544010a712c3fa92ad73bf0bf785e1a6b53a15d/src/wallet/rpc/spend.cpp" | grep -B 2 -A 20 "input_weights.*RPCArg::Type::ARR" | head -30

echo "---"
echo "Now checking if Dash has input_weights anywhere:"
rg -n "input_weights" src/wallet/rpc/spend.cpp || echo "Not found - this is the dashification part (input_weights renamed to input_sizes)"

Length of output: 134


🏁 Script executed:

#!/bin/bash
# Let's check Bitcoin Core's current master for fundrawtransaction to see the input_weights structure
curl -s "https://raw.githubusercontent.com/bitcoin/bitcoin/master/src/wallet/rpc/spend.cpp" > /tmp/bitcoin_spend.cpp

# Search for fundrawtransaction and input_weights in the Bitcoin Core file
grep -A 100 "RPCHelpMan fundrawtransaction" /tmp/bitcoin_spend.cpp | grep -B 5 -A 15 "input_weights" | head -40

echo "========================================"
echo "Now let's check the walletcreatefundedpsbt for comparison:"
grep -A 150 "RPCHelpMan walletcreatefundedpsbt" /tmp/bitcoin_spend.cpp | grep -B 5 -A 15 "inputs.*RPCArg::Type::ARR" | head -50

Length of output: 3776


@knst, I've compared with the upstream Bitcoin Core code and this is indeed a bug in the backport, not just an improvement suggestion.In the upstream Bitcoin Core PR bitcoin#23201, the wallet RPCs send, walletcreatefundedpsbt, and fundrawtransaction add support for specifying the weights of external inputs. When I checked the actual Bitcoin Core code structure:

In Bitcoin Core's fundrawtransaction:

{"input_weights", RPCArg::Type::ARR, ...,
    {
        {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",  // ← OBJ wrapper present
            {
                {"txid", RPCArg::Type::STR_HEX, ...},
                {"vout", RPCArg::Type::NUM, ...},
                {"weight", RPCArg::Type::NUM, ...},
            },
        },
    },
}

In Dash's current code (this PR):

{"input_sizes", RPCArg::Type::ARR, ...,
    {
        {"txid", RPCArg::Type::STR_HEX, ...},     // ← Missing OBJ wrapper
        {"vout", RPCArg::Type::NUM, ...},
        {"size", RPCArg::Type::NUM, ...},
    },
}

The Dash backport renamed input_weightsinput_sizes and weightsize, but failed to preserve the intermediate OBJ structure from the upstream code. This is a structural bug in the backport, not just a style improvement.

The correct structure should have the empty-string OBJ element wrapping the fields, as CodeRabbit suggested. This matches how Bitcoin Core structures similar array-of-objects parameters (like inputs in walletcreatefundedpsbt).

Recommendation: Apply the CodeRabbit suggestion to fix the backport bug and match the upstream structure.

🐰✨


🧬 Code Graph Analysis Results

[src/wallet/spend.cpp] lines 1093-1140

  • Summary: CreateTransaction
    • Parameters:
      • CWallet& wallet: wallet instance.
      • const std::vector& vecSend: recipients to send to.
      • int change_pos: preferred change output position.
      • const CCoinControl& coin_control: coin selection and spending constraints.
      • bool sign: whether to sign the transaction.
      • int nExtraPayloadSize: extra payload space for signing data.
    • Returns: util::Result
    • Behavior:
      • Validates inputs (must have at least one recipient; amounts non-negative).
      • Locks the wallet and invokes internal transaction creation.
      • Logs tracing information about fee and change position.
      • If creation fails, returns an error via util::Error or propagates the failure.
      • On success, returns the created transaction and associated metadata.
    • Errors/Edge cases:
      • If the result is empty (creation failed), returns that failure.
      • Uses internal fee/change_pos, and may choose different internal flows (e.g., APS) depending on results.

[src/wallet/spend.cpp] lines 1142-1218

  • Summary: FundTransaction
    • Parameters:
      • CWallet& wallet: wallet instance.
      • CMutableTransaction& tx: the mutable transaction being funded (inputs/outputs may be updated).
      • CAmount& fee_out: output parameter for the fee estimated.
      • int& change_position: output parameter for the chosen change output position.
      • const UniValue& options: options for funding (inputs, change, fee rate, etc.).
      • CCoinControl& coinControl: coin control to be configured for funding.
      • bool override_min_fee: whether to override minimum fee requirements.
    • Returns: bool
    • Behavior:
      • If options are provided, validates keys and compatibility (e.g., feeRate vs fee_rate, conf_target vs fee_rate, etc.).
      • Parses options such as change address, change position, include_watching, inputs, etc.
      • Applies fee estimation mode via SetFeeEstimateMode.
      • Calls into internal funding logic to select inputs/outputs and adjust the transaction.
      • If funding succeeds, modifies tx accordingly and sets fee_out/change_position.
    • Errors/Edge cases:
      • Returns false and sets error via bilingual_str if funding fails (in caller context).
      • Ensures compatibility between various fee-related options to avoid conflicts.

[src/wallet/rpc/spend.cpp] lines ~SetFeeEstimateMode function (in same file)

  • Summary: SetFeeEstimateMode
    • Parameters:
      • const CWallet& wallet: wallet instance.
      • CCoinControl& cc: coin control to update with fee estimation settings.
      • const UniValue& conf_target: optional confirmation target (blocks).
      • const UniValue& estimate_mode: optional string mode ("unset", "economical", "conservative").
      • const UniValue& fee_rate: optional explicit fee rate (sat/vB or a rate value depending on context).
      • bool override_min_fee: whether to set fOverrideFeeRate to true to bypass minimum fee checks.
    • Returns: void
    • Behavior:
      • If fee_rate is provided:
        • Rejects if conf_target or estimate_mode are also provided (mutually exclusive).
        • Sets cc.m_feerate from fee_rate (3 significant digits) and enables fOverrideFeeRate if requested.
        • Returns early.
      • Otherwise, validates estimate_mode via FeeModeFromString and updates cc.m_fee_mode.
      • If conf_target is provided, parses it into cc.m_confirm_target with proper bounds.
    • Errors/Edge cases:
      • Throws JSONRPCError(RPC_INVALID_PARAMETER, ...) for invalid combinations or invalid values.

🧠 Learnings used
Learnt from: kwvg
Repo: dashpay/dash PR: 6530
File: src/validation.cpp:360-362
Timestamp: 2025-01-14T08:37:16.955Z
Learning: The UpdateTransactionsFromBlock() method in txmempool.cpp takes parameters in the order: vHashUpdate, ancestor_size_limit, ancestor_count_limit. The size limit comes before the count limit.

Learnt from: knst
Repo: dashpay/dash PR: 6916
File: src/univalue/include/univalue.h:81-88
Timestamp: 2025-10-25T07:08:51.918Z
Learning: For backport PRs from bitcoin/bitcoin, bitcoin-core/gui, etc., backported changes should match the original upstream PRs even if they appear strange, modify vendored code, or seem to violate coding guidelines. Still flag genuine issues like bugs, undefined behavior, crashes, compilation errors, or linter failures.

Learnt from: knst
Repo: dashpay/dash PR: 6805
File: src/wallet/rpc/wallet.cpp:357-357
Timestamp: 2025-08-08T07:01:47.332Z
Learning: In src/wallet/rpc/wallet.cpp, the upgradetohd RPC now returns a UniValue string message (RPCResult::Type::STR) instead of a boolean, including guidance about mnemonic backup and null-character passphrase handling; functional tests have been updated to assert returned strings in several cases.

Learnt from: kwvg
Repo: dashpay/dash PR: 6543
File: src/wallet/receive.cpp:240-251
Timestamp: 2025-02-06T14:34:30.466Z
Learning: Pull request #6543 is focused on move-only changes and refactoring, specifically backporting from Bitcoin. Behavior changes should be proposed in separate PRs.

Learnt from: knst
Repo: dashpay/dash PR: 6883
File: src/rpc/rawtransaction.cpp:1088-1125
Timestamp: 2025-10-13T12:37:12.357Z
Learning: In backport pull requests (especially from Bitcoin Core), treat "moved" or refactored code as out-of-scope for content-level review. Focus validation on verifying that code is moved correctly: no fields added, no fields removed, no fields reordered, and no unexpected changes beyond whitespace adjustments. Pre-existing issues in the upstream code should be preserved to maintain fidelity to the original implementation.

Learnt from: knst
Repo: dashpay/dash PR: 6871
File: contrib/guix/libexec/build.sh:358-360
Timestamp: 2025-10-05T20:38:28.457Z
Learning: In the Dash repository, when backporting code from Bitcoin Core, typos and minor issues in comments should be kept as-is to reduce merge conflicts in future backports, even if they remain unfixed in Bitcoin Core's master branch.

Learnt from: PastaPastaPasta
Repo: dashpay/dash PR: 6804
File: src/qt/proposalwizard.cpp:40-42
Timestamp: 2025-08-11T17:16:36.654Z
Learning: In the Dash repository, when a PR adds new files that are not from Bitcoin backports, these files must be added to the list in test/util/data/non-backported.txt. This applies to newly created files like qt/proposalwizard.{h,cpp} and forms/proposalwizard.ui. Limited exemptions may exist for subtrees and similar cases.

laanwj and others added 2 commits December 9, 2025 05:45
…n funding a transaction

3866272 tests: Test specifying input weights (Andrew Chow)
6fa762a rpc, wallet: Allow users to specify input weights (Andrew Chow)
808068e wallet: Allow user specified input size to override (Andrew Chow)
4060c50 wallet: add input weights to CCoinControl (Andrew Chow)

Pull request description:

  When funding a transaction with external inputs, instead of providing solving data, a user may want to just provide the maximum signed size of that input. This is particularly useful in cases where the input is nonstandard as our dummy signer is unable to handle those inputs.

  The input weight can be provided to any input regardless of whether it belongs to the wallet and the provided weight will always be used regardless of any calculated input weight. This allows the user to override the calculated input weight which may overestimate in some circumstances due to missing information (e.g. if the private key is not known, a maximum size signature will be used, but the actual signer may be doing additional work which reduces the size of the signature).

  For `send` and `walletcreatefundedpsbt`, the input weight is specified in a `weight` field in an input object. For `fundrawtransaction`,  a new `input_weights` field is added to the `options` object. This is an array of objects consisting of a txid, vout, and weight.

  Closes bitcoin#23187

ACKs for top commit:
  instagibbs:
    reACK bitcoin@3866272
  glozow:
    reACK 3866272 via range-diff
  t-bast:
    ACK bitcoin@3866272

Tree-SHA512: 2c8b471ee537c62a51389b7c4e86b5ac1c3a223b444195042be8117b3c83e29c0619463610b950cbbd1648d3ed01ecc5bb0b3c4f39640680da9157763b9b9f9f
…shes over silent ignore

fa277cd univalue: Throw exception on invalid pushes over silent ignore (MacroFake)
ccccc17 refactor: Default options in walletcreatefundedpsbt to VOBJ instead of VNULL (MacroFake)

Pull request description:

  The return value of the `push*` helpers is never used, but important to determine if the operation was successful. One way to fix this would be to add the "nodiscard" attribute. However, this would make the code (and this diff) overly verbose for no reason.

  So fix it by removing the never used return value. Also, fail verbosely in case of a programming mistake.

ACKs for top commit:
  furszy:
    code ACK fa277cd

Tree-SHA512: ef212a5bf5ae6bbad20acc4dafa3715521e81544185988d1eab724f440e4864a27e686aff51d5bc51b3017892c2eb8e577bcb8f37e8ddbaa0d8833bb622f2f9c
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/wallet/rpc/spend.cpp (1)

643-653: RPC documentation structure matches backport pattern.

Per the past review discussion and retrieved learnings, backported changes should match the original upstream PRs. This RPC argument structure for input_sizes follows the same pattern used in Bitcoin Core's implementation. Based on learnings, backported code should be preserved as-is unless there are genuine bugs.

🧹 Nitpick comments (2)
test/functional/wallet_send.py (1)

542-542: Use string literal for Decimal to avoid floating-point precision issues.

Per static analysis hint (RUF032), Decimal() should be called with a string literal instead of a float literal to avoid potential floating-point representation issues.

-        assert_fee_amount(testres["fees"]["base"], testres["vsize"], Decimal(0.0001))
+        assert_fee_amount(testres["fees"]["base"], testres["vsize"], Decimal("0.0001"))
test/functional/rpc_fundrawtransaction.py (1)

1027-1027: Use string literal for Decimal to avoid floating-point precision issues.

Per static analysis hint (RUF032), Decimal() should be called with a string literal instead of a float literal.

-        assert_fee_amount(funded_tx4["fee"], tx4_size, Decimal(0.0001))
+        assert_fee_amount(funded_tx4["fee"], tx4_size, Decimal("0.0001"))
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 151c1a5 and 0a38158.

📒 Files selected for processing (9)
  • src/wallet/coincontrol.h (2 hunks)
  • src/wallet/rpc/spend.cpp (9 hunks)
  • src/wallet/spend.cpp (1 hunks)
  • src/wallet/test/spend_tests.cpp (1 hunks)
  • src/wallet/wallet.cpp (2 hunks)
  • src/wallet/wallet.h (1 hunks)
  • test/functional/rpc_fundrawtransaction.py (3 hunks)
  • test/functional/rpc_psbt.py (3 hunks)
  • test/functional/wallet_send.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/wallet/wallet.h
  • src/wallet/wallet.cpp
  • src/wallet/spend.cpp
  • src/wallet/test/spend_tests.cpp
🧰 Additional context used
📓 Path-based instructions (3)
src/**/*.{cpp,h,hpp,cc}

📄 CodeRabbit inference engine (CLAUDE.md)

Dash Core implementation must be written in C++20, requiring at least Clang 16 or GCC 11.1

Files:

  • src/wallet/rpc/spend.cpp
  • src/wallet/coincontrol.h
src/wallet/**/*.{cpp,h}

📄 CodeRabbit inference engine (CLAUDE.md)

Wallet implementation must use Berkeley DB and SQLite

Files:

  • src/wallet/rpc/spend.cpp
  • src/wallet/coincontrol.h
test/functional/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Functional tests in test/functional/ must be written in Python (minimum version specified in .python-version) and depend on dashd and dash-node

Files:

  • test/functional/rpc_fundrawtransaction.py
  • test/functional/rpc_psbt.py
  • test/functional/wallet_send.py
🧠 Learnings (8)
📓 Common learnings
Learnt from: kwvg
Repo: dashpay/dash PR: 6543
File: src/wallet/receive.cpp:240-251
Timestamp: 2025-02-06T14:34:30.466Z
Learning: Pull request #6543 is focused on move-only changes and refactoring, specifically backporting from Bitcoin. Behavior changes should be proposed in separate PRs.
Learnt from: knst
Repo: dashpay/dash PR: 6916
File: src/univalue/include/univalue.h:81-88
Timestamp: 2025-10-25T07:08:51.918Z
Learning: For backport PRs from bitcoin/bitcoin, bitcoin-core/gui, etc., backported changes should match the original upstream PRs even if they appear strange, modify vendored code, or seem to violate coding guidelines. Still flag genuine issues like bugs, undefined behavior, crashes, compilation errors, or linter failures.
Learnt from: knst
Repo: dashpay/dash PR: 6883
File: src/rpc/rawtransaction.cpp:1088-1125
Timestamp: 2025-10-13T12:37:12.357Z
Learning: In backport pull requests (especially from Bitcoin Core), treat "moved" or refactored code as out-of-scope for content-level review. Focus validation on verifying that code is moved correctly: no fields added, no fields removed, no fields reordered, and no unexpected changes beyond whitespace adjustments. Pre-existing issues in the upstream code should be preserved to maintain fidelity to the original implementation.
📚 Learning: 2025-08-08T07:01:47.332Z
Learnt from: knst
Repo: dashpay/dash PR: 6805
File: src/wallet/rpc/wallet.cpp:357-357
Timestamp: 2025-08-08T07:01:47.332Z
Learning: In src/wallet/rpc/wallet.cpp, the upgradetohd RPC now returns a UniValue string message (RPCResult::Type::STR) instead of a boolean, including guidance about mnemonic backup and null-character passphrase handling; functional tests have been updated to assert returned strings in several cases.

Applied to files:

  • src/wallet/rpc/spend.cpp
  • test/functional/rpc_psbt.py
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{validation,txmempool}/**/*.{cpp,h} : Block validation and mempool handling must use extensions to Bitcoin Core mechanisms for special transaction validation and enhanced transaction relay

Applied to files:

  • src/wallet/rpc/spend.cpp
📚 Learning: 2025-10-25T07:08:51.918Z
Learnt from: knst
Repo: dashpay/dash PR: 6916
File: src/univalue/include/univalue.h:81-88
Timestamp: 2025-10-25T07:08:51.918Z
Learning: For backport PRs from bitcoin/bitcoin, bitcoin-core/gui, etc., backported changes should match the original upstream PRs even if they appear strange, modify vendored code, or seem to violate coding guidelines. Still flag genuine issues like bugs, undefined behavior, crashes, compilation errors, or linter failures.

Applied to files:

  • src/wallet/rpc/spend.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{validation,consensus,net_processing}/**/*.{cpp,h} : ValidationInterface callbacks must be used for event-driven architecture to coordinate subsystems during block/transaction processing

Applied to files:

  • src/wallet/rpc/spend.cpp
📚 Learning: 2025-01-14T08:37:16.955Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6530
File: src/validation.cpp:360-362
Timestamp: 2025-01-14T08:37:16.955Z
Learning: The UpdateTransactionsFromBlock() method in txmempool.cpp takes parameters in the order: vHashUpdate, ancestor_size_limit, ancestor_count_limit. The size limit comes before the count limit.

Applied to files:

  • src/wallet/rpc/spend.cpp
📚 Learning: 2025-06-09T16:43:20.996Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6718
File: test/functional/test_framework/test_framework.py:2102-2102
Timestamp: 2025-06-09T16:43:20.996Z
Learning: In the test framework consolidation PR (#6718), user kwvg prefers to limit functional changes to those directly related to MasternodeInfo, avoiding scope creep even for minor improvements like error handling consistency.

Applied to files:

  • test/functional/rpc_fundrawtransaction.py
  • test/functional/rpc_psbt.py
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/coinjoin/**/*.{cpp,h} : CoinJoin implementation must use masternode-coordinated mixing sessions with uniform denomination outputs

Applied to files:

  • src/wallet/coincontrol.h
🧬 Code graph analysis (3)
test/functional/rpc_fundrawtransaction.py (2)
test/functional/test_framework/util.py (4)
  • assert_raises_rpc_error (132-148)
  • assert_equal (69-74)
  • assert_greater_than (77-79)
  • assert_fee_amount (44-52)
src/wallet/rpc/spend.cpp (4)
  • fundrawtransaction (603-704)
  • fundrawtransaction (603-603)
  • signrawtransactionwithwallet (706-795)
  • signrawtransactionwithwallet (706-706)
test/functional/rpc_psbt.py (2)
test/functional/test_framework/util.py (2)
  • assert_greater_than (77-79)
  • assert_raises_rpc_error (132-148)
test/functional/test_framework/test_node.py (1)
  • get_wallet_rpc (363-369)
src/wallet/coincontrol.h (4)
src/wallet/spend.cpp (1)
  • outpoint (150-150)
src/wallet/wallet.cpp (1)
  • outpoint (1093-1093)
src/wallet/interfaces.cpp (4)
  • outpoint (399-399)
  • outpoint (399-399)
  • outpoint (400-400)
  • outpoint (400-400)
src/wallet/coinjoin.cpp (2)
  • outpoint (510-510)
  • outpoint (560-560)
🪛 Ruff (0.14.7)
test/functional/rpc_fundrawtransaction.py

1027-1027: Decimal() called with float literal argument

Replace with string literal

(RUF032)

test/functional/wallet_send.py

542-542: Decimal() called with float literal argument

Replace with string literal

(RUF032)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: linux64_multiprocess-build / Build source
  • GitHub Check: linux64_tsan-build / Build source
  • GitHub Check: win64-build / Build source
  • GitHub Check: linux64_sqlite-build / Build source
  • GitHub Check: linux64_ubsan-build / Build source
  • GitHub Check: linux64_fuzz-build / Build source
  • GitHub Check: linux64-build / Build source
  • GitHub Check: mac-build / Build source
  • GitHub Check: linux64_nowallet-build / Build source
  • GitHub Check: arm-linux-build / Build source
🔇 Additional comments (15)
src/wallet/coincontrol.h (2)

132-147: LGTM! Clean API for per-input weight tracking.

The three new methods follow a consistent pattern: SetInputWeight to store, HasInputWeight to check existence, and GetInputWeight with an assert precondition requiring callers to verify existence first. This matches the usage pattern in src/wallet/spend.cpp where HasInputWeight is always checked before GetInputWeight.


162-163: Comment accurately reflects Dash context.

The comment notes "weight (equals to size)" which is correct for Dash since there's no segregated witness data, making weight equivalent to serialized size. This aligns with the PR description noting the backport differs from Bitcoin Core due to missing segregated-data stack handling.

src/wallet/rpc/spend.cpp (4)

5-5: New include is appropriate.

The consensus/validation.h include is needed for MAX_STANDARD_TX_SIZE used in the size validation at line 554.


531-560: Robust input_sizes validation logic.

The parsing correctly validates:

  • txid via ParseHashO
  • vout presence, type, and non-negativity
  • size presence, minimum bound (41 bytes for outpoint + sequence + empty scriptSig), and maximum bound (MAX_STANDARD_TX_SIZE)

The CHECK_NONFATAL(min_input_size == 41) is a good compile-time sanity check ensuring the constant matches expectations.


586-601: SetOptionsInputWeights correctly enforces input specification location.

The guard at line 588-590 prevents users from specifying input_sizes in options when using send or walletcreatefundedpsbt, enforcing that sizes must be specified inline with inputs. The function then extracts and consolidates size specifications from inputs into the options for downstream processing.


1174-1184: Correct integration in walletcreatefundedpsbt.

The code properly:

  1. Creates a mutable copy of options if provided, or an empty object
  2. Calls SetOptionsInputWeights to extract per-input sizes from the inputs array
  3. Passes the enriched options to FundTransaction
test/functional/wallet_send.py (2)

506-514: Input weight calculation logic looks correct.

The code correctly:

  1. Decodes the PSBT to find the external input
  2. Locates the matching input by txid/vout
  3. Calculates input_weight = 41 + len_scriptsig - 1 where 41 is the base input size (outpoint + sequence + 1-byte scriptSig length), and the -1 compensates for the length byte already included in the base.

517-523: Good coverage for error path.

The test correctly verifies that specifying input_sizes in the options object (rather than inline with inputs) raises the expected RPC error with code -8.

test/functional/rpc_fundrawtransaction.py (3)

980-985: Comprehensive error path coverage for input_sizes validation.

The tests cover all validation scenarios:

  • Missing vout key
  • Negative vout
  • Missing size key
  • Size below minimum (40, -1)
  • Size above maximum (400001)

1000-1006: Input weight calculation is well-documented.

The calculation input_weight = signed_weight - unsigned_weight + 41 correctly derives the input's signing overhead by comparing signed vs unsigned transaction sizes, then adding back the base input components (prevout, sequence, 1-byte scriptSig length).


1030-1031: Good boundary testing for CompactSize encoding.

Testing at sizes 255 and 65539 validates correct handling at CompactSize encoding boundaries (1-byte to 3-byte, and 3-byte to 5-byte transitions), ensuring the weight serialization doesn't have off-by-one issues at these thresholds.

test/functional/rpc_psbt.py (4)

17-17: Import added for new assertion.

The assert_greater_than import is needed for the fee comparison assertions in the new weight-based tests.


515-521: Input weight formula includes witness handling (safely degenerates for Dash).

The formula input_weight = ((41 + len_scriptsig + 2) * 4) + len_scriptwitness is designed for Bitcoin's witness weight calculation. For Dash (no SegWit), len_scriptwitness will always be 0, and the formula correctly computes the input weight as (41 + len_scriptsig + 2) * 4. The +2 buffer provides margin for signature size variance.


523-531: Error path test for options-based input_sizes.

Consistent with wallet_send.py, this test verifies that specifying input_sizes in the options object raises RPC error -8 with the expected message.


533-577: Comprehensive PSBT weight-based funding tests.

The tests cover:

  1. Funding with exact input weight → mempool acceptance
  2. Lower weight → lower fee (line 549)
  3. Higher weight → higher fee (line 556)
  4. Provided weight overrides solving_data calculated weight (line 563)
  5. Wallet inputs also respect provided weights after descriptor import (lines 566-577)

This provides excellent coverage of the new input weight functionality.

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.

3 participants