From 1dc91aa0ec4bc3b9f7d3e912e953e463a020c581 Mon Sep 17 00:00:00 2001 From: Rushil Kapoor Date: Fri, 30 Jun 2023 10:24:28 +0530 Subject: [PATCH] feat(abstract-utxo): move keysSignatures test to separate file Loop over all utxo coins BG-79423 --- modules/abstract-utxo/src/abstractUtxoCoin.ts | 26 ++++++++++++---- .../test/v2/unit/coins/abstractUtxoCoin.ts | 19 ++---------- .../test/v2/unit/coins/utxo/keySignatures.ts | 31 +++++++++++++++++++ .../sdk-core/src/bitgo/baseCoin/baseCoin.ts | 15 +++++++++ .../sdk-core/src/bitgo/baseCoin/iBaseCoin.ts | 8 +++++ 5 files changed, 76 insertions(+), 23 deletions(-) create mode 100644 modules/bitgo/test/v2/unit/coins/utxo/keySignatures.ts diff --git a/modules/abstract-utxo/src/abstractUtxoCoin.ts b/modules/abstract-utxo/src/abstractUtxoCoin.ts index 522c212b31..9c566610b1 100644 --- a/modules/abstract-utxo/src/abstractUtxoCoin.ts +++ b/modules/abstract-utxo/src/abstractUtxoCoin.ts @@ -281,9 +281,9 @@ export interface RecoverFromWrongChainOptions { } export interface VerifyKeySignaturesOptions { - userKeychain?: Keychain; - keychainToVerify?: Keychain; - keySignature?: string; + userKeychain: { pub?: string }; + keychainToVerify: { pub?: string }; + keySignature: string; } export interface VerifyUserPublicKeyOptions { @@ -744,7 +744,13 @@ export abstract class AbstractUtxoCoin extends BaseCoin { if (!keySignature) { throw new Error(`missing required custom change ${KeyIndices[keyIndex].toLowerCase()} keychain signature`); } - if (!this.verifyKeySignature({ userKeychain, keychainToVerify, keySignature })) { + if ( + !this.verifyKeySignature({ + userKeychain: userKeychain as { pub: string }, + keychainToVerify: keychainToVerify as { pub: string }, + keySignature, + }) + ) { debug('failed to verify custom change %s key signature!', KeyIndices[keyIndex].toLowerCase()); return false; } @@ -814,8 +820,16 @@ export abstract class AbstractUtxoCoin extends BaseCoin { // let's verify these keychains const keySignatures = parsedTransaction.keySignatures; if (!_.isEmpty(keySignatures)) { - const verify = (key, pub) => - this.verifyKeySignature({ userKeychain: keychains.user, keychainToVerify: key, keySignature: pub }); + const verify = (key, pub) => { + if (!keychains.user || !keychains.user.pub) { + throw new Error('missing user keychain'); + } + return this.verifyKeySignature({ + userKeychain: keychains.user as { pub: string }, + keychainToVerify: key, + keySignature: pub, + }); + }; const isBackupKeySignatureValid = verify(keychains.backup, keySignatures.backupPub); const isBitgoKeySignatureValid = verify(keychains.bitgo, keySignatures.bitgoPub); if (!isBackupKeySignatureValid || !isBitgoKeySignatureValid) { diff --git a/modules/bitgo/test/v2/unit/coins/abstractUtxoCoin.ts b/modules/bitgo/test/v2/unit/coins/abstractUtxoCoin.ts index 4ff7b7ecd7..edbdfc6949 100644 --- a/modules/bitgo/test/v2/unit/coins/abstractUtxoCoin.ts +++ b/modules/bitgo/test/v2/unit/coins/abstractUtxoCoin.ts @@ -1,7 +1,7 @@ import * as utxolib from '@bitgo/utxo-lib'; import * as should from 'should'; import * as sinon from 'sinon'; -import { Keychain, UnexpectedAddressError, VerificationOptions } from '@bitgo/sdk-core'; +import { UnexpectedAddressError, VerificationOptions } from '@bitgo/sdk-core'; import { TestBitGo } from '@bitgo/sdk-test'; import { BitGo } from '../../../../src/bitgo'; import { @@ -261,8 +261,7 @@ describe('Abstract UTXO Coin:', () => { }; // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const sign = async (key, keychain, coinToSignFor = coin) => - (await coinToSignFor.signMessage(keychain, key.pub!)).toString('hex'); + const sign = async (key, keychain) => (await coin.signMessage(keychain, key.pub!)).toString('hex'); const signUser = (key) => sign(key, userKeychain); const signOther = (key) => sign(key, otherKeychain); const passphrase = 'test_passphrase'; @@ -582,19 +581,5 @@ describe('Abstract UTXO Coin:', () => { coinMock.restore(); bitcoinMock.restore(); }); - - it('should verify key signature of ZEC', async () => { - const zecCoin = bitgo.coin('tzec') as AbstractUtxoCoin; - const userKeychain = await zecCoin.keychains().create(); - const otherKeychain = await zecCoin.keychains().create(); - - await zecCoin - .verifyKeySignature({ - userKeychain: userKeychain as unknown as Keychain, - keychainToVerify: otherKeychain as unknown as Keychain, - keySignature: await sign(userKeychain, otherKeychain, zecCoin), - }) - .should.be.true(); - }); }); }); diff --git a/modules/bitgo/test/v2/unit/coins/utxo/keySignatures.ts b/modules/bitgo/test/v2/unit/coins/utxo/keySignatures.ts new file mode 100644 index 0000000000..46b26613f4 --- /dev/null +++ b/modules/bitgo/test/v2/unit/coins/utxo/keySignatures.ts @@ -0,0 +1,31 @@ +import * as assert from 'assert'; +import { AbstractUtxoCoin } from '@bitgo/abstract-utxo'; +import { Keychain } from '@bitgo/sdk-core'; + +import { utxoCoins } from './util'; + +function describeWithCoin(coin: AbstractUtxoCoin) { + describe(`verifyKeySignatures for ${coin.getChain()}`, function () { + it('should verify key signature of ZEC', async () => { + const userKeychain = await coin.keychains().create(); + const backupKeychain = await coin.keychains().create(); + const bitgoKeychain = await coin.keychains().create(); + + const signatures = await coin.createKeySignatures( + userKeychain.prv, + { pub: backupKeychain.pub as string }, + { pub: bitgoKeychain.pub as string }, + ); + + assert.ok( + await coin.verifyKeySignature({ + userKeychain: (userKeychain as unknown) as Keychain, + keychainToVerify: (backupKeychain as unknown) as Keychain, + keySignature: signatures.backup, + })); + }); + }); +} + +utxoCoins.forEach(coin => describeWithCoin(coin)); + diff --git a/modules/sdk-core/src/bitgo/baseCoin/baseCoin.ts b/modules/sdk-core/src/bitgo/baseCoin/baseCoin.ts index 420f4473a0..078a65e4e4 100644 --- a/modules/sdk-core/src/bitgo/baseCoin/baseCoin.ts +++ b/modules/sdk-core/src/bitgo/baseCoin/baseCoin.ts @@ -471,4 +471,19 @@ export abstract class BaseCoin implements IBaseCoin { getHashFunction(): Hash { throw new NotImplementedError('getHashFunction is not supported for this coin'); } + + // `AbstractUtxoCoin` implements and uses the complementary `verifyKeySignature` method. + public async createKeySignatures( + prv: string, + backupKeychain: { pub: string }, + bitgoKeychain: { pub: string } + ): Promise<{ + backup: string; + bitgo: string; + }> { + return { + backup: (await this.signMessage({ prv }, backupKeychain.pub)).toString('hex'), + bitgo: (await this.signMessage({ prv }, bitgoKeychain.pub)).toString('hex'), + }; + } } diff --git a/modules/sdk-core/src/bitgo/baseCoin/iBaseCoin.ts b/modules/sdk-core/src/bitgo/baseCoin/iBaseCoin.ts index 0ed4f37c38..5fac9d3e13 100644 --- a/modules/sdk-core/src/bitgo/baseCoin/iBaseCoin.ts +++ b/modules/sdk-core/src/bitgo/baseCoin/iBaseCoin.ts @@ -473,4 +473,12 @@ export interface IBaseCoin { recoverToken(params: RecoverWalletTokenOptions): Promise; getInscriptionBuilder(wallet: Wallet): IInscriptionBuilder; getHashFunction(): Hash; + createKeySignatures( + prv: string, + backupKeychain: { pub: string }, + bitgoKeychain: { pub: string } + ): Promise<{ + backup: string; + bitgo: string; + }>; }