fix(batch)!: NormalizeInnerTransaction returns a copy instead of rewriting its argument - #159
Conversation
|
@coderabbitai full review |
✅ Action performedFull review finished. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
Included review availability: 3 reviews are currently available. Your included PR review attempts over the past 7 days set your current allowance at 4 reviews per hour. 📝 WalkthroughWalkthroughThe change makes inner-transaction normalization non-mutating. Batch signing stores normalized transactions in the serialized payload before calculating IDs and signatures. Tests validate normalized blobs, signature preimages, and unchanged inputs. The package version is 11.2.0.0. ChangesBatch signing consistency
Estimated code review effort: 2 (Simple) | ~10 minutes Merge Risk: ⚪ Minimal · up to The change makes inner-transaction normalization explicit at the call site while preserving the expected batch signing behavior; no actionable merge-blocking risk remains beyond normal checks and review. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 10 functions across 3 files. (2 skipped: 2 unsupported.)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Comment |
…iting its argument The method strips TxnSignature, Signers and LastLedgerSequence and overwrites Fee, SigningPubKey and Flags. It did that to the caller's own JsonObject and returned that same instance, so anything a consumer held and handed over came back altered - from a public extension method whose signature reads as though it produces something new. The two overloads also disagreed. NormalizeInnerTransaction(object) rewrote its argument when the runtime type happened to be a JsonObject and did not when it was anything else: the same call, with aliasing decided by a type test the caller cannot see. SignAsBatchPart depended on that mutation, and not visibly. It normalises each inner transaction, hashes the results into the batch preimage, and encodes outer into the blob - and the normalised fields reached the blob only because normalisation rewrote the objects living inside outer. The call site read as though it collected a list for the txIDs and nothing more. It now writes the normalised transaction back explicitly, saying what the old code accomplished by side effect. Two tests hold the contract: the argument survives the call untouched, and the emitted blob still carries normalised inner transactions covered by the signature. Both were checked by mutation. The defensive DeepClone the batch tests used before the call is gone - it is exactly the ceremony this change removes. Xrpl moves to 11.2.0.0: the observable behaviour of a public method has changed.
f6bb638 to
8b9e30b
Compare
Second of the two steps agreed in #157. Stacked on #158 — until that merges, the diff here also shows its test commit.
The change
NormalizeInnerTransactionnormalises a copy and returns it, leaving the argument alone. The call site inSignAsBatchPartwrites the normalised transaction back:That line is the point of the change. The blob is encoded from
outerfurther down, and the batch preimage commits to the txIDs computed here — the old code got the normalised transactions into the blob purely because normalisation rewrote the objects insideouter, which nothing at the call site said.Why
The public surface. This is an extension method on
JsonObjectin a published package. It stripsTxnSignature,SignersandLastLedgerSequenceand overwritesFee,SigningPubKeyandFlags— and did so to whatever a consumer handed over, from a signature that reads as though it produces something new.The overloads disagreed.
NormalizeInnerTransaction(object)rewrote its argument when the runtime type happened to be aJsonObject, and did not when it was anything else. The same call, with aliasing decided by a type test the caller cannot see.The old behaviour was a trap. Adding a defensive clone — the obvious tidy-up — silently emitted blobs the signature had not committed to, and left all 1215 unit tests green. #158 closed that hole; this removes the trap itself.
Verification
Both contracts are pinned, and both pins were checked by mutation:
DeepCloneAll seven batch tests pass with the pure version. Full unit suite: 1217 passed, 0 failed.
The defensive
DeepClone()the batch tests performed before calling is gone — that ceremony is exactly what this removes. The remaining clone inBuildOuterBatchstays for an unrelated reason: aJsonNodecannot be attached to two parents.Release
Xrplmoves to 11.2.0.0 with aCHANGES.mdentry: the observable behaviour of a public method has changed. The base packages are untouched and stay where they are.Summary by CodeRabbit
Bug Fixes
Tests
Chores