Repository navigation
TIP-935: Harden ECDSA Signature Validation #935
Description
Activity
- changed the title
[-]TIP: Harden ECDSA Signature Validation[/-][+]TIP-935: Harden ECDSA Signature Validation[/+]on Sep 15, 2026 Thanks for explicitly separating fresh-input admission from governance activation.
One useful addition would be a small, machine-readable SDK integration fixture for the auxiliary-query → broadcast boundary:
- Start with identical
raw_dataand two signature encodings: a valid 65-byte signature, and the same signature with an extra trailing byte. - Record the exact transaction submitted to and returned by
getTransactionSignWeight/getTransactionApprovedList. - Specify the expected fresh-admission result with the governance flag both disabled and enabled.
The important client-side assertion is that a successful query on a returned, normalized transaction must not mark the original padded transaction as broadcast-ready, even though their transaction IDs match. The final length check should apply to the exact transaction being serialized for broadcast, rather than silently repairing it during submission.
Shipping this fixture alongside the first node release implementing the admission policy would give wallet and multisig SDK maintainers a concrete migration test. This would operationalize the existing specification without changing the auxiliary APIs.
- Start with identical
@Redchar1992 Thanks for highlighting this. I think we should also reconsider preserving the truncation behavior in these two APIs.
My preference is for both
getTransactionSignWeightandgetTransactionApprovedListto reject any supplied signature whose length is not exactly 65 bytes, returningSIGNATURE_FORMAT_ERRORbefore conversion or recovery, instead of silently truncating it. Transactions with no signatures would retain their existing query behavior.This would align their signature-length checks with fresh-input admission, independently of governance activation. It would change the current API behavior, so the TIP and migration notes should explicitly document it.
Your proposed SDK fixture would still be useful: it should verify that both queries and fresh admission reject the padded signature with the governance flag either disabled or enabled, and that SDKs validate the exact transaction being serialized for broadcast.
Thanks, I agree that rejecting malformed signature lengths in both APIs is cleaner than preserving silent truncation.
Returning
SIGNATURE_FORMAT_ERRORbefore conversion or recovery would align their length checks with fresh-input admission, independently of governance activation. Preserving the existing behavior for transactions with no signatures is important for unsigned and multisig workflows.The fixture should therefore cover:
- A valid 65-byte signature proceeding to normal validation, and its padded variant being rejected by both queries and fresh admission, with the governance flag both disabled and enabled.
- Transactions with no signatures retaining their existing query behavior.
- SDKs checking the exact transaction serialized for broadcast, without silently trimming signatures during submission.
Since this changes existing API behavior, the TIP and migration notes should identify the first node release enforcing it, so clients do not mistakenly wait until governance activation to adapt.
@Redchar1992 Thanks, agreed. I’ve updated the TIP to specify rejection with
SIGNATURE_FORMAT_ERRORbefore conversion or recovery, preserve the existing behavior for transactions with no signatures, and document the API compatibility change.The test cases now cover the 65-byte and padded variants under both governance states, unsigned transactions, and checking the exact transaction serialized for broadcast without silently trimming signatures.
The first enforcing node release should also be explicitly identified in the TIP and migration notes once the release version is confirmed. Clients need to adapt before their serving nodes deploy that version; governance activation is a separate milestone.
Reacted by milovaneThe overall direction is sound, but a few points should be clarified before the TIP advances:
- Add a compact enforcement matrix covering deployment, the activating maintenance block, the first post-activation block, and historical replay. It should state which rules apply to RPC/P2P admission, block validation, relay HELLO, auxiliary queries, and TVM precompiles.
- Exact-65 enforcement removes the intentional 65–68 byte compatibility window introduced by #6782. Please provide compatibility evidence and a migration period, and document mixed-version behavior before activation.
- Consensus and TVM recovery should receive an immutable validation mode derived from the correct block-state snapshot. Async tasks and cached results must remain bound to that mode; full permission-verification caches must also account for permission-state changes.
- Define whether an auxiliary-query format error returns the original transaction unchanged or omits it, and publish machine-readable recovery vectors for other implementations.
With these activation, compatibility, and cache semantics made explicit, I support the proposal moving forward.
Thanks for the auxiliary-query update. Rejecting malformed signature lengths while preserving unsigned-query behavior gives SDK maintainers a clear migration contract. I also agree with @lxcmyf’s points on the enforcement matrix and permission-state dependencies.
Building on those comments, I suggest two required integration scenarios and one refinement to the existing TVM fixtures.
- Fork switching across activation
Could the suite explicitly cover activation at maintenance block H, strict validation at H+1, and then a switch to a valid competing branch where the proposal has not activated?
Block validation and TVM execution should follow that branch’s applicable proposal state, while the upgraded node’s fresh-input length policy remains enforced. The governance parameter being one-way should not make the validation mode irreversible across a chain rollback.
Switching back should also exercise cached results and outstanding verification tasks. The TIP already mentions fork-switching tests; making this specific scenario mandatory would give the deployment-policy versus consensus-state distinction a concrete acceptance test.
- Permission-cache reuse under changing state
TransactionCapsule.isVerified covers permission and weight checks as well as recovery. Manager.getVerifyTxs() matches pending transactions by txID and signature bytes, with owner-based exclusions for permission updates, before marking matching block transactions verified. Fork switching also explicitly clears verified state.
It would be useful to test these existing mechanisms together with the new validation mode:
- Identical transaction and signature bytes on branches with different account permissions.
- Pending-pool reuse when an AccountPermissionUpdateContract for the same owner is present.
The assertions should establish that authorization is evaluated against the applicable permission state and that neither cached nor late asynchronous results bypass the required checks. This would test the interaction without prescribing a particular cache design.
- Make the TVM infinity fixtures exercise the intended check
For ValidateMultiSign, the fixture should use its internally computed digest and explicitly configure the permission keys and threshold, so the legacy case reaches the infinity-derived address successfully and the strict case fails specifically because infinity recovery is rejected.
This would complement the existing ECRecover and mixed-batch tests, including both constant-call and worker-pool execution. It would also demonstrate the activation-dependent behavior without relying on a live mainnet account or assumptions about how deployed contracts use the result.
Publishing these fixtures alongside the SDK cases would give implementers reproducible acceptance criteria.
@Federico2014 Is v4.8.3 the target release for this?
@halibobo1205 Yes, v4.8.3 is the target release.
@lxcmyf Thanks. I agree these details should be explicit. Here is the intended behavior and what still needs to be documented.
- Enforcement timing
Let H be the maintenance block that enables the proposal:
Path on an upgraded node Before activation, including H H+1 onward Fresh RPC/P2P transaction admission Exactly 65 bytes per supplied signature Same Relay HELLO Exactly 65 bytes; strict ECDSA recovery Same Transaction and block-witness consensus validation Legacy validation Strict ECDSA validation TVM precompiles Legacy recovery Strict recovery, preserving existing ABI and entry-specific recovery-value rules Auxiliary queries, after the planned update is deployed Reject non-65-byte signatures Same Historical replay and fork switching must follow the applicable branch state. Switching to a branch where the proposal has not activated must restore legacy consensus/TVM behavior; deployment-time admission policies remain enforced. Original signature bytes must be checked before any truncation could hide an invalid length.
- Compatibility and migration
#6782 intentionally retained the 65–68-byte window. Before removing it, we need evidence of current padded-signature usage and compatibility results for affected wallets, SDKs, and signing services. Historical padded transactions alone do not establish whether clients still produce these signatures.
The migration period must precede deployment of the enforcing release. During mixed-version operation, older nodes may accept padded transactions that upgraded nodes reject at ingress. Before activation, however, otherwise valid blocks containing those transactions remain subject to legacy consensus rules. The release notes should state this explicitly and specify the migration dates once established.
- Cached verification results
Activation already clears the isVerified flag across the pending, re-push, popped, and pushing queues. Those transactions must pass strict validation before their verification results can be reused.
Validation and invalidation are synchronized on each transaction. If legacy validation is already running, invalidation waits for it to finish and then clears its result, preventing that result from surviving the activation transition.
I’ll document this mechanism explicitly. We should also verify its interaction with fork switching and permission-state changes, and make the validation mode used by asynchronous consensus and TVM tasks explicit.
- Auxiliary-query responses and recovery vectors
As discussed in my earlier reply, both auxiliary queries will reject non-65-byte signatures with SIGNATURE_FORMAT_ERROR in a follow-up change. I propose returning the original transaction unchanged on that error. Empty signature lists retain existing behavior; a supplied zero-length signature is rejected.
Machine-readable vectors should include exact inputs and expected legacy/strict outputs for scalar boundaries, recovery-byte aliases, and infinity recovery. TVM cases should cover all three precompiles, including a mixed BatchValidateSign batch where only the failed signature’s result byte becomes zero, through both constant-call and worker execution.
@waynercheung Thanks. I agree with these scenarios. The source analysis also identifies an important entry-point condition for the fork-switching test.
- Fork switching across activation
The test should cover branch A activating at maintenance block H, strict validation at H+1, switching to a valid branch B where the proposal remains disabled, and switching back to A. Consensus validation and TVM execution must follow the applicable branch state, while fresh-input admission continues requiring exactly 65-byte signatures.
The Head-state dependency predates this proposal. Our sample of 50,000 consecutive mainnet blocks found no witness signatures incompatible with the proposed rules, suggesting limited compatibility impact on ordinary block production. The fork test should therefore target the specific edge case where activation states differ and the candidate signature is valid only under legacy rules.
This needs coverage through both live P2P reception and block synchronization. Preliminary block-signature checks currently read the node’s Head state. Although fork switching subsequently rolls back state and revalidates signatures, a candidate rejected at the preliminary check never reaches that protection.
Assertions should check whether the candidate reaches branch-state validation, whether its provider is disconnected, and whether nodes can converge on the valid branch. Ordinary valid 65-byte signatures should serve as controls, and the reverse activation-state arrangement should also be covered.
The sample covered mainnet heights 86185078–86235077. It did not test transaction signatures, historical witness permissions, TVM, caches, or competing branches; those require separate validation.
- Permission-cache reuse
Activation already clears cached verification flags across the pending, re-push, popped, and pushing queues. Validation and invalidation are synchronized so an in-flight validation finishes before its result is cleared.
The additional tests should exercise this together with permission-update exclusions and fork-switch invalidation. Use identical transaction and signature bytes on branches where the signer respectively has and lacks sufficient permission, and verify that authorization follows the applicable state. Also include a pending AccountPermissionUpdateContract for the same owner and confirm that cached verification does not bypass the required permission checks. Outstanding verification tasks should be included in these transitions.
- ValidateMultiSign infinity fixture
Agreed. Construct the signature using the digest that ValidateMultiSign actually computes from the account address, permission ID, and data. Configure the selected permission with the infinity-derived address at weight 1 and threshold 1.
The legacy case should return a 32-byte true word; strict validation should return a 32-byte zero word because infinity recovery is rejected. The precompile call-success flag should remain true. This establishes that the changed recovery rule causes the different outcome.
These fixtures should include reproducible state setup and expected outputs alongside the SDK cases. BatchValidateSign should cover the valid/infinity/valid sequence, expecting result bytes 01 00 01 followed by zeros under strict validation, through both constant-call and worker execution.
Adding mainnet data to quantify the compatibility impact of requiring exactly 65-byte signatures.
A scan of blocks 72,179,711–82,691,711 covered 10,512,001 blocks with no scan failures and 3,633,093,120 transactions:
- 18,978,360 transactions (0.5224%) contained signatures longer than 65 bytes.
- These transactions involved 525,994 distinct owner addresses.
- The distribution among signatures longer than 65 bytes was:
Signature length Count 66 bytes 7,011 67 bytes 6 68 bytes 18,971,342 96 bytes 1 68-byte signatures account for 99.963% of the longer signatures. This suggests that migration planning should pay particular attention to clients producing this encoding, rather than focusing only on extreme lengths.
The maximum observed length was 96 bytes, in block 76,824,974: example transaction.
Under the current proposal, upgraded nodes enforce exact-length checks for fresh admission and the two auxiliary signature queries upon deployment, before governance activation. Preserving historical consensus validation therefore does not eliminate the client compatibility impact.
Before rollout, I suggest:
- Measuring recent daily/weekly usage to determine how much of this encoding remains active.
- Identifying the SDKs or signing services producing longer signatures and coordinating migration before node deployment.
- Using real 66/67/68/96-byte examples as regression fixtures for legacy historical validation and strict admission rejection.
The 0.5224% is a historical sample proportion, not a forecast of post-upgrade failures, and distinct owner addresses should not be interpreted as distinct users or client implementations.
Thanks @Federico2014 for the detailed follow-up, and @3for for the compatibility data. The proposed fixtures address the scenarios I raised. The preliminary Head-state check also identifies a concrete implementation issue that should be resolved before treating branch compatibility as covered.
I would suggest recording two acceptance conditions for the rollout.
1. Branch-correct handling from the network entry point
Could the implementation clarify how preliminary validation handles a candidate whose applicable branch state is not yet available? A signature failure under the local Head's rules should not, by itself, establish invalidity on the candidate branch or justify penalizing its provider.
The ingress paths discussed make the consequence concrete: on the advertisement path,
BLOCK_SIGN_INVALIDis mapped toReasonCode.BAD_BLOCKand disconnects the peer; on the synchronization path, the same error triggers theattackFlaghandling and aBAD_BLOCKdisconnect. If the preliminary check rejects an otherwise valid candidate solely because it applies the local Head's validation mode, that candidate never reaches branch-state revalidation.This particular ingress check concerns the block-witness signature. Ordinary valid 65-byte witness signatures should remain valid under both modes, consistent with the reported sample. A constructed legacy-only-valid signature is therefore useful for exercising the difference. The sample does not cover competing branches, however, so it cannot establish that this mismatch is unreachable in practice. The acceptance condition should be that preliminary validation does not reject an otherwise valid candidate, or disconnect its provider, solely because the local Head and the candidate branch require different validation modes.
Alongside the legacy-only-valid candidate and the ordinary valid controls already proposed, please also include a signature that is invalid under both rule sets. This verifies that preventing premature rejection does not weaken rejection of genuinely invalid signatures.
The test should start from live P2P reception and block synchronization and demonstrate the expected branch adoption and peer handling; directly exercising
switchFork()would miss the earlier rejection path. Linking the implementation and these test results would make this requirement reviewable.2. A deployment decision supported by current compatibility evidence
The reported 0.5224% establishes historical usage, and the concentration in 68-byte signatures gives the migration work a clear focus. It does not establish the current failure rate.
With v4.8.3 as the target, could the migration record include the recent measurement window, the confirmed affected client versions and their fixes, notification dates, and the criteria for starting exact-65 enforcement?
Please also identify the first enforcing release for each affected API. The TIP currently places auxiliary-query enforcement in the same release as admission enforcement, while the reply mentions a follow-up change; confirming that timing would help clients plan.
I support proceeding with the hardening. Incorporating these clarifications into the TIP, and linking the implementation, fixtures, and migration evidence, would make deployment readiness much easier to assess.
During historical synchronization, a malicious peer can append trailing bytes to the witness signature of an otherwise valid block. This does not change the block ID or signed digest, and legacy recovery ignores bytes beyond the first 65. Because pre-activation blocks must remain valid under legacy rules, strict validation activated by governance alone cannot prevent these padded representations from being accepted during historical synchronization.
If retained, the padding can increase storage and propagation costs and amplify memory consumption in block-query responses. We therefore need to trim trailing bytes from block witness signatures.
I propose clarifying the TIP as follows:
- Reject witness signatures shorter than 65 bytes.
- Trim signatures longer than 65 bytes to an independently copied 65-byte prefix before signature validation. Existing block-size limits still apply to the original incoming message.
- Validate the retained prefix under the rules applicable to the candidate branch. After activation, strict scalar bounds, recovery-ID checks, modular inversion, and point-at-infinity rejection still apply.
- Use the trimmed representation for storage, forwarding, and API output.
This would explicitly permit normalization of padded block witness signatures both before and after activation, replacing the current requirement to reject them based on their original length.
The exception would apply only to block witness signatures. Transaction signatures, relay HELLO signatures, and the two auxiliary transaction queries would retain their exact-length requirements.
@waynercheung Thanks for clarifying these two acceptance conditions.
For branch compatibility, the main practical concern is the difference in witness-signature length acceptance. As proposed in my latest comment, trimming trailing bytes under both legacy and strict modes would remove that difference, including when branches have different activation states.
For normal valid witness signatures, the other recovery changes should produce the same result before and after activation. Trimming therefore removes the main expected difference between the two modes.
For deployment, I agree that the historical 0.5224% demonstrates past usage rather than the current failure rate. Before rollout, we still need a recent measurement window, confirmed affected client versions and their fixes, notification dates, and clear conditions for starting exact-65 enforcement. The concentration in 68-byte signatures gives that migration work a useful focus.
Finally, v4.8.3 remains the target release. By “follow-up change,” I meant a separate change for the auxiliary queries, not enforcement in a later release. The intended release should include both admission and auxiliary-query length checks, as the TIP specifies. We should make that timing explicit in the TIP and release notes and update both if the release scope changes.
Thanks @Federico2014. Trimming makes sense for witness signatures: the suffix is outside the signed header and block ID, and removing it preserves the legacy recovery result.
One question on the remaining mode difference. With witness permissions enabled, a test fixture could configure a scheduled witness's permission key as the infinity-derived address
41dcc703c0e500b653ca82273b7bfad8045d85a470, with otherwise valid permission settings.For a block digest whose integer value satisfies
1 <= e < n, choosingr = x(G),s = e, and a recovery byte selectingR = G(wire value 0 or 27, sincey(G)is even) makesQ = r^-1 * (sR - eG)the point at infinity. The signature is exactly 65 bytes and its scalars are in range. Legacy recovery derives the configured address and passes the witness-signature check, while strict recovery rejects the final point.Keeping the witness permission identical on both branches would isolate the validation-mode difference. This provides a legacy-only-valid test input even after trimming removes the length difference.
Would you retain the ingress acceptance condition and use this fixture to verify that a node with a strict Head can receive and validate an otherwise valid legacy-branch candidate without prematurely rejecting it or disconnecting its provider?
@waynercheung The fixture is valid, but it requires a scheduled witness to be deliberately configured with the infinity-derived permission address. This is not a configuration we would expect from normal witness operation, and we have no evidence of it in practice.
java-tron's consensus relies on an honest supermajority of SRs. Assuming more than two thirds follow the applicable consensus rules, a small minority deliberately attempting to create forks cannot independently obtain the support needed to solidify a competing chain. The demonstrated acceptance difference therefore does not, by itself, establish a threat to the chain's overall consensus safety.
Accommodating this exceptional configuration could require broader changes to preliminary block validation and peer handling, affecting critical synchronization and consensus paths. Given the narrow prerequisite and the absence of demonstrated chain-level impact, I do not think the compatibility benefit justifies the additional complexity and regression risk.
I suggest retaining witness-signature trimming and tracking this exceptional case separately rather than expanding the current change.
@Federico2014 Thanks for confirming the fixture and explaining the implementation trade-off. I understand the concern about expanding changes to preliminary validation and peer handling.
Could we record this as a known remaining ingress limitation in the TIP, with the fixture linked from a follow-up tracking item?
The configuration is unusual, but it is permitted by the current permission-update rules. Witness-signature trimming removes the trailing-byte difference; it does not remove this recovery-result difference. The follow-up could record the expected candidate-branch validation result, and identify the preliminary rejection and peer-handling behavior that still needs testing.
That would keep the current implementation scope contained while making the compatibility boundary explicit for reviewers and other implementations.
@waynercheung Agreed. I’ll document the remaining ingress limitation and the related permission compatibility impact in the TIP.
- Remaining ingress limitation
Legacy recovery can turn a point-at-infinity result into a deterministic address instead of rejecting it. Your fixture configures that address as a witness permission key, allowing an exactly 65-byte signature to pass legacy validation while failing strict validation. Trimming trailing bytes does not remove this recovery-result difference.
Preliminary block validation uses the local Head state, which may differ from the candidate branch’s applicable state. If the local Head has activated strict validation while the candidate branch still uses legacy rules, an otherwise valid candidate may be rejected before branch-state revalidation, potentially also causing its provider to be disconnected and preventing the node from following that branch.
This Head-state dependency predates the TIP. End-to-end P2P and synchronization behavior still needs testing; persistent network divergence or finality impact has not been demonstrated. I’ll track this separately with your fixture and the expected legacy/strict results, keeping the current implementation scope contained.
- Related account-permission impact
The infinity-derived address used in this fixture is TW6a53SLU4vM4QJBnK2vWVx4cqTnDChS1m. The same recovery change also affects account permissions that rely on this address for authorization.
As of today’s check (September 30), this account’s Owner and Active permissions were both configured to use another address, TGxZme1LvLVqvPhbxF1en2kSsjeNGg6pNA. Its authorization therefore relies on ordinary signatures from that key, rather than infinity recovery. Provided this configuration is retained, TIP activation will not affect its normal use.
The future risk arises if this account changes its permissions back to require its own address, or another account configures a permission that requires the infinity-derived address to meet its threshold. After activation, infinity-recovery signatures can no longer provide that authorization, making the affected permission unusable. Without another usable permission or recovery path, the account would be unable to transfer assets or initiate transactions, although it could still receive funds.
The TIP does not disable the address itself. I’ll explicitly document the risk of permission configurations that depend on infinity recovery, alongside the remaining ingress limitation and the tests still outstanding.
Thanks @Federico2014, that covers both points.
One addition for the account-permission section: infinity recovery has been used by this account on mainnet. In a scan of TronGrid's confirmed outgoing-transaction index on October 1, 2026, 3,250 of the 3,268 transactions returned carried 65-byte signatures that recover to the point at infinity under legacy recovery. These transactions span blocks 7,807,040–41,441,952.
The last was the AccountPermissionUpdateContract at block 41,441,952, which set both the Owner and Active permission keys to
TGxZme1LvLVqvPhbxF1en2kSsjeNGg6pNA. All 18 subsequent transactions in that index use ordinary signatures recovering to that address, consistent with your observation about the current configuration.This history adds a pre-activation dimension to the permission warning: an infinity-recovery signature can supply the weight assigned to this address without possession of a corresponding private key. It also provides concrete historical compatibility cases: these signatures would fail strict recovery, but replay of their original pre-activation blocks must preserve legacy validation.
Could the TIP mention this history and use a few of these confirmed transactions as historical-replay fixtures alongside the constructed vectors?
@waynercheung Thanks. This adds an important distinction to the permission warning: under legacy recovery, an infinity-recovery signature can contribute the weight assigned to this address without possession of a corresponding private key. Whether that is sufficient for authorization still depends on the permission threshold and the remaining checks.
I’ll verify representative transactions from the reported history and document it in the TIP, with the scan date and index scope clearly stated. The permission-update transaction is particularly useful for explaining the transition to ordinary signing keys and why the account’s present configuration does not depend on infinity recovery.
I’ll also include that transaction and a few earlier examples as historical-replay fixtures alongside the constructed vectors. The fixtures should preserve the original transaction bytes and relevant historical permission state: replay under the original pre-activation rules must remain valid, while strict recovery must reject the infinity result.
This historical transaction evidence should remain distinct from the constructed witness-signature ingress case, whose end-to-end behavior still needs testing.
Thanks @Federico2014. Preserving the original transaction bytes and historical permission state gives these fixtures a clear validation context.
Could the historical fixture set also include this ordinary-signed transaction at block 41,476,026, from the same account after the permission update, as a control?
For this transaction, the recovered signer address should be
TGxZme1LvLVqvPhbxF1en2kSsjeNGg6pNAunder both legacy and strict validation, and permission validation should succeed against the recorded post-update Active permission (Permission_id=2).Paired with the infinity-signed permission-update fixture, this would demonstrate both sides of the transition: historical authorization remains reproducible under legacy rules, while the account's subsequent authorization through an ordinary signing key remains compatible with strict recovery.
@waynercheung Thanks, agreed. I’ll verify and include the ordinary-signed transaction at block 41,476,026 as a control, preserving its original bytes and the relevant post-update permission state.
I’ll check that both legacy and strict recovery produce
TGxZme1LvLVqvPhbxF1en2kSsjeNGg6pNA, and that permission validation succeeds against the recorded Active permission (Permission_id=2).Paired with the infinity-signed permission-update fixture, this should cover both historical authorization under legacy rules and subsequent ordinary-signature compatibility with strict recovery. I’ll record the observed results with the fixtures.
The TIP body has been revised to clarify strict recovery and its compatibility implications. Strict ECDSA recovery covers component bounds, recovery identifiers, digest validation, robust modular inversion, and rejection of infinity before address derivation. High-S acceptance and existing entry-specific recovery-byte rules are preserved.
-
Preliminary block validation and forks
Network-layer witness verification uses local Head state, which can differ from a candidate branch’s applicable rules. A legacy-valid block may therefore be rejected before branch-state revalidation. This is an existing design limitation.
Witness normalization removes the padding-related difference, but differences involving scalar bounds or infinity remain possible. Ordinary valid signatures work under both modes; the demonstrated infinity case requires exceptional witness permissions and deliberately constructed signatures. Consensus safety relies on an honest SR supermajority, while ingress rejection and branch adoption still require end-to-end verification. The mismatch can arise across activation states, including historical synchronization after activation. The remaining limitation is tracked separately.
-
The infinity-derived address and permissions
Historical usage reported in this discussion shows that constructed infinity-recovery signatures supplied authorization weight without possession of a corresponding private key. The account’s Owner and Active permissions were subsequently changed to an ordinary signing key.
TW6a53SLU4vM4QJBnK2vWVx4cqTnDChS1mremains usable as an account controlled through ordinary configured keys. After activation, infinity-recovery signatures cannot contribute permission weight. A permission becomes unusable if it depends on that weight to reach its threshold; permissions whose other usable keys meet the threshold remain functional. -
RPC/P2P admission, consensus activation, and verification reuse
Upgraded nodes require exactly 65 bytes per supplied transaction signature upon deployment. Transaction ECDSA consensus and TVM recovery remain legacy through the activating maintenance block
Hand become strict atH+1. Clients must generate compliant signatures before deployment.Consensus validation and any reused verification results must follow the rules and permissions applicable to the block being validated.
TVM retains its ABI and energy costs. Infinity recovery returns empty data for
ECRecover, a zero word forValidateMultiSign, and a zero at the failed signature’s position inBatchValidateSign, preserving other batch results and the precompile call-success flag. -
Auxiliary signature queries
getTransactionSignWeightandgetTransactionApprovedListare non-consensus interfaces. Exact-length checks and strict ECDSA recovery apply upon deployment. Neither interface rewrites signatures; early count/length errors omit the transaction extension. They evaluate the node’s current permission state, and query success does not certify broadcast or consensus validity. -
Relay authentication
RelayService HELLO authentication requires exactly 65 bytes and strict ECDSA recovery upon deployment, without waiting for governance activation.
-
Witness normalization versus transaction-byte preservation
Witness signatures on historical and newly produced blocks received through P2P can be normalized to an independently copied 65-byte prefix without changing the block ID or signed header. Original message-size limits still apply.
Transaction signatures inside received blocks cannot be truncated. Their bytes are excluded from txID but included in transaction Merkle leaves, affecting
txTrieRoot. Truncation breaks the original header commitment; updating the root changes the header and invalidates its original witness authentication. Historical transaction bytes must remain unchanged. -
PBFT scope
PBFT message-signature validation is unchanged by this TIP.
-
Thanks @Federico2014. The revised text captures the historical fixture pair, deployment-time query behavior, and remaining ingress limitation clearly.
Could the TIP link a dedicated tracking issue for the deferred preliminary block-ingress work? The current "tracked separately" link points back to the earlier discussion comment.
That issue could carry the already-agreed 65-byte legacy-only witness fixture and the outstanding end-to-end P2P and synchronization tests, with candidate-branch validation, peer handling, and branch-adoption results recorded as testing progresses.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsIn Progress
Simple Summary
Introduce governance-activated strict ECDSA recovery: validate signature components, harden modular inversion, and reject recovered public keys at infinity. Upgraded nodes also enforce deployment-time signature admission and query rules and normalize block witness signatures. Historical execution and high-S compatibility are preserved.
Abstract
ALLOW_STRICT_ECDSA_VALIDATIONenables strict transaction consensus and TVM recovery from the block after its activating maintenance block. Independently, deployment requires exactly 65 bytes at transaction ingress, relay HELLO, and auxiliary signature queries; ECDSA relay and query recovery become strict immediately. P2P block ingress retains only an independently copied 65-byte witness signature prefix.Transaction signatures must never be silently truncated: their bytes are committed by the block's transaction Merkle root. Witness signatures are outside that commitment. Permissions depending on infinity recovery require migration, and a known preliminary block-ingress limitation remains separately tracked.
Motivation
This TIP addresses four issues in legacy ECDSA recovery while preserving historical execution and high-S compatibility:
r, requiring resource behavior bounded by operand size.Specification
1. Activation and scope
The target release is java-tron v4.8.3. Proposed governance code
100andVERSION_4_8_3(block version39) remain subject to TIP discussion.0. Governance may set it to1after the required block version passes, and cannot disable it through a subsequent proposal.Hactivates it, consensus and TVM use legacy rules inHand strict rules fromH+1.The policies below apply on upgraded nodes. Preliminary block ingress has the limitation described under Backwards Compatibility.
Admission rules must not be applied to transactions inside historical blocks. Shared deployment-time length checks and witness normalization apply to both crypto engines. Strict recovery is ECDSA-specific; SM2 consensus recovery and PBFT message-signature validation are unchanged.
2. Encoding and strict recovery
Component bounds
Let
nbe the secp256k1 group order:Every strict recovery entry must reject null signatures/components, a null or non-32-byte digest,
rorsoutside[1, n), and normalizedrecIdoutside[0, 3]before curve operations or inversion. High-S remains accepted.Signature encoding
Transaction and witness protobuf signatures encode unsigned big-endian components:
Where exact length is required, reject any original length other than 65 before conversion, Base64 encoding, scalar construction, recovery, or task submission. Witness inputs first follow Section 3.
Transaction and witness wire aliases retain their existing mappings:
0..327..300..34..731..340..327..3027..300..331..3431..340..3All other values are invalid in strict mode. Compact headers
31..34are normalized by subtracting4, thenrecId = header - 27.TVM retains narrower input rules before and after activation:
ECRecover27or28recId = v - 27ValidateMultiSign,BatchValidateSign0,1,27, or280/1to27/28, then subtract27Each entry enforces its existing layout and recovery-value restrictions before shared recovery. Transaction/witness aliases must not broaden TVM acceptance.
Modular-inversion robustness
After component validation, construct candidate point
R, reject invalid coordinates, and perform the existing subgroup check. For validr, compute:Strict recovery must use an inversion algorithm whose work is bounded by operand size. The planned java-tron reference path uses Bouncy Castle
BigIntegers.modOddInverse(n, r); the legacy path retainsBigInteger.modInverse. Equivalent algorithms are permitted if they preserve the valid domain, mathematical results, and specified failure behavior. For everyrin[1, n), the inverse must match the legacy result and satisfyr * rInv mod n = 1.Point-at-infinity recovery
Recover:
When
Qis infinity, legacy recovery encodes it as{0x00}and hashes the empty public-key payload, deriving:That address has a valid format, but infinity is not a valid recovered public key. Strict recovery must reject infinity before public-key encoding, address derivation, or permission evaluation. Successful non-infinity recovery must match legacy results. Recovery must not mutate on-chain state; exception types and messages are implementation-specific.
3. Block witness normalization
Upon deployment, P2P block ingress, including historical synchronization, must:
Normalization must preserve header
raw_data, transactions, block ID, signed digest, and transaction Merkle root. It must not rewrite high-S values, recovery aliases, or transaction signatures. It does not bypass original size limits or establish validity.Existing database records need no bulk rewrite or compulsory read-time normalization. Old and newly synchronized archives may therefore expose different full protobuf bytes for the same block ID. Direct strict validation of an overlong, unnormalized signature must reject it; callers needing normalization must perform it explicitly.
4. TVM failure results
Strict infinity recovery returns the existing signature-validation failure result:
ECRecoverValidateMultiSignfalse)BatchValidateSign0Other batch results and the precompile call-success flag remain unchanged. This failure does not inherently revert the enclosing transaction. ABI errors, timeouts, energy accounting, and costs retain their existing semantics.
5. Admission, queries, and client handling
Fresh RPC/P2P admission requires exactly 65 bytes for every supplied signature upon deployment. Subsequent ECDSA recovery follows governance mode. RPC length failures return
SIGERROR; all other admission checks remain applicable.P2P transaction batches retain existing batch-level checks for duplicate transaction IDs, unsolicited transactions, and missing contracts, including their error and peer-handling behavior. Once these checks pass, remove request entries for all received transactions, including those subsequently rejected for signature length; retain unrelated outstanding requests.
Check signature lengths per transaction before either normal or smart-contract task submission. If any supplied signature is not exactly 65 bytes, discard that transaction without truncation and continue processing the remaining transactions through existing admission and queue handling. A length failure alone must not disconnect or mark the peer as bad. Passing the length check does not establish transaction validity.
Relay HELLO requires exactly 65 bytes and strict ECDSA recovery upon deployment.
getTransactionSignWeightandgetTransactionApprovedListmust:OTHER_ERRORprecedence.SIGNATURE_FORMAT_ERRORbefore hashingraw_data, conversion, recovery, or task submission.COMPUTE_ADDRESS_ERROR; later failures retain existing transaction-extension behavior.An empty signature list retains existing behavior; a supplied zero-length signature is invalid. Query results describe the node's current permission state. Queries can reject a 65-byte signature that pre-activation legacy consensus accepts, and success does not certify broadcast or consensus validity.
Shared helpers must select strict query recovery and applicable-branch consensus recovery explicitly. Signing workflows adopting strict checks for existing signatures must document their deployment timing. SDKs must check the exact broadcast payload, never silently trim signatures, and never transfer query results between different signature encodings merely because their txIDs match.
6. Verification reuse
Reuse requires an explicitly positive result and unchanged dependencies:
Input changes, activation, branch switching, and permission updates must invalidate or version affected results, including pending, re-push, popped, and pushing queues. Late asynchronous results remain tied to their original immutable input and context and must not become reusable authorization under a different context. No particular cache or context-object design is required.
Rationale
Why transaction signatures cannot be truncated
Transaction identity and the block's transaction commitment hash different data:
The latter includes signatures. Replacing a legacy-valid 68-byte signature with its 65-byte prefix preserves txID and the recovered signer, but changes the Merkle leaf and root. Keeping the original header then causes a
txTrieRootmismatch; updating that root changesBlockHeader.raw_data, block ID, and witness signing digest, so the original witness signature no longer authenticates the block.Validators must therefore preserve transaction signature bytes during received-block validation, storage, forwarding, and replay. Strict consensus rejects padding rather than repairing it. Before inclusion, clients may explicitly construct a corrected payload and submit it for fresh checks.
Witness signatures lie outside both header
raw_dataand transaction Merkle data; copying their interpreted prefix preserves these commitments. Existing transaction hashing and block Merkle construction show this distinction.Recovery compatibility
Low-S signing remains recommended, but verification retains high-S and existing entry-specific recovery aliases. Component and subgroup checks cannot replace the final infinity check. Governance controls consensus changes; deployment-time policies protect network and query boundaries without changing historical execution.
Backwards Compatibility
Client migration
Wallets, SDKs, hardware signers, exchanges, and signing services must emit exactly 65 bytes before deployment of the enforcing release, rather than waiting for governance. Clients must also handle missing query transaction extensions on early errors and strict query/relay recovery before activation.
Older nodes may still admit 65–68-byte signatures under PR #6782, while upgraded nodes reject padded transactions individually without disconnecting solely for this reason. Other transactions in the same batch continue through normal handling, but padded transactions still cannot propagate through upgraded nodes. Before activation, otherwise legacy-valid blocks containing padded transactions remain valid; historical replay preserves those bytes afterward.
A reported scan of blocks 72,179,711–82,691,711 found overlength signatures in 18,978,360 of 3,633,093,120 transactions (0.5224%), predominantly 68 bytes. This is historical, contributor-reported evidence, not a current failure-rate forecast. Before deployment, publish recent measurements, affected/fixed client versions, notification dates, the migration window, enforcing versions, and readiness criteria.
Permission migration and historical execution
Legacy infinity recovery can supply authorization weight assigned to
TW6a53SLU4vM4QJBnK2vWVx4cqTnDChS1mwithout a corresponding private key; authorization still depends on thresholds and other permission checks. Strict recovery removes that path. Permissions depending on its weight must migrate before activation. Without another usable permission or update path, affected accounts may be unable to initiate transactions or transfer assets, although they can receive funds. Query-dependent workflows are affected earlier, upon deployment.The address itself remains usable through ordinary configured keys. The author's September 30 check found ordinary Owner/Active permissions. A contributor's October 1 index scan reported 3,250 infinity-signed outgoing transactions followed by ordinary signatures after the permission update at block 41,441,952. These dated observations require independent verification using original transaction bytes and historical permissions; they do not guarantee current permissions. Original pre-activation blocks must replay under legacy rules.
Known preliminary block-ingress limitation
Existing preliminary witness validation reads local Head state, which can differ from candidate-branch activation or permissions. Normalization does not resolve this. For a reproducible mode-difference fixture, configure the scheduled witness key as the infinity-derived address on both branches. Given digest integer
1 <= e < n, chooseR = G,r = x(G),s = e, and recovery byte0or27. Its 65-byte signature can pass legacy authorization but fails strict recovery.A strict Head may reject this legacy-branch candidate before branch revalidation; existing ingress handling may disconnect its provider with
BAD_BLOCK. End-to-end behavior needs testing; persistent divergence or finality impact has not been demonstrated. This pre-existing dependency is tracked separately from the proposed normalization. Final validation still requires candidate-branch state. Record live-P2P and synchronization rejection, peer handling, and branch adoption, with signatures valid in both modes and invalid in both modes as controls.Test Cases
Publish conformance results for the following coverage; this draft does not claim completed validation. Tests should reference the rules above rather than redefine them.
01 00 01in constant and worker executionUse a deterministic infinity vector
R = G,r = x(G),s = 1,e = 1for direct recovery. ForValidateMultiSign, construct the signature from its actual internally computed account/permission/data digest and configure the infinity key with weight and threshold 1, so rejection is attributable to recovery. Also compare successful recovery andr * inverse(r) mod n = 1across legacy, strict, independent conformant, and enabled native implementations.Historical fixtures must independently verify original protobuf bytes and permission snapshots, including the reported permission update at block 41,441,952 and ordinary control at block 41,476,026. The control must authorize under recorded Active permission (
Permission_id=2), recoveringTGxZme1LvLVqvPhbxF1en2kSsjeNGg6pNAin both modes. Infinity fixtures must authorize under original legacy rules and fail strict recovery.Publish versioned JSON vectors at an immutable commit with entry point, engine, mode, exact digest/signature/input bytes, required permission or VM state, and expected recovery, authorization, and call results. Historical vectors include provenance; SDK vectors include exact query responses and broadcast bytes. Fixtures must not depend on live permissions. Results must identify code version, environment, coverage, and incomplete runs; helper tests do not replace network/activation tests.
Implementation
Implementation follows the specification finalized through TIP discussion:
getAllowStrictEcdsaValidation; preserve branch-aware legacy execution.Final governance allocation, release scope/dates, migration and mixed-version rollout evidence, historical fixture verification, and the separate preliminary-ingress follow-up remain open discussion/deployment items.
References
Copyright
Copyright and related rights waived via CC0.