Skip to content

Commit

Permalink
refactor: refactor verify sign message api (#332)
Browse files Browse the repository at this point in the history
  • Loading branch information
stanleyyconsensys authored Aug 26, 2024
1 parent b0d6151 commit 75237eb
Show file tree
Hide file tree
Showing 9 changed files with 198 additions and 269 deletions.
1 change: 1 addition & 0 deletions packages/starknet-snap/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"dependencies": {
"@metamask/key-tree": "9.0.0",
"@metamask/snaps-sdk": "^4.0.0",
"@metamask/utils": "^9.1.0",
"async-mutex": "^0.3.2",
"ethereum-unit-converter": "^0.0.17",
"ethers": "^5.5.1",
Expand Down
14 changes: 9 additions & 5 deletions packages/starknet-snap/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,14 @@ import type {
DisplayPrivateKeyParams,
SignMessageParams,
SignTransactionParams,
VerifySignatureParams,
} from './rpcs';
import {
displayPrivateKey,
signMessage,
signTransaction,
verifySignature,
} from './rpcs';
import { displayPrivateKey, signMessage, signTransaction } from './rpcs';
import { sendTransaction } from './sendTransaction';
import { signDeclareTransaction } from './signDeclareTransaction';
import { signDeployAccountTransaction } from './signDeployAccountTransaction';
Expand Down Expand Up @@ -76,7 +82,6 @@ import {
getCorrectContractAddress,
getKeysFromAddressIndex,
} from './utils/starknetUtils';
import { verifySignedMessage } from './verifySignedMessage';

declare const snap;

Expand Down Expand Up @@ -199,9 +204,8 @@ export const onRpcRequest: OnRpcRequestHandler = async ({ request }) => {
);

case 'starkNet_verifySignedMessage':
apiParams.keyDeriver = await getAddressKeyDeriver(snap);
return await verifySignedMessage(
apiParams as unknown as ApiParamsWithKeyDeriver,
return await verifySignature.execute(
apiParams as unknown as VerifySignatureParams,
);

case 'starkNet_getErc20TokenBalance':
Expand Down
1 change: 1 addition & 0 deletions packages/starknet-snap/src/rpcs/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './signMessage';
export * from './displayPrivateKey';
export * from './signTransaction';
export * from './verify-signature';
75 changes: 75 additions & 0 deletions packages/starknet-snap/src/rpcs/verify-signature.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { InvalidParamsError } from '@metamask/snaps-sdk';
import { constants } from 'starknet';

import typedDataExample from '../__tests__/fixture/typedDataExample.json';
import type { SnapState } from '../types/snapState';
import { STARKNET_SEPOLIA_TESTNET_NETWORK } from '../utils/constants';
import * as starknetUtils from '../utils/starknetUtils';
import { mockAccount, prepareMockAccount } from './__tests__/helper';
import { verifySignature } from './verify-signature';
import type { VerifySignatureParams } from './verify-signature';

jest.mock('../utils/snap');
jest.mock('../utils/logger');

describe('verifySignature', () => {
const state: SnapState = {
accContracts: [],
erc20Tokens: [],
networks: [STARKNET_SEPOLIA_TESTNET_NETWORK],
transactions: [],
};

it('returns true if the signature is correct', async () => {
const account = await mockAccount(constants.StarknetChainId.SN_SEPOLIA);
prepareMockAccount(account, state);

const signature = await starknetUtils.signMessage(
account.privateKey,
typedDataExample,
account.address,
);

const request = {
chainId: constants.StarknetChainId.SN_SEPOLIA,
address: account.address,
typedDataMessage: typedDataExample,
signature,
};

const result = await verifySignature.execute(request);

expect(result).toBe(true);
});

it('returns false if the signature is incorrect', async () => {
const account = await mockAccount(constants.StarknetChainId.SN_SEPOLIA);
const invalidSignatueAccount = await mockAccount(
constants.StarknetChainId.SN_MAIN,
);
prepareMockAccount(account, state);

const invalidSignatue = await starknetUtils.signMessage(
invalidSignatueAccount.privateKey,
typedDataExample,
invalidSignatueAccount.address,
);

const request = {
chainId: constants.StarknetChainId.SN_SEPOLIA,
address: account.address,
typedDataMessage: typedDataExample,
signature: invalidSignatue,
};

const result = await verifySignature.execute(request);

expect(result).toBe(false);
});

it('throws `InvalidParamsError` when request parameter is not correct', async () => {
await expect(
verifySignature.execute({} as unknown as VerifySignatureParams),
).rejects.toThrow(InvalidParamsError);
});
});
73 changes: 73 additions & 0 deletions packages/starknet-snap/src/rpcs/verify-signature.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { HexStruct } from '@metamask/utils';
import type { Infer } from 'superstruct';
import { object, assign, boolean, array } from 'superstruct';

import {
AddressStruct,
TypeDataStruct,
BaseRequestStruct,
AccountRpcController,
} from '../utils';
import { verifyTypedDataMessageSignature } from '../utils/starknetUtils';

export const VerifySignatureRequestStruct = assign(
object({
address: AddressStruct,
typedDataMessage: TypeDataStruct,
signature: array(HexStruct),
}),
BaseRequestStruct,
);

export const VerifySignatureResponseStruct = boolean();

export type VerifySignatureParams = Infer<typeof VerifySignatureRequestStruct>;

export type VerifySignatureResponse = Infer<
typeof VerifySignatureResponseStruct
>;

/**
* The RPC handler to verify a signature.
*/
export class VerifySignatureRpc extends AccountRpcController<
VerifySignatureParams,
VerifySignatureResponse
> {
protected requestStruct = VerifySignatureRequestStruct;

protected responseStruct = VerifySignatureResponseStruct;

/**
* Execute the verify signature request handler.
*
* @param params - The parameters of the request.
* @param params.address - The address of the signer.
* @param params.typedDataMessage - The Starknet type data message to sign.
* @param params.signature - The signed signature in string.
* @param params.chainId - The chain id of the network.
* @returns the verification result in boolean.
*/
async execute(
params: VerifySignatureParams,
): Promise<VerifySignatureResponse> {
return super.execute(params);
}

protected async handleRequest(
params: VerifySignatureParams,
): Promise<VerifySignatureResponse> {
const { typedDataMessage, address, signature } = params;

return verifyTypedDataMessageSignature(
address,
this.account.privateKey,
typedDataMessage,
signature,
);
}
}

export const verifySignature = new VerifySignatureRpc({
showInvalidAccountAlert: false,
});
30 changes: 11 additions & 19 deletions packages/starknet-snap/src/utils/starknetUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import type {
ProviderInterface,
GetTransactionReceiptResponse,
BigNumberish,
ArraySignatureType,
} from 'starknet';
import {
ec,
Expand Down Expand Up @@ -643,28 +644,19 @@ export function getFullPublicKeyPairFromPrivateKey(privateKey: string) {
);
}

export const getTypedDataMessageSignature = (
privateKey: string,
typedDataMessage: TypedData,
signerUserAddress: string,
) => {
const msgHash = typedData.getMessageHash(typedDataMessage, signerUserAddress);
return ec.starkCurve.sign(msgHash, privateKey);
};

export const getSignatureBySignatureString = (signatureStr: string) => {
return ec.starkCurve.Signature.fromDER(signatureStr);
};

export const verifyTypedDataMessageSignature = (
fullPublicKey: string,
address: string,
privateKey: string,
typedDataMessage: TypedData,
signerUserAddress: numUtils.BigNumberish,
signatureStr: string,
signature: ArraySignatureType,
) => {
const signature = getSignatureBySignatureString(signatureStr);
const msgHash = typedData.getMessageHash(typedDataMessage, signerUserAddress);
return ec.starkCurve.verify(signature, msgHash, fullPublicKey);
const fullPublicKey = getFullPublicKeyPairFromPrivateKey(privateKey);
const signatureType = new ec.starkCurve.Signature(
BigInt(signature[0]),
BigInt(signature[1]),
);
const msgHash = typedData.getMessageHash(typedDataMessage, address);
return ec.starkCurve.verify(signatureType, msgHash, fullPublicKey);
};

export const getNextAddressIndex = (
Expand Down
81 changes: 0 additions & 81 deletions packages/starknet-snap/src/verifySignedMessage.ts

This file was deleted.

Loading

0 comments on commit 75237eb

Please sign in to comment.