Skip to content

Commit a4b0d89

Browse files
StackOverflowExcept1onAmxxarr00
authored
MessageHashUtils: Add toDataWithIntendedValidatorHash(address, bytes32) (#5081)
Co-authored-by: Hadrien Croubois <hadrien.croubois@gmail.com> Co-authored-by: Arr00 <13561405+arr00@users.noreply.github.com>
1 parent a9b1f58 commit a4b0d89

File tree

4 files changed

+87
-5
lines changed

4 files changed

+87
-5
lines changed

.changeset/quiet-shrimps-kiss.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"openzeppelin-solidity": patch
3+
---
4+
5+
`MessageHashUtils`: Add `toDataWithIntendedValidatorHash(address, bytes32)`.

contracts/utils/cryptography/MessageHashUtils.sol

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,21 @@ library MessageHashUtils {
6363
return keccak256(abi.encodePacked(hex"19_00", validator, data));
6464
}
6565

66+
/**
67+
* @dev Variant of {toDataWithIntendedValidatorHash-address-bytes} optimized for cases where `data` is a bytes32.
68+
*/
69+
function toDataWithIntendedValidatorHash(
70+
address validator,
71+
bytes32 messageHash
72+
) internal pure returns (bytes32 digest) {
73+
assembly ("memory-safe") {
74+
mstore(0x00, hex"19_00")
75+
mstore(0x02, shl(96, validator))
76+
mstore(0x16, messageHash)
77+
digest := keccak256(0x00, 0x36)
78+
}
79+
}
80+
6681
/**
6782
* @dev Returns the keccak256 digest of an EIP-712 typed data (ERC-191 version `0x01`).
6883
*
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8.20;
4+
5+
import {Test} from "forge-std/Test.sol";
6+
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
7+
8+
contract MessageHashUtilsTest is Test {
9+
function testToDataWithIntendedValidatorHash(address validator, bytes memory data) external pure {
10+
assertEq(
11+
MessageHashUtils.toDataWithIntendedValidatorHash(validator, data),
12+
MessageHashUtils.toDataWithIntendedValidatorHash(_dirty(validator), data)
13+
);
14+
}
15+
16+
function testToDataWithIntendedValidatorHash(address validator, bytes32 messageHash) external pure {
17+
assertEq(
18+
MessageHashUtils.toDataWithIntendedValidatorHash(validator, messageHash),
19+
MessageHashUtils.toDataWithIntendedValidatorHash(_dirty(validator), messageHash)
20+
);
21+
22+
assertEq(
23+
MessageHashUtils.toDataWithIntendedValidatorHash(validator, messageHash),
24+
MessageHashUtils.toDataWithIntendedValidatorHash(validator, abi.encodePacked(messageHash))
25+
);
26+
}
27+
28+
function _dirty(address input) private pure returns (address output) {
29+
assembly ("memory-safe") {
30+
output := or(input, shl(160, not(0)))
31+
}
32+
}
33+
}

test/utils/cryptography/MessageHashUtils.test.js

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,16 @@ describe('MessageHashUtils', function () {
1919
const message = ethers.randomBytes(32);
2020
const expectedHash = ethers.hashMessage(message);
2121

22-
expect(await this.mock.getFunction('$toEthSignedMessageHash(bytes32)')(message)).to.equal(expectedHash);
22+
await expect(this.mock.getFunction('$toEthSignedMessageHash(bytes32)')(message)).to.eventually.equal(
23+
expectedHash,
24+
);
2325
});
2426

2527
it('prefixes dynamic length data correctly', async function () {
2628
const message = ethers.randomBytes(128);
2729
const expectedHash = ethers.hashMessage(message);
2830

29-
expect(await this.mock.getFunction('$toEthSignedMessageHash(bytes)')(message)).to.equal(expectedHash);
31+
await expect(this.mock.getFunction('$toEthSignedMessageHash(bytes)')(message)).to.eventually.equal(expectedHash);
3032
});
3133

3234
it('version match for bytes32', async function () {
@@ -39,15 +41,42 @@ describe('MessageHashUtils', function () {
3941
});
4042

4143
describe('toDataWithIntendedValidatorHash', function () {
42-
it('returns the digest correctly', async function () {
44+
it('returns the digest of `bytes32 messageHash` correctly', async function () {
45+
const verifier = ethers.Wallet.createRandom().address;
46+
const message = ethers.randomBytes(32);
47+
const expectedHash = ethers.solidityPackedKeccak256(
48+
['string', 'address', 'bytes32'],
49+
['\x19\x00', verifier, message],
50+
);
51+
52+
await expect(
53+
this.mock.getFunction('$toDataWithIntendedValidatorHash(address,bytes32)')(verifier, message),
54+
).to.eventually.equal(expectedHash);
55+
});
56+
57+
it('returns the digest of `bytes memory message` correctly', async function () {
4358
const verifier = ethers.Wallet.createRandom().address;
4459
const message = ethers.randomBytes(128);
4560
const expectedHash = ethers.solidityPackedKeccak256(
4661
['string', 'address', 'bytes'],
4762
['\x19\x00', verifier, message],
4863
);
4964

50-
expect(await this.mock.$toDataWithIntendedValidatorHash(verifier, message)).to.equal(expectedHash);
65+
await expect(
66+
this.mock.getFunction('$toDataWithIntendedValidatorHash(address,bytes)')(verifier, message),
67+
).to.eventually.equal(expectedHash);
68+
});
69+
70+
it('version match for bytes32', async function () {
71+
const verifier = ethers.Wallet.createRandom().address;
72+
const message = ethers.randomBytes(32);
73+
const fixed = await this.mock.getFunction('$toDataWithIntendedValidatorHash(address,bytes)')(verifier, message);
74+
const dynamic = await this.mock.getFunction('$toDataWithIntendedValidatorHash(address,bytes32)')(
75+
verifier,
76+
message,
77+
);
78+
79+
expect(fixed).to.equal(dynamic);
5180
});
5281
});
5382

@@ -62,7 +91,7 @@ describe('MessageHashUtils', function () {
6291
const structhash = ethers.randomBytes(32);
6392
const expectedHash = hashTypedData(domain, structhash);
6493

65-
expect(await this.mock.$toTypedDataHash(domainSeparator(domain), structhash)).to.equal(expectedHash);
94+
await expect(this.mock.$toTypedDataHash(domainSeparator(domain), structhash)).to.eventually.equal(expectedHash);
6695
});
6796
});
6897
});

0 commit comments

Comments
 (0)