PHP WebShell
Текущая директория: /opt/BitGoJS/modules/sdk-core/src/bitgo/utils
Просмотр файла: mpcUtils.ts
/**
* @prettier
*/
import assert from 'assert';
import { decrypt, readMessage, readPrivateKey, SerializedKeyPair } from 'openpgp';
import { IBaseCoin, KeychainsTriplet } from '../baseCoin';
import { BitGoBase } from '../bitgoBase';
import { AddKeychainOptions, Keychain, KeyType } from '../keychain';
import { encryptText, getBitgoGpgPubKey } from './opengpgUtils';
import {
IntentRecipient,
PopulatedIntent,
PrebuildTransactionWithIntentOptions,
TokenTransferRecipientParams,
} from './tss/baseTypes';
import { envRequiresBitgoPubGpgKeyConfig, isBitgoMpcPubKey } from '../tss/bitgoPubKeys';
import { coins, NetworkType } from '@bitgo/statics';
export interface MpcKeyShare {
publicShare: string;
privateShare: string;
privateShareProof?: string;
vssProof?: string;
}
export abstract class MpcUtils {
protected bitgo: BitGoBase;
protected baseCoin: IBaseCoin;
constructor(bitgo: BitGoBase, baseCoin: IBaseCoin) {
this.bitgo = bitgo;
this.baseCoin = baseCoin;
}
protected async decryptPrivateShare(privateShare: string, userGpgKey: SerializedKeyPair<string>): Promise<string> {
const privateShareMessage = await readMessage({
armoredMessage: privateShare,
});
const userGpgPrivateKey = await readPrivateKey({ armoredKey: userGpgKey.privateKey });
const decryptedPrivateShare = (
await decrypt({
message: privateShareMessage,
decryptionKeys: [userGpgPrivateKey],
format: 'utf8',
})
).data;
return decryptedPrivateShare;
}
protected async createBitgoKeychainInWP(
userGpgKey: SerializedKeyPair<string>,
backupGpgKey: SerializedKeyPair<string>,
userKeyShare: MpcKeyShare,
backupKeyShare: MpcKeyShare,
keyType: KeyType,
enterprise?: string
): Promise<Keychain> {
const bitgoKey = (await getBitgoGpgPubKey(this.bitgo)).mpcV1;
if (envRequiresBitgoPubGpgKeyConfig(this.bitgo.getEnv())) {
// Ensure the public key is one of the expected BitGo public keys when in test or prod.
assert(isBitgoMpcPubKey(bitgoKey.armor(), 'mpcv1'), 'Invalid BitGo GPG public key');
}
const encUserToBitGoMessage = await encryptText(userKeyShare.privateShare, bitgoKey);
const encBackupToBitGoMessage = await encryptText(backupKeyShare.privateShare, bitgoKey);
const createBitGoMPCParams: AddKeychainOptions = {
keyType,
source: 'bitgo',
keyShares: [
{
from: 'user',
to: 'bitgo',
publicShare: userKeyShare.publicShare,
privateShare: encUserToBitGoMessage,
privateShareProof: userKeyShare.privateShareProof,
vssProof: userKeyShare.vssProof,
},
{
from: 'backup',
to: 'bitgo',
publicShare: backupKeyShare.publicShare,
privateShare: encBackupToBitGoMessage,
privateShareProof: backupKeyShare.privateShareProof,
vssProof: backupKeyShare.vssProof,
},
],
userGPGPublicKey: userGpgKey.publicKey,
backupGPGPublicKey: backupGpgKey.publicKey,
enterprise: enterprise,
};
return await this.baseCoin.keychains().add(createBitGoMPCParams);
}
/**
* Creates User, Backup, and BitGo MPC Keychains.
*
* @param params.passphrase - passphrase used to encrypt signing materials created for User and Backup
* @param params.enterprise - optional enterprise id that will be attached to the BitGo Keychain
* @param params.originalPasscodeEncryptionCode - optional encryption code used to reset the user's password, if absent, password recovery will not work
*/
abstract createKeychains(params: {
passphrase: string;
enterprise?: string;
originalPasscodeEncryptionCode?: string;
}): Promise<KeychainsTriplet>;
/**
* This function would be responsible for populating intents
* based on the type of coin / sig scheme the coin uses
* @param {IBaseCoin} baseCoin
* @param {PrebuildTransactionWithIntentOptions} params
* @returns {Record<string, unknown>}
*/
populateIntent(baseCoin: IBaseCoin, params: PrebuildTransactionWithIntentOptions): PopulatedIntent {
const chain = this.baseCoin.getChain();
if (!['acceleration', 'fillNonce', 'transferToken'].includes(params.intentType)) {
assert(params.recipients, `'recipients' is a required parameter for ${params.intentType} intent`);
}
const intentRecipients = params.recipients?.map((recipient) => {
const formattedRecipient: IntentRecipient = {
address: { address: recipient.address },
amount: { value: `${recipient.amount}`, symbol: recipient.tokenName ? recipient.tokenName : chain },
};
if (recipient.data) {
formattedRecipient.data = recipient.data;
}
const { tokenData } = recipient;
if (tokenData && (tokenData.tokenContractAddress || tokenData.tokenName)) {
// token related recipient data gets validated in WP
if (!(tokenData.tokenType && tokenData.tokenQuantity)) {
throw new Error(
'token type and quantity is required to request a transaction with intent to transfer a token'
);
}
tokenData.tokenName = this.getTokenName(baseCoin, tokenData);
if (tokenData.tokenName) {
formattedRecipient.amount.symbol = tokenData.tokenName;
}
formattedRecipient.tokenData = tokenData;
}
return formattedRecipient;
});
const baseIntent: PopulatedIntent = {
intentType: params.intentType,
sequenceId: params.sequenceId,
comment: params.comment,
nonce: params.nonce,
recipients: intentRecipients,
};
if (baseCoin.getFamily() === 'eth' || baseCoin.getFamily() === 'polygon' || baseCoin.getFamily() === 'bsc') {
switch (params.intentType) {
case 'payment':
case 'transferToken':
case 'fillNonce':
return {
...baseIntent,
selfSend: params.selfSend,
feeOptions: params.feeOptions,
hopParams: params.hopParams,
isTss: params.isTss,
nonce: params.nonce,
custodianTransactionId: params.custodianTransactionId,
receiveAddress: params.receiveAddress,
};
case 'acceleration':
return {
...baseIntent,
txid: params.lowFeeTxid,
receiveAddress: params.receiveAddress,
feeOptions: params.feeOptions,
};
default:
throw new Error(`Unsupported intent type ${params.intentType}`);
}
}
this.baseCoin.setCoinSpecificFieldsInIntent(baseIntent, params);
if (params.feeOptions !== undefined) {
return {
...baseIntent,
memo: params.memo?.value,
token: params.tokenName,
enableTokens: params.enableTokens,
feeOptions: params.feeOptions,
};
}
return {
...baseIntent,
memo: params.memo?.value,
token: params.tokenName,
enableTokens: params.enableTokens,
};
}
getTokenName(baseCoin: IBaseCoin, tokenData: TokenTransferRecipientParams): string | undefined {
if (tokenData.tokenName) {
return tokenData.tokenName;
}
const networkPrefix = baseCoin.getConfig().network.type === NetworkType.TESTNET ? 't' : '';
const tokenStaticsKey = `${networkPrefix}${baseCoin.getFamily()}:${tokenData.tokenContractAddress}`;
if (coins.has(tokenStaticsKey)) {
const tokenStatics = coins.get(tokenStaticsKey);
tokenData.tokenName = tokenStatics.name;
}
return tokenData.tokenName;
}
}
Выполнить команду
Для локальной разработки. Не используйте в интернете!