wallet rpc: expose tx input sources from describeTxSet
What changed, and why it matters
This commit changes a Monero wallet library so that transaction data returned by the wallet's RPC interface now includes the input sources (where the funds came from) instead of silently ignoring them. Previously the code discarded the 'sources' field; now it parses each source's amount, global index, and public key into wallet output objects. This is a data-exposure/functional change, not a code vulnerability. It makes the library more complete and improves test coverage by verifying the parsed inputs match the transaction's input sum.
No security action required. This is a feature/test enhancement. Reviewers may want to confirm that exposing input sources does not violate any privacy expectations of library consumers, and that the pubkey substring logic remains correct across Monero RPC versions.
Security signals we found
Previously ignored RPC field is now parsed and exposed to callers
No input validation on cast fields (amount, global_index) beyond existing test assertions
Pubkey truncation to 64 chars may be lossy if upstream format assumptions change
Evidence from the diff
In MoneroWalletRpc.describeTxSet(), the ‘sources’ branch was previously a no-op. The patch now instantiates tx.getInputs() as a list of MoneroOutputWallet objects, populating amount, global_index (as long), and the first 64 chars of pubkey (treated as the destination/stealth public key). A test was added asserting that described tx sets have non-null, non-empty inputs, that each input references the parsed tx, has a valid unsigned amount, non-negative index, 64-char stealth public key, and that the sum of input amounts equals tx.getInputSum().
Changed components
src/main/java/monero/wallet/MoneroWalletRpc.javasrc/test/java/TestMoneroWalletCommon.javaInspect captured patch +26 / −1
### src/main/java/monero/wallet/MoneroWalletRpc.java
@@ -3053,7 +3053,19 @@ else if (key.equals("destinations") || key.equals("recipients")) {
if (transfer == null) transfer = new MoneroOutgoingTransfer().setTx(tx);
((MoneroOutgoingTransfer) transfer).setDestinations(destinations);
}
- else if (key.equals("sources")) {} // ignoring
+ else if (key.equals("sources")) {
+ GenUtils.assertTrue(tx.getInputs() == null);
+ tx.setInputs(new ArrayList<MoneroOutput>());
+ for (Map<String, Object> rpcSource : (List<Map<String, Object>>) val) {
+ MoneroOutputWallet input = new MoneroOutputWallet();
+ input.setTx(tx);
+ input.setAmount((BigInteger) rpcSource.get("amount"));
+ input.setIndex(((BigInteger) rpcSource.get("global_index")).longValue());
+ String pubkey = (String) rpcSource.get("pubkey");
+ if (pubkey != null) input.setStealthPublicKey(pubkey.substring(0, Math.min(64, pubkey.length()))); // dest key of dest||mask
+ tx.getInputs().add(input);
+ }
+ }
else if (key.equals("multisig_txset") && val != null) {} // handled elsewhere; this method only builds a tx wallet
else if (key.equals("unsigned_txset") && val != null) {} // handled elsewhere; this method only builds a tx wallet
else if (key.equals("amount_in")) tx.setInputSum((BigInteger) val);
### src/test/java/TestMoneroWalletCommon.java
@@ -6083,6 +6083,19 @@ private static void testDescribedTxSet(MoneroTxSet describedTxSet) {
for (MoneroDestination destination : parsedTx.getOutgoingTransfer().getDestinations()) {
testDestination(destination);
}
+
+ // test input sources
+ assertNotNull(parsedTx.getInputs());
+ assertFalse(parsedTx.getInputs().isEmpty());
+ BigInteger inputSum = BigInteger.valueOf(0);
+ for (MoneroOutputWallet input : parsedTx.getInputsWallet()) {
+ assertTrue(input.getTx() == parsedTx);
+ TestUtils.testUnsignedBigInteger(input.getAmount(), true);
+ assertTrue(input.getIndex() >= 0);
+ assertEquals(64, input.getStealthPublicKey().length());
+ inputSum = inputSum.add(input.getAmount());
+ }
+ assertEquals(inputSum, parsedTx.getInputSum());
}
}
Why this scored 19/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.