Skip to content

Commit

Permalink
fix: solve wrong response for account.verifyMessage
Browse files Browse the repository at this point in the history
  • Loading branch information
PhilippeR26 committed Dec 13, 2023
1 parent a074bde commit 05f4095
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 24 deletions.
29 changes: 25 additions & 4 deletions __tests__/account.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
Contract,
DeclareDeployUDCResponse,
Provider,
SignatureVerifResult,
TransactionType,
cairo,
constants,
Expand Down Expand Up @@ -397,7 +398,7 @@ describe('deploy and test Wallet', () => {
expect(toBigInt(response.number as string).toString()).toStrictEqual('57');
});

test('sign and verify offchain message fail', async () => {
test('sign and verify EIP712 message fail', async () => {
const signature = await account.signMessage(typedDataExample);
const [r, s] = stark.formatSignature(signature);

Expand All @@ -408,12 +409,32 @@ describe('deploy and test Wallet', () => {

if (!signature2) return;

expect(await account.verifyMessage(typedDataExample, signature2)).toBe(false);
const verifMessageResponse: SignatureVerifResult = await account.verifyMessage(
typedDataExample,
signature2
);
expect(verifMessageResponse.isVerificationProcessed).toBe(true);
expect(verifMessageResponse.isSignatureValid).toBe(false);

const wrongAccount = new Account(provider, '0x037891', '0x026789', undefined, TEST_TX_VERSION); // non existing account
const verifMessageResponse2: SignatureVerifResult = await wrongAccount.verifyMessage(
typedDataExample,
signature2
);
expect(verifMessageResponse2.isVerificationProcessed).toBe(false);
expect(verifMessageResponse2.error?.message).toContain(
'Signature verification request is rejected by the network.'
);
});

test('sign and verify offchain message', async () => {
test('sign and verify message', async () => {
const signature = await account.signMessage(typedDataExample);
expect(await account.verifyMessage(typedDataExample, signature)).toBe(true);
const verifMessageResponse: SignatureVerifResult = await account.verifyMessage(
typedDataExample,
signature
);
expect(verifMessageResponse.isVerificationProcessed).toBe(true);
expect(verifMessageResponse.isSignatureValid).toBe(true);
});

describe('Contract interaction with Account', () => {
Expand Down
49 changes: 43 additions & 6 deletions src/account/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
Nonce,
ProviderOptions,
Signature,
SignatureVerifResult,
SimulateTransactionDetails,
SimulateTransactionResponse,
TransactionType,
Expand Down Expand Up @@ -573,23 +574,59 @@ export class Account extends Provider implements AccountInterface {
return getMessageHash(typedData, this.address);
}

public async verifyMessageHash(hash: BigNumberish, signature: Signature): Promise<boolean> {
public async verifyMessageHash(
hash: BigNumberish,
signature: Signature
): Promise<SignatureVerifResult> {
try {
await this.callContract({
const resp = await this.callContract({
contractAddress: this.address,
entrypoint: 'isValidSignature',
calldata: CallData.compile({
hash: toBigInt(hash).toString(),
signature: formatSignature(signature),
}),
});
return true;
} catch {
return false;
// console.log('verifySign=', resp);
if (BigInt(resp.result[0]) === 0n) {
// OpenZeppelin 0.8.0 invalid signature
return {
isVerificationProcessed: true,
isSignatureValid: false,
} as SignatureVerifResult;
}
// OpenZeppelin 0.8.0, ArgentX 0.3.0 & Braavos Cairo 0 valid signature
return {
isVerificationProcessed: true,
isSignatureValid: true,
} as SignatureVerifResult;
} catch (err) {
// console.log('verifySign error=', err);
if ((err as Error).message.includes('argent/invalid-signature')) {
// ArgentX 0.3.0 invalid signature
return {
isVerificationProcessed: true,
isSignatureValid: false,
} as SignatureVerifResult;
}
if ((err as Error).message.includes('is invalid, with respect to the public key')) {
// Braavos Cairo 0 invalid signature
return {
isVerificationProcessed: true,
isSignatureValid: false,
} as SignatureVerifResult;
}
return {
isVerificationProcessed: false,
error: new Error('Signature verification request is rejected by the network.'),
} as SignatureVerifResult;
}
}

public async verifyMessage(typedData: TypedData, signature: Signature): Promise<boolean> {
public async verifyMessage(
typedData: TypedData,
signature: Signature
): Promise<SignatureVerifResult> {
const hash = await this.hashMessage(typedData);
return this.verifyMessageHash(hash, signature);
}
Expand Down
11 changes: 9 additions & 2 deletions src/account/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
MultiDeployContractResponse,
Nonce,
Signature,
SignatureVerifResult,
SimulateTransactionDetails,
SimulateTransactionResponse,
TypedData,
Expand Down Expand Up @@ -356,7 +357,10 @@ export abstract class AccountInterface extends ProviderInterface {
* @returns true if the signature is valid, false otherwise
* @throws {Error} if the JSON object is not a valid JSON or the signature is not a valid signature
*/
public abstract verifyMessage(typedData: TypedData, signature: Signature): Promise<boolean>;
public abstract verifyMessage(
typedData: TypedData,
signature: Signature
): Promise<SignatureVerifResult>;

/**
* Verify a signature of a given hash
Expand All @@ -367,7 +371,10 @@ export abstract class AccountInterface extends ProviderInterface {
* @returns true if the signature is valid, false otherwise
* @throws {Error} if the signature is not a valid signature
*/
public abstract verifyMessageHash(hash: BigNumberish, signature: Signature): Promise<boolean>;
public abstract verifyMessageHash(
hash: BigNumberish,
signature: Signature
): Promise<SignatureVerifResult>;

/**
* Gets the nonce of the account with respect to a specific block
Expand Down
5 changes: 5 additions & 0 deletions src/types/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ import { CompiledContract, CompiledSierraCasm, ContractClass } from './contract'
export type WeierstrassSignatureType = weierstrass.SignatureType;
export type ArraySignatureType = string[];
export type Signature = ArraySignatureType | WeierstrassSignatureType;
export type SignatureVerifResult = {
isVerificationProcessed: boolean;
isSignatureValid?: boolean;
error?: Error;
};

export type BigNumberish = string | number | bigint;

Expand Down
18 changes: 6 additions & 12 deletions www/docs/guides/signature.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,17 +174,11 @@ const signature2 = await account.signMessage(typedDataValidate) as WeierstrassSi
On the receiver side, you receive the JSON, the signature, and the account address. To verify the message:

```typescript
const compiledAccount = json.parse(fs.readFileSync("./compiledContracts/Account_0_5_1.json").toString("ascii"));
const contractAccount = new Contract(compiledAccount.abi, accountAddress, provider);

const msgHash5 = typedData.getMessageHash(typedDataValidate, accountAddress);
// The call of isValidSignature will generate an error if not valid
let result5: boolean;
try {
await contractAccount.isValidSignature(msgHash5, [signature2.r, signature2.s]);
result5 = true;
} catch {
result5 = false;
const myAccount = new Account(provider, accountAddress, "0x0123"); // fake private key
const result = await myAccount.verifyMessage(typedMessage, signature);
if (result.isVerificationProcessed) {
console.log("Result (boolean) =", result.isSignatureValid);
} else {
console.log("verification failed :", result.error);
}
console.log("Result5 (boolean) =", result5);
```

0 comments on commit 05f4095

Please sign in to comment.