fix(psbt): parse param schema, return error, closes #3804

This commit is contained in:
kyranjamie
2023-06-19 14:31:02 +02:00
committed by kyranjamie
parent 73444e47bd
commit 950a28a955
3 changed files with 57 additions and 24 deletions

View File

@@ -3,7 +3,11 @@ import * as btc from '@scure/btc-signer';
import { hexToBytes } from '@stacks/common';
import { RouteUrls } from '@shared/route-urls';
import { SignPsbtRequest } from '@shared/rpc/methods/sign-psbt';
import {
SignPsbtRequest,
getRpcSignPsbtParamErrors,
validateRpcSignPsbtParams,
} from '@shared/rpc/methods/sign-psbt';
import { makeRpcErrorResponse } from '@shared/rpc/rpc-methods';
import { ensureArray, isDefined } from '@shared/utils';
@@ -25,24 +29,23 @@ function validatePsbt(hex: string) {
}
export async function rpcSignPsbt(message: SignPsbtRequest, port: chrome.runtime.Port) {
if (!message.params || !message.params.hex) {
if (!validateRpcSignPsbtParams(message.params)) {
const errors = getRpcSignPsbtParamErrors(message.params);
chrome.tabs.sendMessage(
getTabIdFromPort(port),
makeRpcErrorResponse('signPsbt', {
id: message.id,
error: { code: RpcErrorCode.INVALID_PARAMS, message: 'Invalid parameters' },
error: {
code: RpcErrorCode.INVALID_PARAMS,
message:
'Invalid parameters: ' +
errors.map(e => `Error in path ${e.path}, ${e.message}.`).join(' '),
},
})
);
return;
}
const params: RequestParams = [
['requestId', message.id],
['hex', message.params.hex],
['publicKey', message.params.publicKey],
['network', message.params.network ?? 'mainnet'],
];
if (!validatePsbt(message.params.hex)) {
chrome.tabs.sendMessage(
getTabIdFromPort(port),
@@ -54,21 +57,28 @@ export async function rpcSignPsbt(message: SignPsbtRequest, port: chrome.runtime
return;
}
const requestParams: RequestParams = [
['requestId', message.id],
['hex', message.params.hex],
['publicKey', message.params.publicKey],
['network', message.params.network ?? 'mainnet'],
];
if (isDefined(message.params.account)) {
params.push(['accountIndex', message.params.account.toString()]);
requestParams.push(['accountIndex', message.params.account.toString()]);
}
if (isDefined(message.params.allowedSighash))
ensureArray(message.params.allowedSighash).forEach(hash =>
params.push(['allowedSighash', hash.toString()])
requestParams.push(['allowedSighash', hash.toString()])
);
if (isDefined(message.params.signAtIndex))
ensureArray(message.params.signAtIndex).forEach(index =>
params.push(['signAtIndex', index.toString()])
requestParams.push(['signAtIndex', index.toString()])
);
const { urlParams, tabId } = makeSearchParamsWithDefaults(port, params);
const { urlParams, tabId } = makeSearchParamsWithDefaults(port, requestParams);
const { id } = await triggerRequestWindowOpen(RouteUrls.RpcSignPsbt, urlParams);
listenForPopupClose({

View File

@@ -38,7 +38,9 @@ export enum WalletDefaultNetworkConfigurationIds {
export type DefaultNetworkConfigurations = keyof typeof WalletDefaultNetworkConfigurationIds;
export type NetworkModes = 'mainnet' | 'testnet';
export const networkModes = ['mainnet', 'testnet'] as const;
export type NetworkModes = (typeof networkModes)[number];
type BitcoinTestnetModes = 'testnet' | 'regtest' | 'signet';

View File

@@ -1,15 +1,36 @@
import { DefineRpcMethod, RpcRequest, RpcResponse } from '@btckit/types';
import { SignatureHash } from '@scure/btc-signer';
import * as yup from 'yup';
import { NetworkModes } from '@shared/constants';
import { networkModes } from '@shared/constants';
interface SignPsbtRequestParams {
publicKey: string;
allowedSighash?: SignatureHash[];
hex: string;
signAtIndex?: number | number[];
network?: NetworkModes;
account?: number;
const rpcSignPsbtValidator = yup.object().shape({
publicKey: yup.string().required(),
allowedSighash: yup.array().of(yup.number().required()),
hex: yup.string().required(),
signAtIndex: yup.number().integer().positive(),
network: yup.string().oneOf(networkModes),
account: yup.number().integer().positive(),
});
type SignPsbtRequestParams = yup.InferType<typeof rpcSignPsbtValidator>;
export function validateRpcSignPsbtParams(obj: unknown): obj is SignPsbtRequestParams {
try {
rpcSignPsbtValidator.validateSync(obj, { abortEarly: false });
return true;
} catch (e) {
return false;
}
}
export function getRpcSignPsbtParamErrors(obj: unknown) {
try {
rpcSignPsbtValidator.validateSync(obj, { abortEarly: false });
return [];
} catch (e) {
if (e instanceof yup.ValidationError) return e.inner;
return [];
}
}
export type SignPsbtRequest = RpcRequest<'signPsbt', SignPsbtRequestParams>;