Skip to content

Commit 0fd9714

Browse files
committed
feat(sdk-core): added OFC BitGo signing on wallet and coins object
allow wallet and coins object to sign using the BitGo key if the passphrase is not provided during signing Ticket: WCN-217-2
1 parent b6be824 commit 0fd9714

7 files changed

Lines changed: 381 additions & 53 deletions

File tree

modules/sdk-core/src/bitgo/trading/tradingAccount.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ export class TradingAccount implements ITradingAccount {
2222
return this.wallet.id();
2323
}
2424

25+
get userKeySigningRequired(): boolean {
26+
const walletData = this.wallet.toJSON();
27+
return walletData.coinSpecific?.userKeySigningRequired ?? walletData.userKeySigningRequired ?? true;
28+
}
29+
2530
/**
2631
* Signs an arbitrary payload. Use the user key if passphrase/prv is provided, or the BitGo key if not.
2732
* @param params
@@ -48,8 +53,7 @@ export class TradingAccount implements ITradingAccount {
4853
params: Omit<SignPayloadParameters, 'walletPassphrase' | 'prv'>
4954
): Promise<string> {
5055
const walletData = this.wallet.toJSON();
51-
const userKeySigningRequired = walletData.coinSpecific?.userKeySigningRequired ?? walletData.userKeySigningRequired;
52-
if (userKeySigningRequired) {
56+
if (this.userKeySigningRequired) {
5357
throw new Error(
5458
'Wallet must use user key to sign ofc transaction, please provide the wallet passphrase or visit your wallet settings page to configure one.'
5559
);

modules/sdk-core/src/bitgo/wallet/wallet.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2137,7 +2137,7 @@ export class Wallet implements IWallet {
21372137
* @param params
21382138
* - txPrebuild
21392139
* - [keychain / key] (object) or prv (string)
2140-
* - walletPassphrase
2140+
* - walletPassphrase (optional ONLY for OFC wallets with userKeySigningRequired = false)
21412141
* - verifyTxParams (optional) - when provided, the transaction will be verified before signing
21422142
* - txParams: transaction parameters used for verification
21432143
* - verification: optional verification options
@@ -2264,6 +2264,17 @@ export class Wallet implements IWallet {
22642264
};
22652265
return params.customSigningFunction(signTransactionParamsWithSeed);
22662266
}
2267+
2268+
if (this.baseCoin.getFamily() === 'ofc') {
2269+
const userKeySigningRequired = this.toTradingAccount().userKeySigningRequired;
2270+
const prv = userKeySigningRequired ? await this.getUserPrvAsync(presign as GetUserPrvOptions) : undefined;
2271+
return this.baseCoin.signTransaction({
2272+
...signTransactionParams,
2273+
prv,
2274+
wallet: this,
2275+
});
2276+
}
2277+
22672278
return this.baseCoin.signTransaction({
22682279
...signTransactionParams,
22692280
prv: await this.getUserPrvAsync(presign as GetUserPrvOptions),

modules/sdk-core/src/coins/ofc.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
SignTransactionOptions,
1616
VerifyAddressOptions,
1717
VerifyTransactionOptions,
18+
Wallet,
1819
} from '../';
1920

2021
export class Ofc extends BaseCoin {
@@ -104,6 +105,26 @@ export class Ofc extends BaseCoin {
104105
throw new MethodNotImplementedError();
105106
}
106107

108+
/**
109+
* Signs a message using a trading wallet's BitGo Key
110+
* @param wallet - uses the BitGo key of this trading wallet to sign the message remotely in a KMS
111+
* @param message
112+
*/
113+
async signMessage(wallet: Wallet, message: string): Promise<Buffer>;
114+
/**
115+
* Signs a message using the private key
116+
* @param key - uses the private key to sign the message
117+
* @param message
118+
*/
119+
async signMessage(key: { prv: string }, message: string): Promise<Buffer>;
120+
async signMessage(keyOrWallet: { prv: string } | Wallet, message: string): Promise<Buffer> {
121+
if (!(keyOrWallet instanceof Wallet)) {
122+
return super.signMessage(keyOrWallet, message);
123+
}
124+
const signatureHexString = await keyOrWallet.toTradingAccount().signPayload({ payload: message });
125+
return Buffer.from(signatureHexString, 'hex');
126+
}
127+
107128
/** @inheritDoc */
108129
auditDecryptedKey(params: AuditDecryptedKeyParams) {
109130
throw new MethodNotImplementedError();

modules/sdk-core/src/coins/ofcToken.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
SignTransactionOptions as BaseSignTransactionOptions,
1010
SignedTransaction,
1111
ITransactionRecipient,
12+
Wallet,
1213
} from '../';
1314
import { isBolt11Invoice } from '../lightning';
1415

@@ -18,7 +19,8 @@ export interface SignTransactionOptions extends BaseSignTransactionOptions {
1819
txPrebuild: {
1920
payload: string;
2021
};
21-
prv: string;
22+
prv?: string;
23+
wallet?: Wallet;
2224
}
2325

2426
export { OfcTokenConfig };
@@ -107,15 +109,25 @@ export class OfcToken extends Ofc {
107109
}
108110

109111
/**
110-
* Assemble keychain and half-sign prebuilt transaction
112+
* Signs a half-signed OFC transaction.
113+
* Signs the transaction remotely using the BitGo key if prv is not provided.
111114
* @param params
112115
* @returns {Promise<SignedTransaction>}
113116
*/
114117
async signTransaction(params: SignTransactionOptions): Promise<SignedTransaction> {
115118
const txPrebuild = params.txPrebuild;
116119
const payload = txPrebuild.payload;
117-
const signatureBuffer = (await this.signMessage(params, payload)) as any;
118-
const signature: string = signatureBuffer.toString('hex');
120+
121+
let signature: string;
122+
if (params.wallet) {
123+
signature = await params.wallet.toTradingAccount().signPayload({ payload, prv: params.prv });
124+
} else if (params.prv) {
125+
const signatureBuffer = (await this.signMessage({ prv: params.prv }, payload)) as any;
126+
signature = signatureBuffer.toString('hex');
127+
} else {
128+
throw new Error('You must pass in either one of wallet or prv');
129+
}
130+
119131
return { halfSigned: { payload, signature } } as any;
120132
}
121133

modules/sdk-core/test/unit/bitgo/trading/tradingAccount.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@ describe('TradingAccount', function () {
5454
toJSON: sinon.stub().returns({
5555
id: 'test-wallet-id',
5656
keys: ['user-key-id', 'backup-key-id', 'bitgo-key-id'],
57-
coinSpecific: {},
57+
coinSpecific: {
58+
userKeySigningRequired: false,
59+
},
5860
}),
5961
baseCoin: mockBaseCoin,
6062
bitgo: mockBitGo,
@@ -85,7 +87,6 @@ describe('TradingAccount', function () {
8587
it('should sign using the BitGo key remotely when no passphrase is provided', async function () {
8688
const result = await tradingAccount.signPayload({ payload });
8789

88-
mockWallet.toJSON.calledOnce.should.be.true();
8990
mockBitGo.post.calledOnce.should.be.true();
9091
sendStub.calledWith({ payload: JSON.stringify(payload) }).should.be.true();
9192
result.should.equal(signature);
@@ -98,11 +99,13 @@ describe('TradingAccount', function () {
9899
});
99100

100101
it('should throw if coinSpecific.userKeySigningRequired is true and no passphrase and prv are provided', async function () {
101-
mockWallet.toJSON.onCall(mockWallet.toJSON.callCount).returns({
102+
const mockWalletJSON = {
102103
id: 'test-wallet-id',
103104
keys: ['user-key-id', 'backup-key-id', 'bitgo-key-id'],
104105
coinSpecific: { userKeySigningRequired: true },
105-
});
106+
};
107+
mockWallet.toJSON.onCall(mockWallet.toJSON.callCount).returns(mockWalletJSON);
108+
mockWallet.toJSON.onCall(mockWallet.toJSON.callCount + 1).returns(mockWalletJSON);
106109

107110
await tradingAccount
108111
.signPayload({ payload })
@@ -112,12 +115,14 @@ describe('TradingAccount', function () {
112115
});
113116

114117
it('should fall back to top-level userKeySigningRequired when coinSpecific does not carry it', async function () {
115-
mockWallet.toJSON.onCall(mockWallet.toJSON.callCount).returns({
118+
const mockWalletJSON = {
116119
id: 'test-wallet-id',
117120
keys: ['user-key-id', 'backup-key-id', 'bitgo-key-id'],
118121
coinSpecific: {},
119122
userKeySigningRequired: true,
120-
});
123+
};
124+
mockWallet.toJSON.onCall(mockWallet.toJSON.callCount).returns(mockWalletJSON);
125+
mockWallet.toJSON.onCall(mockWallet.toJSON.callCount + 1).returns(mockWalletJSON);
121126

122127
await tradingAccount
123128
.signPayload({ payload })

0 commit comments

Comments
 (0)