This commit is contained in:
dexorder
2024-10-17 02:42:28 -04:00
commit 25def69c66
878 changed files with 112489 additions and 0 deletions

View File

@@ -0,0 +1,213 @@
const { ethers } = require('hardhat');
const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const TEST_MESSAGE = ethers.id('OpenZeppelin');
const WRONG_MESSAGE = ethers.id('Nope');
const NON_HASH_MESSAGE = '0xabcd';
async function fixture() {
const [signer] = await ethers.getSigners();
const mock = await ethers.deployContract('$ECDSA');
return { signer, mock };
}
describe('ECDSA', function () {
beforeEach(async function () {
Object.assign(this, await loadFixture(fixture));
});
describe('recover with invalid signature', function () {
it('with short signature', async function () {
await expect(this.mock.$recover(TEST_MESSAGE, '0x1234'))
.to.be.revertedWithCustomError(this.mock, 'ECDSAInvalidSignatureLength')
.withArgs(2);
});
it('with long signature', async function () {
await expect(
// eslint-disable-next-line max-len
this.mock.$recover(
TEST_MESSAGE,
'0x01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789',
),
)
.to.be.revertedWithCustomError(this.mock, 'ECDSAInvalidSignatureLength')
.withArgs(85);
});
});
describe('recover with valid signature', function () {
describe('using <signer>.sign', function () {
it('returns signer address with correct signature', async function () {
// Create the signature
const signature = await this.signer.signMessage(TEST_MESSAGE);
// Recover the signer address from the generated message and signature.
expect(await this.mock.$recover(ethers.hashMessage(TEST_MESSAGE), signature)).to.equal(this.signer);
});
it('returns signer address with correct signature for arbitrary length message', async function () {
// Create the signature
const signature = await this.signer.signMessage(NON_HASH_MESSAGE);
// Recover the signer address from the generated message and signature.
expect(await this.mock.$recover(ethers.hashMessage(NON_HASH_MESSAGE), signature)).to.equal(this.signer);
});
it('returns a different address', async function () {
const signature = await this.signer.signMessage(TEST_MESSAGE);
expect(await this.mock.$recover(WRONG_MESSAGE, signature)).to.not.be.equal(this.signer);
});
it('reverts with invalid signature', async function () {
// eslint-disable-next-line max-len
const signature =
'0x332ce75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feff48e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e01c';
await expect(this.mock.$recover(TEST_MESSAGE, signature)).to.be.revertedWithCustomError(
this.mock,
'ECDSAInvalidSignature',
);
});
});
describe('with v=27 signature', function () {
const signer = '0x2cc1166f6212628A0deEf2B33BEFB2187D35b86c';
// eslint-disable-next-line max-len
const signatureWithoutV =
'0x5d99b6f7f6d1f73d1a26497f2b1c89b24c0993913f86e9a2d02cd69887d9c94f3c880358579d811b21dd1b7fd9bb01c1d81d10e69f0384e675c32b39643be892';
it('works with correct v value', async function () {
const v = '0x1b'; // 27 = 1b.
const signature = ethers.concat([signatureWithoutV, v]);
expect(await this.mock.$recover(TEST_MESSAGE, signature)).to.equal(signer);
const { r, s, yParityAndS: vs } = ethers.Signature.from(signature);
expect(await this.mock.getFunction('$recover(bytes32,uint8,bytes32,bytes32)')(TEST_MESSAGE, v, r, s)).to.equal(
signer,
);
expect(await this.mock.getFunction('$recover(bytes32,bytes32,bytes32)')(TEST_MESSAGE, r, vs)).to.equal(signer);
});
it('rejects incorrect v value', async function () {
const v = '0x1c'; // 28 = 1c.
const signature = ethers.concat([signatureWithoutV, v]);
expect(await this.mock.$recover(TEST_MESSAGE, signature)).to.not.equal(signer);
const { r, s, yParityAndS: vs } = ethers.Signature.from(signature);
expect(
await this.mock.getFunction('$recover(bytes32,uint8,bytes32,bytes32)')(TEST_MESSAGE, v, r, s),
).to.not.equal(signer);
expect(await this.mock.getFunction('$recover(bytes32,bytes32,bytes32)')(TEST_MESSAGE, r, vs)).to.not.equal(
signer,
);
});
it('reverts wrong v values', async function () {
for (const v of ['0x00', '0x01']) {
const signature = ethers.concat([signatureWithoutV, v]);
await expect(this.mock.$recover(TEST_MESSAGE, signature)).to.be.revertedWithCustomError(
this.mock,
'ECDSAInvalidSignature',
);
const { r, s } = ethers.Signature.from(signature);
await expect(
this.mock.getFunction('$recover(bytes32,uint8,bytes32,bytes32)')(TEST_MESSAGE, v, r, s),
).to.be.revertedWithCustomError(this.mock, 'ECDSAInvalidSignature');
}
});
it('rejects short EIP2098 format', async function () {
const v = '0x1b'; // 27 = 1b.
const signature = ethers.concat([signatureWithoutV, v]);
const { compactSerialized } = ethers.Signature.from(signature);
await expect(this.mock.$recover(TEST_MESSAGE, compactSerialized))
.to.be.revertedWithCustomError(this.mock, 'ECDSAInvalidSignatureLength')
.withArgs(64);
});
});
describe('with v=28 signature', function () {
const signer = '0x1E318623aB09Fe6de3C9b8672098464Aeda9100E';
// eslint-disable-next-line max-len
const signatureWithoutV =
'0x331fe75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feff48e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e0';
it('works with correct v value', async function () {
const v = '0x1c'; // 28 = 1c.
const signature = ethers.concat([signatureWithoutV, v]);
expect(await this.mock.$recover(TEST_MESSAGE, signature)).to.equal(signer);
const { r, s, yParityAndS: vs } = ethers.Signature.from(signature);
expect(await this.mock.getFunction('$recover(bytes32,uint8,bytes32,bytes32)')(TEST_MESSAGE, v, r, s)).to.equal(
signer,
);
expect(await this.mock.getFunction('$recover(bytes32,bytes32,bytes32)')(TEST_MESSAGE, r, vs)).to.equal(signer);
});
it('rejects incorrect v value', async function () {
const v = '0x1b'; // 27 = 1b.
const signature = ethers.concat([signatureWithoutV, v]);
expect(await this.mock.$recover(TEST_MESSAGE, signature)).to.not.equal(signer);
const { r, s, yParityAndS: vs } = ethers.Signature.from(signature);
expect(
await this.mock.getFunction('$recover(bytes32,uint8,bytes32,bytes32)')(TEST_MESSAGE, v, r, s),
).to.not.equal(signer);
expect(await this.mock.getFunction('$recover(bytes32,bytes32,bytes32)')(TEST_MESSAGE, r, vs)).to.not.equal(
signer,
);
});
it('reverts invalid v values', async function () {
for (const v of ['0x00', '0x01']) {
const signature = ethers.concat([signatureWithoutV, v]);
await expect(this.mock.$recover(TEST_MESSAGE, signature)).to.be.revertedWithCustomError(
this.mock,
'ECDSAInvalidSignature',
);
const { r, s } = ethers.Signature.from(signature);
await expect(
this.mock.getFunction('$recover(bytes32,uint8,bytes32,bytes32)')(TEST_MESSAGE, v, r, s),
).to.be.revertedWithCustomError(this.mock, 'ECDSAInvalidSignature');
}
});
it('rejects short EIP2098 format', async function () {
const v = '0x1b'; // 28 = 1b.
const signature = ethers.concat([signatureWithoutV, v]);
const { compactSerialized } = ethers.Signature.from(signature);
await expect(this.mock.$recover(TEST_MESSAGE, compactSerialized))
.to.be.revertedWithCustomError(this.mock, 'ECDSAInvalidSignatureLength')
.withArgs(64);
});
});
it('reverts with high-s value signature', async function () {
const message = '0xb94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9';
// eslint-disable-next-line max-len
const highSSignature =
'0xe742ff452d41413616a5bf43fe15dd88294e983d3d36206c2712f39083d638bde0a0fc89be718fbc1033e1d30d78be1c68081562ed2e97af876f286f3453231d1b';
const r = ethers.dataSlice(highSSignature, 0, 32);
const s = ethers.dataSlice(highSSignature, 32, 64);
const v = ethers.dataSlice(highSSignature, 64, 65);
await expect(this.mock.$recover(message, highSSignature))
.to.be.revertedWithCustomError(this.mock, 'ECDSAInvalidSignatureS')
.withArgs(s);
await expect(this.mock.getFunction('$recover(bytes32,uint8,bytes32,bytes32)')(TEST_MESSAGE, v, r, s))
.to.be.revertedWithCustomError(this.mock, 'ECDSAInvalidSignatureS')
.withArgs(s);
expect(() => ethers.Signature.from(highSSignature)).to.throw('non-canonical s');
});
});
});

View File

@@ -0,0 +1,105 @@
const { ethers } = require('hardhat');
const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { getDomain, domainSeparator, hashTypedData } = require('../../helpers/eip712');
const { formatType } = require('../../helpers/eip712-types');
const LENGTHS = {
short: ['A Name', '1'],
long: ['A'.repeat(40), 'B'.repeat(40)],
};
const fixture = async () => {
const [from, to] = await ethers.getSigners();
const lengths = {};
for (const [shortOrLong, [name, version]] of Object.entries(LENGTHS)) {
lengths[shortOrLong] = { name, version };
lengths[shortOrLong].eip712 = await ethers.deployContract('$EIP712Verifier', [name, version]);
lengths[shortOrLong].domain = {
name,
version,
chainId: await ethers.provider.getNetwork().then(({ chainId }) => chainId),
verifyingContract: lengths[shortOrLong].eip712.target,
};
}
return { from, to, lengths };
};
describe('EIP712', function () {
for (const [shortOrLong, [name, version]] of Object.entries(LENGTHS)) {
describe(`with ${shortOrLong} name and version`, function () {
beforeEach('deploying', async function () {
Object.assign(this, await loadFixture(fixture));
Object.assign(this, this.lengths[shortOrLong]);
});
describe('domain separator', function () {
it('is internally available', async function () {
const expected = await domainSeparator(this.domain);
expect(await this.eip712.$_domainSeparatorV4()).to.equal(expected);
});
it("can be rebuilt using EIP-5267's eip712Domain", async function () {
const rebuildDomain = await getDomain(this.eip712);
expect(rebuildDomain).to.be.deep.equal(this.domain);
});
if (shortOrLong === 'short') {
// Long strings are in storage, and the proxy will not be properly initialized unless
// the upgradeable contract variant is used and the initializer is invoked.
it('adjusts when behind proxy', async function () {
const factory = await ethers.deployContract('$Clones');
const clone = await factory
.$clone(this.eip712)
.then(tx => tx.wait())
.then(receipt => receipt.logs.find(ev => ev.fragment.name == 'return$clone_address').args.instance)
.then(address => ethers.getContractAt('$EIP712Verifier', address));
const expectedDomain = { ...this.domain, verifyingContract: clone.target };
expect(await getDomain(clone)).to.be.deep.equal(expectedDomain);
const expectedSeparator = await domainSeparator(expectedDomain);
expect(await clone.$_domainSeparatorV4()).to.equal(expectedSeparator);
});
}
});
it('hash digest', async function () {
const structhash = ethers.hexlify(ethers.randomBytes(32));
expect(await this.eip712.$_hashTypedDataV4(structhash)).to.equal(hashTypedData(this.domain, structhash));
});
it('digest', async function () {
const types = {
Mail: formatType({
to: 'address',
contents: 'string',
}),
};
const message = {
to: this.to.address,
contents: 'very interesting',
};
const signature = await this.from.signTypedData(this.domain, types, message);
await expect(this.eip712.verify(signature, this.from.address, message.to, message.contents)).to.not.be.reverted;
});
it('name', async function () {
expect(await this.eip712.$_EIP712Name()).to.equal(name);
});
it('version', async function () {
expect(await this.eip712.$_EIP712Version()).to.equal(version);
});
});
}
});

View File

@@ -0,0 +1,173 @@
const { ethers } = require('hardhat');
const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { StandardMerkleTree } = require('@openzeppelin/merkle-tree');
const toElements = str => str.split('').map(e => [e]);
const hashPair = (a, b) => ethers.keccak256(Buffer.concat([a, b].sort(Buffer.compare)));
async function fixture() {
const mock = await ethers.deployContract('$MerkleProof');
return { mock };
}
describe('MerkleProof', function () {
beforeEach(async function () {
Object.assign(this, await loadFixture(fixture));
});
describe('verify', function () {
it('returns true for a valid Merkle proof', async function () {
const merkleTree = StandardMerkleTree.of(
toElements('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='),
['string'],
);
const root = merkleTree.root;
const hash = merkleTree.leafHash(['A']);
const proof = merkleTree.getProof(['A']);
expect(await this.mock.$verify(proof, root, hash)).to.be.true;
expect(await this.mock.$verifyCalldata(proof, root, hash)).to.be.true;
// For demonstration, it is also possible to create valid proofs for certain 64-byte values *not* in elements:
const noSuchLeaf = hashPair(
ethers.toBeArray(merkleTree.leafHash(['A'])),
ethers.toBeArray(merkleTree.leafHash(['B'])),
);
expect(await this.mock.$verify(proof.slice(1), root, noSuchLeaf)).to.be.true;
expect(await this.mock.$verifyCalldata(proof.slice(1), root, noSuchLeaf)).to.be.true;
});
it('returns false for an invalid Merkle proof', async function () {
const correctMerkleTree = StandardMerkleTree.of(toElements('abc'), ['string']);
const otherMerkleTree = StandardMerkleTree.of(toElements('def'), ['string']);
const root = correctMerkleTree.root;
const hash = correctMerkleTree.leafHash(['a']);
const proof = otherMerkleTree.getProof(['d']);
expect(await this.mock.$verify(proof, root, hash)).to.be.false;
expect(await this.mock.$verifyCalldata(proof, root, hash)).to.be.false;
});
it('returns false for a Merkle proof of invalid length', async function () {
const merkleTree = StandardMerkleTree.of(toElements('abc'), ['string']);
const root = merkleTree.root;
const hash = merkleTree.leafHash(['a']);
const proof = merkleTree.getProof(['a']);
const badProof = proof.slice(0, -1);
expect(await this.mock.$verify(badProof, root, hash)).to.be.false;
expect(await this.mock.$verifyCalldata(badProof, root, hash)).to.be.false;
});
});
describe('multiProofVerify', function () {
it('returns true for a valid Merkle multi proof', async function () {
const merkleTree = StandardMerkleTree.of(toElements('abcdef'), ['string']);
const root = merkleTree.root;
const { proof, proofFlags, leaves } = merkleTree.getMultiProof(toElements('bdf'));
const hashes = leaves.map(e => merkleTree.leafHash(e));
expect(await this.mock.$multiProofVerify(proof, proofFlags, root, hashes)).to.be.true;
expect(await this.mock.$multiProofVerifyCalldata(proof, proofFlags, root, hashes)).to.be.true;
});
it('returns false for an invalid Merkle multi proof', async function () {
const merkleTree = StandardMerkleTree.of(toElements('abcdef'), ['string']);
const otherMerkleTree = StandardMerkleTree.of(toElements('ghi'), ['string']);
const root = merkleTree.root;
const { proof, proofFlags, leaves } = otherMerkleTree.getMultiProof(toElements('ghi'));
const hashes = leaves.map(e => merkleTree.leafHash(e));
expect(await this.mock.$multiProofVerify(proof, proofFlags, root, hashes)).to.be.false;
expect(await this.mock.$multiProofVerifyCalldata(proof, proofFlags, root, hashes)).to.be.false;
});
it('revert with invalid multi proof #1', async function () {
const merkleTree = StandardMerkleTree.of(toElements('abcd'), ['string']);
const root = merkleTree.root;
const hashA = merkleTree.leafHash(['a']);
const hashB = merkleTree.leafHash(['b']);
const hashCD = hashPair(
ethers.toBeArray(merkleTree.leafHash(['c'])),
ethers.toBeArray(merkleTree.leafHash(['d'])),
);
const hashE = merkleTree.leafHash(['e']); // incorrect (not part of the tree)
const fill = ethers.randomBytes(32);
await expect(
this.mock.$multiProofVerify([hashB, fill, hashCD], [false, false, false], root, [hashA, hashE]),
).to.be.revertedWithCustomError(this.mock, 'MerkleProofInvalidMultiproof');
await expect(
this.mock.$multiProofVerifyCalldata([hashB, fill, hashCD], [false, false, false], root, [hashA, hashE]),
).to.be.revertedWithCustomError(this.mock, 'MerkleProofInvalidMultiproof');
});
it('revert with invalid multi proof #2', async function () {
const merkleTree = StandardMerkleTree.of(toElements('abcd'), ['string']);
const root = merkleTree.root;
const hashA = merkleTree.leafHash(['a']);
const hashB = merkleTree.leafHash(['b']);
const hashCD = hashPair(
ethers.toBeArray(merkleTree.leafHash(['c'])),
ethers.toBeArray(merkleTree.leafHash(['d'])),
);
const hashE = merkleTree.leafHash(['e']); // incorrect (not part of the tree)
const fill = ethers.randomBytes(32);
await expect(
this.mock.$multiProofVerify([hashB, fill, hashCD], [false, false, false, false], root, [hashE, hashA]),
).to.be.revertedWithPanic(0x32);
await expect(
this.mock.$multiProofVerifyCalldata([hashB, fill, hashCD], [false, false, false, false], root, [hashE, hashA]),
).to.be.revertedWithPanic(0x32);
});
it('limit case: works for tree containing a single leaf', async function () {
const merkleTree = StandardMerkleTree.of(toElements('a'), ['string']);
const root = merkleTree.root;
const { proof, proofFlags, leaves } = merkleTree.getMultiProof(toElements('a'));
const hashes = leaves.map(e => merkleTree.leafHash(e));
expect(await this.mock.$multiProofVerify(proof, proofFlags, root, hashes)).to.be.true;
expect(await this.mock.$multiProofVerifyCalldata(proof, proofFlags, root, hashes)).to.be.true;
});
it('limit case: can prove empty leaves', async function () {
const merkleTree = StandardMerkleTree.of(toElements('abcd'), ['string']);
const root = merkleTree.root;
expect(await this.mock.$multiProofVerify([root], [], root, [])).to.be.true;
expect(await this.mock.$multiProofVerifyCalldata([root], [], root, [])).to.be.true;
});
it('reverts processing manipulated proofs with a zero-value node at depth 1', async function () {
// Create a merkle tree that contains a zero leaf at depth 1
const leave = ethers.id('real leaf');
const root = hashPair(ethers.toBeArray(leave), Buffer.alloc(32, 0));
// Now we can pass any **malicious** fake leaves as valid!
const maliciousLeaves = ['malicious', 'leaves'].map(ethers.id).map(ethers.toBeArray).sort(Buffer.compare);
const maliciousProof = [leave, leave];
const maliciousProofFlags = [true, true, false];
await expect(
this.mock.$multiProofVerify(maliciousProof, maliciousProofFlags, root, maliciousLeaves),
).to.be.revertedWithCustomError(this.mock, 'MerkleProofInvalidMultiproof');
await expect(
this.mock.$multiProofVerifyCalldata(maliciousProof, maliciousProofFlags, root, maliciousLeaves),
).to.be.revertedWithCustomError(this.mock, 'MerkleProofInvalidMultiproof');
});
});
});

View File

@@ -0,0 +1,68 @@
const { ethers } = require('hardhat');
const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { domainSeparator, hashTypedData } = require('../../helpers/eip712');
async function fixture() {
const mock = await ethers.deployContract('$MessageHashUtils');
return { mock };
}
describe('MessageHashUtils', function () {
beforeEach(async function () {
Object.assign(this, await loadFixture(fixture));
});
describe('toEthSignedMessageHash', function () {
it('prefixes bytes32 data correctly', async function () {
const message = ethers.randomBytes(32);
const expectedHash = ethers.hashMessage(message);
expect(await this.mock.getFunction('$toEthSignedMessageHash(bytes32)')(message)).to.equal(expectedHash);
});
it('prefixes dynamic length data correctly', async function () {
const message = ethers.randomBytes(128);
const expectedHash = ethers.hashMessage(message);
expect(await this.mock.getFunction('$toEthSignedMessageHash(bytes)')(message)).to.equal(expectedHash);
});
it('version match for bytes32', async function () {
const message = ethers.randomBytes(32);
const fixed = await this.mock.getFunction('$toEthSignedMessageHash(bytes32)')(message);
const dynamic = await this.mock.getFunction('$toEthSignedMessageHash(bytes)')(message);
expect(fixed).to.equal(dynamic);
});
});
describe('toDataWithIntendedValidatorHash', function () {
it('returns the digest correctly', async function () {
const verifier = ethers.Wallet.createRandom().address;
const message = ethers.randomBytes(128);
const expectedHash = ethers.solidityPackedKeccak256(
['string', 'address', 'bytes'],
['\x19\x00', verifier, message],
);
expect(await this.mock.$toDataWithIntendedValidatorHash(verifier, message)).to.equal(expectedHash);
});
});
describe('toTypedDataHash', function () {
it('returns the digest correctly', async function () {
const domain = {
name: 'Test',
version: '1',
chainId: 1n,
verifyingContract: ethers.Wallet.createRandom().address,
};
const structhash = ethers.randomBytes(32);
const expectedHash = hashTypedData(domain, structhash);
expect(await this.mock.$toTypedDataHash(domainSeparator(domain), structhash)).to.equal(expectedHash);
});
});
});

View File

@@ -0,0 +1,61 @@
const { ethers } = require('hardhat');
const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const TEST_MESSAGE = ethers.id('OpenZeppelin');
const TEST_MESSAGE_HASH = ethers.hashMessage(TEST_MESSAGE);
const WRONG_MESSAGE = ethers.id('Nope');
const WRONG_MESSAGE_HASH = ethers.hashMessage(WRONG_MESSAGE);
async function fixture() {
const [signer, other] = await ethers.getSigners();
const mock = await ethers.deployContract('$SignatureChecker');
const wallet = await ethers.deployContract('ERC1271WalletMock', [signer]);
const malicious = await ethers.deployContract('ERC1271MaliciousMock');
const signature = await signer.signMessage(TEST_MESSAGE);
return { signer, other, mock, wallet, malicious, signature };
}
describe('SignatureChecker (ERC1271)', function () {
before('deploying', async function () {
Object.assign(this, await loadFixture(fixture));
});
describe('EOA account', function () {
it('with matching signer and signature', async function () {
expect(await this.mock.$isValidSignatureNow(this.signer, TEST_MESSAGE_HASH, this.signature)).to.be.true;
});
it('with invalid signer', async function () {
expect(await this.mock.$isValidSignatureNow(this.other, TEST_MESSAGE_HASH, this.signature)).to.be.false;
});
it('with invalid signature', async function () {
expect(await this.mock.$isValidSignatureNow(this.signer, WRONG_MESSAGE_HASH, this.signature)).to.be.false;
});
});
describe('ERC1271 wallet', function () {
for (const fn of ['isValidERC1271SignatureNow', 'isValidSignatureNow']) {
describe(fn, function () {
it('with matching signer and signature', async function () {
expect(await this.mock.getFunction(`$${fn}`)(this.wallet, TEST_MESSAGE_HASH, this.signature)).to.be.true;
});
it('with invalid signer', async function () {
expect(await this.mock.getFunction(`$${fn}`)(this.mock, TEST_MESSAGE_HASH, this.signature)).to.be.false;
});
it('with invalid signature', async function () {
expect(await this.mock.getFunction(`$${fn}`)(this.wallet, WRONG_MESSAGE_HASH, this.signature)).to.be.false;
});
it('with malicious wallet', async function () {
expect(await this.mock.getFunction(`$${fn}`)(this.malicious, TEST_MESSAGE_HASH, this.signature)).to.be.false;
});
});
}
});
});