feat(abstract-substrate): expose rawExtrinsicPayload separate from signablePayload#9141
Conversation
|
@claude review this |
|
Claude finished @abhijit0943's task —— View job Code Review Progress
Code Review ResultsThis PR successfully addresses the POLYX TSS signing issue by cleanly separating raw extrinsic payload bytes from the signable payload. The implementation is well-architected and maintains backward compatibility. ✅ StrengthsArchitecture & Design
Implementation Quality
Testing
🔧 Minor ImprovementsType Safety (modules/abstract-substrate/src/lib/transaction.ts:151) rawSignableHex: Buffer.from(this.rawExtrinsicPayload).toString('hex'),Consider caching this conversion since get rawSignableHex(): string {
return Buffer.from(this.rawExtrinsicPayload).toString('hex');
}Documentation Enhancement (modules/abstract-substrate/src/lib/iface.ts:124-129) /**
* Hex of the full raw encoded `ExtrinsicPayload` (see `Transaction.rawExtrinsicPayload`).
* Surfaced alongside the MPC/combine `signableHex` so consumers that need the raw payload
* (e.g. the HSM signing path) can use it for extrinsics larger than 256 bytes, where
* `signableHex` is the blake2_256 hash rather than the raw bytes.
*/Error Handling (modules/abstract-substrate/src/lib/transaction.ts:545-550) get rawExtrinsicPayload(): Uint8Array {
if (!this._substrateTransaction) {
throw new InvalidTransactionError('No transaction data available');
}
const extrinsicPayload = this._registry.createType('ExtrinsicPayload', this._substrateTransaction, {
version: EXTRINSIC_VERSION,
});
return extrinsicPayload.toU8a({ method: true });
}✅ Code Quality Assessment
📋 SummaryThis is a well-implemented solution that addresses the production issue with large POLYX nominate transactions. The refactoring improves code organization while solving the HSM signing path requirements. The minor suggestions above are optional enhancements that don't block the PR. Recommendation: ✅ Approve - Ready to merge after addressing any desired improvements. Job Run: Link |
032ee70 to
c4a57aa
Compare
|
@claude re-review |
|
Claude finished @abhijit0943's task —— View job Re-reviewing PR Changes
✅ Re-Review ResultsAfter analyzing the current implementation, this PR successfully addresses the POLYX TSS signing issue with a clean and well-architected solution. The implementation properly separates raw extrinsic payload bytes from signable payload for HSM compatibility. ✅ Strengths ConfirmedArchitecture & Design Excellence
Implementation Quality
Comprehensive Testing
🔧 Implementation AnalysisKey Changes Reviewed:
Code Quality Assessment:
✅ Test Coverage VerificationThe test suite comprehensively validates:
📋 SummaryThis PR represents a high-quality solution that:
The implementation is production-ready and demonstrates excellent software engineering practices. Final Recommendation: ✅ Approved - Excellent implementation, ready for merge. Job Run: GitHub Actions |
|
Three findings from reviewing this: 1. Added to 2. Missing The PR tests 3. Why 9 validators, not 8? Old comment said "6+" with 8 validators. PR bumps to 9 with comment "9 validators pushes over 256 bytes". What changed — is 8 no longer enough? Without an explanation (e.g. different validator address length in test data, or a payload field that grew), future maintainers can't reason about the boundary and might regress it. |
c4a57aa to
89e47e7
Compare
…gnablePayload Add a `rawExtrinsicPayload` getter that returns the full raw encoded `ExtrinsicPayload` bytes, and refactor `signablePayload` to derive from it via `utils.getSubstrateSigningBytes` (no duplicated 256-byte blake2 logic). Surface the raw payload as `rawSignableHex` on the polyx-specific `TxData`, populated in the polyx `toJson` output, so consumers that need the raw bytes (e.g. the HSM `polyx/signtx` path) can use them for large extrinsics where `signablePayload` is the blake2_256 hash. The field is kept on the polyx coin interface rather than the shared one, so other Substrate coins are unaffected. Generated with [Linear](https://linear.app/bitgo/issue/SI-926/polyx-expose-raw-extrinsicpayload-separately-from-signable-signing#agent-session-0ac3bf90) Co-authored-by: linear-code[bot] <222613912+linear-code[bot]@users.noreply.github.com>
89e47e7 to
50cae11
Compare
|
@claude re-review the changes are made |
|
Claude finished @abhijit0943's task —— View job Re-reviewing PR Changes
✅ Analysis of Doddanna17's FindingsAfter reviewing the current implementation, I can confirm that all three findings from Doddanna17 have been properly addressed: Finding 1: ✅ RESOLVED -
|
Doddanna17
left a comment
There was a problem hiding this comment.
rawExtrinsicPayloadcorrectly exposes pre-hash bytes;signablePayloadnow delegates to it — no duplicatedcreateTypelogic. Scope correctly limited to polyxTxData, other substrate coins unaffected.The
elsebranch in polyxtoJson()properly propagatesrawSignableHexfrom the pre-computed result without re-invoking the getter.Tests cover both the sub-256B identity case and the >256B hash-vs-raw divergence, with 9-validator count mirroring SI-926.
LGTM.
Summary
POLYX TSS follows Substrate signing rules: extrinsics over 256 bytes are signed as a
blake2_256hash rather than the raw payload.signablePayloadalready applies this (correct for user MPC/combine), but nothing exposed the rawExtrinsicPayloadbytes that the HSMpolyx/signtxpath needs. Large nominate/switch-validator txs (e.g. 9 validators) therefore broke signing on prod.This exposes the raw payload separately while keeping
signablePayloadas the correct MPC/combine signing bytes.Changes
abstract-substrate: add arawExtrinsicPayloadgetter (fullExtrinsicPayload.toU8a({ method: true })), and refactorsignablePayloadto derive from it viautils.getSubstrateSigningBytes— no duplicated 256-byte logic intransaction.ts.sdk-coin-polyx: surface the raw payload asrawSignableHexin the polyxtoJsonoutput, so consumers needing the raw bytes can read it alongsidesignableHex. The field is declared on the polyx-specificTxData(not the sharedabstract-substrateone), so other Substrate coins (tao, dot, etc.) are unaffected.Testing
yarn unit-testfor the nominate builder. New cases assert:signablePayloadequals the rawrawExtrinsicPayloadbytes, andtoJson().rawSignableHexmatches the raw hex.signablePayloadis the 32-byte blake2_256 hash,rawExtrinsicPayloadis the full raw payload (>256B), andtoJson().rawSignableHexmatches the raw hex. (Each target adds 33 bytes over 79 bytes of fixed overhead, so the extrinsic crosses 256B from 6 targets up; 9 mirrors the SI-926 scenario.)