Added unit tests for LibEIP712

This commit is contained in:
Alex Towle 2019-07-29 18:26:55 -07:00
parent 065f46a020
commit f9292a8fb8
10 changed files with 191 additions and 3 deletions

View File

@ -42,6 +42,7 @@ export const constants = {
NUM_ERC1155_FUNGIBLE_TOKENS_MINT: 4,
NUM_ERC1155_NONFUNGIBLE_TOKENS_MINT: 4,
NULL_ADDRESS: '0x0000000000000000000000000000000000000000',
NULL_BYTES32: '0x0000000000000000000000000000000000000000000000000000000000000000',
UNLIMITED_ALLOWANCE_IN_BASE_UNITS: new BigNumber(2).pow(256).minus(1),
TESTRPC_PRIVATE_KEYS: _.map(TESTRPC_PRIVATE_KEYS_STRINGS, privateKeyString => ethUtil.toBuffer(privateKeyString)),
INITIAL_ERC20_BALANCE: Web3Wrapper.toBaseUnitAmount(new BigNumber(10000), 18),

View File

@ -36,6 +36,7 @@
"test/TestLibAddress.sol",
"test/TestLibAddressArray.sol",
"test/TestLibBytes.sol",
"test/TestLibEIP712.sol",
"test/TestOwnable.sol",
"test/TestReentrancyGuard.sol",
"test/TestSafeMath.sol"

View File

@ -1,6 +1,6 @@
/*
Copyright 2018 ZeroEx Intl.
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -0,0 +1,52 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
import "../src/LibEIP712.sol";
contract TestLibEIP712 is
LibEIP712
{
function externalHashEIP712DomainSeperator(
string calldata name,
string calldata version,
uint256 chainid,
address verifyingcontractaddress
)
external
pure
returns (bytes32)
{
return _hashEIP712Domain(
name,
version,
chainid,
verifyingcontractaddress
);
}
function externalHashEIP712Message(bytes32 eip712DomainHash, bytes32 hashStruct)
external
pure
returns (bytes32)
{
return _hashEIP712Message(eip712DomainHash, hashStruct);
}
}

View File

@ -34,7 +34,7 @@
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
},
"config": {
"abis": "./generated-artifacts/@(IOwnable|LibAddress|LibBytes|LibEIP1271|LibEIP712|Ownable|ReentrancyGuard|SafeMath|TestConstants|TestLibAddress|TestLibAddressArray|TestLibBytes|TestOwnable|TestReentrancyGuard|TestSafeMath).json",
"abis": "./generated-artifacts/@(IOwnable|LibAddress|LibBytes|LibEIP1271|LibEIP712|Ownable|ReentrancyGuard|SafeMath|TestConstants|TestLibAddress|TestLibAddressArray|TestLibBytes|TestLibEIP712|TestOwnable|TestReentrancyGuard|TestSafeMath).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {

View File

@ -17,6 +17,7 @@ import * as TestConstants from '../generated-artifacts/TestConstants.json';
import * as TestLibAddress from '../generated-artifacts/TestLibAddress.json';
import * as TestLibAddressArray from '../generated-artifacts/TestLibAddressArray.json';
import * as TestLibBytes from '../generated-artifacts/TestLibBytes.json';
import * as TestLibEIP712 from '../generated-artifacts/TestLibEIP712.json';
import * as TestOwnable from '../generated-artifacts/TestOwnable.json';
import * as TestReentrancyGuard from '../generated-artifacts/TestReentrancyGuard.json';
import * as TestSafeMath from '../generated-artifacts/TestSafeMath.json';
@ -33,6 +34,7 @@ export const artifacts = {
TestLibAddress: TestLibAddress as ContractArtifact,
TestLibAddressArray: TestLibAddressArray as ContractArtifact,
TestLibBytes: TestLibBytes as ContractArtifact,
TestLibEIP712: TestLibEIP712 as ContractArtifact,
TestOwnable: TestOwnable as ContractArtifact,
TestReentrancyGuard: TestReentrancyGuard as ContractArtifact,
TestSafeMath: TestSafeMath as ContractArtifact,

View File

@ -15,6 +15,7 @@ export * from '../generated-wrappers/test_constants';
export * from '../generated-wrappers/test_lib_address';
export * from '../generated-wrappers/test_lib_address_array';
export * from '../generated-wrappers/test_lib_bytes';
export * from '../generated-wrappers/test_lib_e_i_p712';
export * from '../generated-wrappers/test_ownable';
export * from '../generated-wrappers/test_reentrancy_guard';
export * from '../generated-wrappers/test_safe_math';

View File

@ -0,0 +1,107 @@
import { chaiSetup, constants, hexConcat, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { BigNumber, signTypedDataUtils } from '@0x/utils';
import * as chai from 'chai';
import * as ethUtil from 'ethereumjs-util';
import * as _ from 'lodash';
import { artifacts, TestLibEIP712Contract } from '../src';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
/**
* Tests a specific instance of EIP712 domain hashing.
* @param lib The LibEIP712 contract to call.
* @param name The name of the domain.
* @param version The version of the domain.
* @param chainId The chain id of the domain.
* @param verifyingContractAddress The verifying contract address of the domain.
*/
async function testHashEIP712DomainAsync(
lib: TestLibEIP712Contract,
name: string,
version: string,
chainId: number,
verifyingContractAddress: string,
): Promise<void> {
const expectedHash = signTypedDataUtils.generateDomainHash({
name,
version,
chainId,
verifyingContractAddress,
});
const actualHash = await lib.externalHashEIP712DomainSeperator.callAsync(
name,
version,
new BigNumber(chainId),
verifyingContractAddress,
);
expect(actualHash).to.be.eq(hexConcat(expectedHash));
}
/**
* Tests a specific instance of EIP712 message hashing.
* @param lib The LibEIP712 contract to call.
* @param domainHash The hash of the EIP712 domain of this instance.
* @param hashStruct The hash of the struct of this instance.
*/
async function testHashEIP712MessageAsync(
lib: TestLibEIP712Contract,
domainHash: string,
hashStruct: string,
): Promise<void> {
const input = '0x1901'.concat(domainHash.slice(2, domainHash.length).concat(hashStruct.slice(2, hashStruct.length)));
const expectedHash = '0x'.concat(ethUtil.sha3(input).toString('hex'));
const actualHash = await lib.externalHashEIP712Message.callAsync(
domainHash,
hashStruct,
);
expect(actualHash).to.be.eq(expectedHash);
}
describe('LibEIP712', () => {
let lib: TestLibEIP712Contract;
before(async () => {
await blockchainLifecycle.startAsync();
// Deploy SafeMath
lib = await TestLibEIP712Contract.deployFrom0xArtifactAsync(
artifacts.TestLibEIP712,
provider,
txDefaults,
);
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
describe('_hashEIP712Domain', async () => {
it('should correctly hash empty input', async () => {
await testHashEIP712DomainAsync(
lib,
'',
'',
0,
constants.NULL_ADDRESS,
);
});
});
describe('_hashEIP712Message', () => {
it('should correctly hash empty input', async () => {
/*
const expectedHash = hashEIP712Message(constants.NULL_BYTES32, constants.NULL_BYTES32);
const actualHash = await lib.externalHashEIP712Message.callAsync(constants.NULL_BYTES32, constants.NULL_BYTES32);
expect(actualHash).to.be.eq(expectedHash);
*/
await testHashEIP712MessageAsync(
lib,
constants.NULL_BYTES32,
constants.NULL_BYTES32,
);
});
});
});

View File

@ -15,6 +15,7 @@
"generated-artifacts/TestLibAddress.json",
"generated-artifacts/TestLibAddressArray.json",
"generated-artifacts/TestLibBytes.json",
"generated-artifacts/TestLibEIP712.json",
"generated-artifacts/TestOwnable.json",
"generated-artifacts/TestReentrancyGuard.json",
"generated-artifacts/TestSafeMath.json"

View File

@ -1,10 +1,11 @@
import { EIP712Object, EIP712ObjectValue, EIP712TypedData, EIP712Types } from '@0x/types';
import { EIP712DomainWithDefaultSchema, EIP712Object, EIP712ObjectValue, EIP712TypedData, EIP712Types } from '@0x/types';
import * as ethUtil from 'ethereumjs-util';
import * as ethers from 'ethers';
import * as _ from 'lodash';
import { BigNumber } from './configured_bignumber';
export const signTypedDataUtils = {
/**
* Generates the EIP712 Typed Data hash for signing
@ -20,6 +21,28 @@ export const signTypedDataUtils = {
]),
);
},
/**
* Generates the hash of a EIP712 Domain with the default schema
* @param domain An EIP712 domain with the default schema containing a name, version, chain id,
* and verifying address.
* @return A buffer that contains the hash of the domain.
*/
generateDomainHash(domain: EIP712Object): Buffer {
return signTypedDataUtils._structHash(
'EIP712Domain',
domain,
// HACK(jalextowle): When we consolidate our testing packages into test-utils, we can use a constant
// to eliminate code duplication. At the moment, there isn't a good way to do that because of cyclic-dependencies.
{
EIP712Domain: [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' },
{ name: 'chainId', type: 'uint256' },
{ name: 'verifyingContractAddress', type: 'address' },
]
} as EIP712Types,
);
},
_findDependencies(primaryType: string, types: EIP712Types, found: string[] = []): string[] {
if (found.includes(primaryType) || types[primaryType] === undefined) {
return found;