feat(sdk-coin-iota): add MPCv2 signed hot recovery support - #9426
feat(sdk-coin-iota): add MPCv2 signed hot recovery support#9426Marzooqa wants to merge 1 commit into
Conversation
Detects MPCv1 vs MPCv2 keycard format in Iota.recover() and dispatches signing accordingly, mirroring the SOL implementation. MPCv2 signatures are wrapped in the existing SUI-style envelope (0x00 flag + 64-byte sig + 32-byte pubkey). TICKET: WCI-1226
089d472 to
ed08c7c
Compare
|
Claude finished @Marzooqa's task —— View job Code Review for MPCv2 Signed Hot Recovery Support ✅Todo List:
OverviewThis PR successfully adds MPCv2 signed hot recovery support to the IOTA coin implementation. The changes follow the established pattern from SOL implementation and provide good backwards compatibility with MPCv1. ✅ Positive AspectsClean Architecture & Pattern Consistency
Comprehensive Test Coverage
Security & Error Handling
🔍 Technical Implementation DetailsSignature Format -
|
vibhavgo
left a comment
There was a problem hiding this comment.
Code review — local review against the MPCv2 signed hot recovery quality bar established across DOT/POLYX/SUI/ADA. Both helpers (
getEddsaSigningMaterial,signEddsaMpcV2RecoveryTx) are used correctly from@bitgo/sdk-core. MPCv1 regression test is present ✅.
1. Guard tests are in the wrong describe block
should handle error in recover function if a required field is missing lives in the MPCv1 describe('Recover Transactions:') block and uses MPCv1 key fixtures (keys.userKey, keys.backupKey). The describe('Recover Transactions (MPCv2):') block has zero guard tests — missing-field errors on the MPCv2 code path are untested.
Add to the MPCv2 describe:
should throw 'missing userKey'with MPCv2 key fixtures whenuserKeyis omittedshould throw 'missing backupKey'with MPCv2 key fixtures whenbackupKeyis omitted
2. No cryptographic signature verification
The MPCv2 native test validates envelope structure:
sigBuffer.length.should.equal(97);
sigBuffer[0].should.equal(0x00);This confirms the envelope is well-formed but does not verify that the inner 64 bytes are a valid Ed25519 signature over unsignedTx.signablePayload for the derived public key. tweetnacl is available in sdk-coin-iota's package.json (IOTA is SUI-derived, same dep tree). The pattern from prior PRs:
const rawSig = sigBuffer.slice(1, 65);
const isValid = nacl.sign.detached.verify(
new Uint8Array(signablePayload),
new Uint8Array(rawSig),
new Uint8Array(Buffer.from(derivedPublicKey, 'hex'))
);
isValid.should.be.true();Reviewed by Claude Code against the MPCv2 recovery PR series baseline.
Summary
Iota.recover(), alongside the existing MPCv1 path (auto-detected, no new params)EDDSAUtils.isEddsaMpcV1SigningMaterialand dispatches signing accordingly, mirroring the SOL implementation (WCI-398)EDDSAUtils.getEddsaMpcV2RecoveryKeySharesFromReducedKey+EDDSAUtils.signRecoveryEddsaMPCv2, validatescommonKeyChainagainstbitgoKey, and wraps the resulting 64-byte signature in the existing SUI-style envelope (0x00flag byte + 64-byte sig + 32-byte pubkey)recoverConsolidations()MPCv2 support is out of scope, tracked separately in WCI-1235Test plan
{ serializedTx, scanIndex }with correctly-formed signature envelopebitgoKeyvs keycardcommonKeyChain— throwsyarn unit-test(251 passing),tsc --noEmit,eslintall cleanTICKET: WCI-1226
🤖 Generated with Claude Code