Skip to content

Commit 401493f

Browse files
authored
Merge pull request #9557 from BitGo/fix/sdk-abstract-substrate/mpcv2-recovery
fix(abstract-substrate): remove double Ed25519 prefix in MPCv2 recovery
2 parents a2d21b1 + 06ec943 commit 401493f

2 files changed

Lines changed: 17 additions & 8 deletions

File tree

modules/abstract-substrate/src/abstractSubstrateCoin.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -517,8 +517,14 @@ export class SubstrateCoin extends BaseCoin {
517517

518518
/**
519519
* Adds an MPCv1 or MPCv2 signature to a Substrate transaction builder.
520-
* MPCv2 signatures are prefixed with ED25519_MULTI_SIGNATURE_PREFIX (Ed25519 discriminant
521-
* in the Substrate MultiSignature enum).
520+
*
521+
* Both branches hand off the raw 64-byte Ed25519 signature untouched.
522+
* Transaction#constructSignedPayload already prepends the 0x00 type-tag
523+
* (the Substrate MultiSignature enum discriminant for Ed25519) to whatever
524+
* signature buffer is passed to addSignature, so wrapping the signature
525+
* here would produce a double discriminant, corrupting the on-wire sig
526+
* bytes and causing the chain to reject the extrinsic with `1010: Bad
527+
* signature`.
522528
*/
523529
protected async addSubstrateRecoverySignature(
524530
txBuilder: NativeTransferBuilder,
@@ -530,7 +536,6 @@ export class SubstrateCoin extends BaseCoin {
530536
bitgoKey: string,
531537
accountId: string
532538
): Promise<void> {
533-
const ED25519_MULTI_SIGNATURE_PREFIX = 0x00;
534539
const substrateKeyPair = new SubstrateKeyPair({ pub: accountId });
535540

536541
if (signingMaterial.version === 'v2') {
@@ -543,8 +548,7 @@ export class SubstrateCoin extends BaseCoin {
543548
derivationPath: currPath,
544549
bitgo: this.bitgo,
545550
});
546-
const substrateSig = Buffer.concat([Buffer.from([ED25519_MULTI_SIGNATURE_PREFIX]), rawSig]);
547-
txBuilder.addSignature({ pub: substrateKeyPair.getKeys().pub }, substrateSig);
551+
txBuilder.addSignature({ pub: substrateKeyPair.getKeys().pub }, rawSig);
548552
} else {
549553
const userSigningMaterial = JSON.parse(signingMaterial.userPrv) as EDDSAMethodTypes.UserSigningMaterial;
550554
const backupPrv = await decryptKeychainPrivateKey(this.bitgo, { encryptedPrv: backupKey }, walletPassphrase);

modules/abstract-substrate/test/unit/abstractSubstrateCoin.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,12 @@ describe('SubstrateCoin MPCv2 recovery helpers:', function () {
8686
(coin as unknown as { bitgo: unknown }).bitgo = { decrypt: instanceDecryptStub };
8787
});
8888

89-
it('should prepend ED25519 0x00 discriminant on MPCv2 path', async function () {
89+
it('should pass the raw 64-byte signature to addSignature on MPCv2 path', async function () {
90+
// Regression: previously wrapped rawSig with a manual Ed25519 discriminant
91+
// (0x00) before addSignature. constructSignedPayload already prepends that
92+
// discriminant, so wrapping here caused a double prefix that shifted the
93+
// on-wire signature by one byte, dropping the last byte of `sigma` and
94+
// producing `1010: Bad signature` on-chain.
9095
const rawSig = Buffer.alloc(64, 0xab);
9196
sinon.stub(coin as unknown, 'signSubstrateMpcV2Recovery').resolves(rawSig);
9297

@@ -103,8 +108,8 @@ describe('SubstrateCoin MPCv2 recovery helpers:', function () {
103108

104109
addSignatureStub.calledOnce.should.be.true();
105110
const sig: Buffer = addSignatureStub.firstCall.args[1];
106-
sig[0].should.equal(0x00);
107-
sig.slice(1).should.deepEqual(rawSig);
111+
sig.length.should.equal(64);
112+
sig.should.deepEqual(rawSig);
108113
});
109114

110115
it('should call getTSSSignature and pass result to addSignature on MPCv1 path', async function () {

0 commit comments

Comments
 (0)