fix(api): bound and truncate api signatures#6820
Open
Federico2014 wants to merge 4 commits into
Open
Conversation
3for
reviewed
Jun 4, 2026
3for
reviewed
Jun 4, 2026
3for
reviewed
Jun 4, 2026
b13b51a to
dfe3346
Compare
dfe3346 to
95599ad
Compare
95599ad to
a9bdbae
Compare
halibobo1205
reviewed
Jun 5, 2026
ce3b0d9 to
c859797
Compare
c859797 to
8dc1d8d
Compare
…provedlist-sig-limit
halibobo1205
approved these changes
Jun 5, 2026
Collaborator
halibobo1205
left a comment
There was a problem hiding this comment.
LGTM.
[SHOULD] Please make sure this behavior change is called out in the release notes / API changelog, so SDK and client owners can adapt.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Hardens the two read-only signature APIs —
getTransactionApprovedList(/wallet/getapprovedlist) andgetTransactionSignWeight— against CPU exhaustion and oversized-signature echo-back.1. Bound the recovery loop (CPU DoS)
Wallet.getTransactionApprovedListpreviously ran a hand-rolled unboundedecrecoverloop. Replaced by delegation toTransactionCapsule.checkWeight, matchinggetTransactionSignWeight.checkWeightrejects up front whensigs.size() > permission.getKeysCount(), before any signature recovery.2. Reject excessive signature counts at entry
Both APIs now check
trx.getSignatureCount() > getTotalSignNum()as the very first step, returningOTHER_ERROR/ "too many signatures" immediately. This is a zero-cost guard that mirrors the bound already enforced by the consensus signature-verification path.3. Truncate oversized signatures at entry
TransactionCapsule.truncateSignatures(trx)truncates any signature > 65 bytes to its first 65 bytes. The truncated bytes are copied out (ByteString.copyFrom(sig.substring(...).toByteArray())) rather than kept as aByteString.substringview — a bounded view would still pin the original oversized backing array, so the copy lets it be released. Called at the top of both methods; the echoed-back transaction is set on the response builder at entry, so no path leaks untruncated signatures.4. Harden checkWeight error message
weight == 0path previously hex-encodedsig.toByteArray()(potentially oversized) into the exception message. Changed to the transaction hash (hash), a fixed 32-byte value that is more useful for debugging and cannot bloat error responses. This protects all callers ofcheckWeight, including consensus paths.5. Keep the HTTP error path intact for result-only responses
The "too many signatures" early-return builds a result-only response with no
transactionfield.Util.printTransactionSignWeight/printTransactionApprovedListpreviously assumedtransactionalways exists, so on the HTTP servlet path they threw an NPE and the endpoint returned a generic servlet error instead of the intendedOTHER_ERROR/ "too many signatures". Both helpers now skip the nested-transaction rewrite when no transaction is present.Why are these changes required?
getTransactionApprovedListwas the only signature-recovery path without a bound on signature count. A single unauthenticated request with thousands of padded signatures could trigger unboundedecrecovercalls. Signature truncation, the memory-safe copy, and the error-message fix ensure clean, bounded, canonical responses on every code path — including the HTTP endpoints.This PR has been tested by
testApprovedListSigBound— signature withinkeysCountsucceeds; exceedingkeysCountrejected before recoverytestApprovedListSigTruncate— 70-byte signature truncated to 65 bytes, signer still recoveredtestApprovedListTooManySigs— exceedinggetTotalSignNum()rejected at entry with "too many signatures"testSignWeightSigTruncate— same truncation test forgetTransactionSignWeighttestSignWeightTooManySigs— same total-sign-num guard test forgetTransactionSignWeighttestPrintApprovedListTooManySigsHttpPath/testPrintSignWeightTooManySigsHttpPath— HTTP print helpers return theOTHER_ERROR/ "too many signatures" JSON (notransactionfield) instead of throwingWalletTest,TransactionUtilTest,UtilTest,RpcApiServicesTest— all green:framework:checkstyleMain,:framework:checkstyleTest— cleanFollow up
None.
Extra details
getTransactionApprovedListnow delegates tocheckWeight, applying the same permission semantics asgetTransactionSignWeight. Behavioral changes:permission.getKeysCount().Callers that relied on the old "recover any signature" behavior to probe arbitrary signer addresses will see stricter results. No database, consensus, or config changes; read-only API paths only.