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>
This commit is contained in:
committed by
GitHub
parent
a9b1f58b00
commit
a4b0d89900
5
.changeset/quiet-shrimps-kiss.md
Normal file
5
.changeset/quiet-shrimps-kiss.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"openzeppelin-solidity": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
`MessageHashUtils`: Add `toDataWithIntendedValidatorHash(address, bytes32)`.
|
||||||
@ -63,6 +63,21 @@ library MessageHashUtils {
|
|||||||
return keccak256(abi.encodePacked(hex"19_00", validator, data));
|
return keccak256(abi.encodePacked(hex"19_00", validator, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Variant of {toDataWithIntendedValidatorHash-address-bytes} optimized for cases where `data` is a bytes32.
|
||||||
|
*/
|
||||||
|
function toDataWithIntendedValidatorHash(
|
||||||
|
address validator,
|
||||||
|
bytes32 messageHash
|
||||||
|
) internal pure returns (bytes32 digest) {
|
||||||
|
assembly ("memory-safe") {
|
||||||
|
mstore(0x00, hex"19_00")
|
||||||
|
mstore(0x02, shl(96, validator))
|
||||||
|
mstore(0x16, messageHash)
|
||||||
|
digest := keccak256(0x00, 0x36)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev Returns the keccak256 digest of an EIP-712 typed data (ERC-191 version `0x01`).
|
* @dev Returns the keccak256 digest of an EIP-712 typed data (ERC-191 version `0x01`).
|
||||||
*
|
*
|
||||||
|
|||||||
33
test/utils/cryptography/MessageHashUtils.t.sol
Normal file
33
test/utils/cryptography/MessageHashUtils.t.sol
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
pragma solidity ^0.8.20;
|
||||||
|
|
||||||
|
import {Test} from "forge-std/Test.sol";
|
||||||
|
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
|
||||||
|
|
||||||
|
contract MessageHashUtilsTest is Test {
|
||||||
|
function testToDataWithIntendedValidatorHash(address validator, bytes memory data) external pure {
|
||||||
|
assertEq(
|
||||||
|
MessageHashUtils.toDataWithIntendedValidatorHash(validator, data),
|
||||||
|
MessageHashUtils.toDataWithIntendedValidatorHash(_dirty(validator), data)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testToDataWithIntendedValidatorHash(address validator, bytes32 messageHash) external pure {
|
||||||
|
assertEq(
|
||||||
|
MessageHashUtils.toDataWithIntendedValidatorHash(validator, messageHash),
|
||||||
|
MessageHashUtils.toDataWithIntendedValidatorHash(_dirty(validator), messageHash)
|
||||||
|
);
|
||||||
|
|
||||||
|
assertEq(
|
||||||
|
MessageHashUtils.toDataWithIntendedValidatorHash(validator, messageHash),
|
||||||
|
MessageHashUtils.toDataWithIntendedValidatorHash(validator, abi.encodePacked(messageHash))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _dirty(address input) private pure returns (address output) {
|
||||||
|
assembly ("memory-safe") {
|
||||||
|
output := or(input, shl(160, not(0)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -19,14 +19,16 @@ describe('MessageHashUtils', function () {
|
|||||||
const message = ethers.randomBytes(32);
|
const message = ethers.randomBytes(32);
|
||||||
const expectedHash = ethers.hashMessage(message);
|
const expectedHash = ethers.hashMessage(message);
|
||||||
|
|
||||||
expect(await this.mock.getFunction('$toEthSignedMessageHash(bytes32)')(message)).to.equal(expectedHash);
|
await expect(this.mock.getFunction('$toEthSignedMessageHash(bytes32)')(message)).to.eventually.equal(
|
||||||
|
expectedHash,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('prefixes dynamic length data correctly', async function () {
|
it('prefixes dynamic length data correctly', async function () {
|
||||||
const message = ethers.randomBytes(128);
|
const message = ethers.randomBytes(128);
|
||||||
const expectedHash = ethers.hashMessage(message);
|
const expectedHash = ethers.hashMessage(message);
|
||||||
|
|
||||||
expect(await this.mock.getFunction('$toEthSignedMessageHash(bytes)')(message)).to.equal(expectedHash);
|
await expect(this.mock.getFunction('$toEthSignedMessageHash(bytes)')(message)).to.eventually.equal(expectedHash);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('version match for bytes32', async function () {
|
it('version match for bytes32', async function () {
|
||||||
@ -39,7 +41,20 @@ describe('MessageHashUtils', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('toDataWithIntendedValidatorHash', function () {
|
describe('toDataWithIntendedValidatorHash', function () {
|
||||||
it('returns the digest correctly', async function () {
|
it('returns the digest of `bytes32 messageHash` correctly', async function () {
|
||||||
|
const verifier = ethers.Wallet.createRandom().address;
|
||||||
|
const message = ethers.randomBytes(32);
|
||||||
|
const expectedHash = ethers.solidityPackedKeccak256(
|
||||||
|
['string', 'address', 'bytes32'],
|
||||||
|
['\x19\x00', verifier, message],
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
this.mock.getFunction('$toDataWithIntendedValidatorHash(address,bytes32)')(verifier, message),
|
||||||
|
).to.eventually.equal(expectedHash);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns the digest of `bytes memory message` correctly', async function () {
|
||||||
const verifier = ethers.Wallet.createRandom().address;
|
const verifier = ethers.Wallet.createRandom().address;
|
||||||
const message = ethers.randomBytes(128);
|
const message = ethers.randomBytes(128);
|
||||||
const expectedHash = ethers.solidityPackedKeccak256(
|
const expectedHash = ethers.solidityPackedKeccak256(
|
||||||
@ -47,7 +62,21 @@ describe('MessageHashUtils', function () {
|
|||||||
['\x19\x00', verifier, message],
|
['\x19\x00', verifier, message],
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(await this.mock.$toDataWithIntendedValidatorHash(verifier, message)).to.equal(expectedHash);
|
await expect(
|
||||||
|
this.mock.getFunction('$toDataWithIntendedValidatorHash(address,bytes)')(verifier, message),
|
||||||
|
).to.eventually.equal(expectedHash);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('version match for bytes32', async function () {
|
||||||
|
const verifier = ethers.Wallet.createRandom().address;
|
||||||
|
const message = ethers.randomBytes(32);
|
||||||
|
const fixed = await this.mock.getFunction('$toDataWithIntendedValidatorHash(address,bytes)')(verifier, message);
|
||||||
|
const dynamic = await this.mock.getFunction('$toDataWithIntendedValidatorHash(address,bytes32)')(
|
||||||
|
verifier,
|
||||||
|
message,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(fixed).to.equal(dynamic);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -62,7 +91,7 @@ describe('MessageHashUtils', function () {
|
|||||||
const structhash = ethers.randomBytes(32);
|
const structhash = ethers.randomBytes(32);
|
||||||
const expectedHash = hashTypedData(domain, structhash);
|
const expectedHash = hashTypedData(domain, structhash);
|
||||||
|
|
||||||
expect(await this.mock.$toTypedDataHash(domainSeparator(domain), structhash)).to.equal(expectedHash);
|
await expect(this.mock.$toTypedDataHash(domainSeparator(domain), structhash)).to.eventually.equal(expectedHash);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user