Expose eth_signTypedData functionality for order signing

This commit is contained in:
Jacob Evans
2018-10-01 20:37:13 +10:00
parent 119f8c9449
commit adcfaa2e80
10 changed files with 245 additions and 531 deletions

View File

@@ -5,11 +5,11 @@ import { crypto } from './crypto';
import { EIP712Schema, EIP712Types } from './types';
const EIP191_PREFIX = '\x19\x01';
const EIP712_DOMAIN_NAME = '0x Protocol';
const EIP712_DOMAIN_VERSION = '2';
const EIP712_VALUE_LENGTH = 32;
export const EIP712_DOMAIN_NAME = '0x Protocol';
export const EIP712_DOMAIN_VERSION = '2';
const EIP712_DOMAIN_SCHEMA: EIP712Schema = {
export const EIP712_DOMAIN_SCHEMA: EIP712Schema = {
name: 'EIP712Domain',
parameters: [
{ name: 'name', type: EIP712Types.String },

View File

@@ -8,7 +8,7 @@ import { EIP712Schema, EIP712Types } from './types';
const INVALID_TAKER_FORMAT = 'instance.takerAddress is not of a type(s) string';
const EIP712_ORDER_SCHEMA: EIP712Schema = {
export const EIP712_ORDER_SCHEMA: EIP712Schema = {
name: 'Order',
parameters: [
{ name: 'makerAddress', type: EIP712Types.Address },

View File

@@ -1,5 +1,5 @@
import { schemas } from '@0xproject/json-schemas';
import { ECSignature, SignatureType, SignerType, ValidatorSignature } from '@0xproject/types';
import { ECSignature, Order, SignatureType, SignerType, ValidatorSignature } from '@0xproject/types';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import { Provider } from 'ethereum-types';
import * as ethUtil from 'ethereumjs-util';
@@ -7,9 +7,11 @@ import * as _ from 'lodash';
import { artifacts } from './artifacts';
import { assert } from './assert';
import { EIP712_DOMAIN_NAME, EIP712_DOMAIN_SCHEMA, EIP712_DOMAIN_VERSION } from './eip712_utils';
import { ExchangeContract } from './generated_contract_wrappers/exchange';
import { IValidatorContract } from './generated_contract_wrappers/i_validator';
import { IWalletContract } from './generated_contract_wrappers/i_wallet';
import { EIP712_ORDER_SCHEMA } from './order_hash';
import { OrderError } from './types';
import { utils } from './utils';
@@ -191,6 +193,52 @@ export const signatureUtils = {
return false;
}
},
/**
* Signs an order using `eth_signTypedData` and returns it's elliptic curve signature and signature type.
* This method currently supports Ganache.
* @param order The Order to sign.
* @param signerAddress The hex encoded Ethereum address you wish to sign it with. This address
* must be available via the Provider supplied to 0x.js.
* @return A hex encoded string containing the Elliptic curve signature generated by signing the orderHash and the Signature Type.
*/
async ecSignOrderAsync(provider: Provider, order: Order, signerAddress: string): Promise<string> {
assert.isWeb3Provider('provider', provider);
assert.isETHAddressHex('signerAddress', signerAddress);
const web3Wrapper = new Web3Wrapper(provider);
await assert.isSenderAddressAsync('signerAddress', signerAddress, web3Wrapper);
const normalizedSignerAddress = signerAddress.toLowerCase();
const typedData = {
types: {
EIP712Domain: EIP712_DOMAIN_SCHEMA.parameters,
Order: EIP712_ORDER_SCHEMA.parameters,
},
domain: {
name: EIP712_DOMAIN_NAME,
version: EIP712_DOMAIN_VERSION,
verifyingContract: order.exchangeAddress,
},
message: {
...order,
salt: order.salt.toString(),
makerFee: order.makerFee.toString(),
takerFee: order.takerFee.toString(),
makerAssetAmount: order.makerAssetAmount.toString(),
takerAssetAmount: order.takerAssetAmount.toString(),
expirationTimeSeconds: order.expirationTimeSeconds.toString(),
},
primaryType: 'Order',
};
const signature = await web3Wrapper.signTypedDataAsync(normalizedSignerAddress, typedData);
const ecSignatureRSV = parseSignatureHexAsRSV(signature);
const signatureBuffer = Buffer.concat([
ethUtil.toBuffer(ecSignatureRSV.v),
ethUtil.toBuffer(ecSignatureRSV.r),
ethUtil.toBuffer(ecSignatureRSV.s),
ethUtil.toBuffer(SignatureType.EIP712),
]);
const signatureHex = `0x${signatureBuffer.toString('hex')}`;
return signatureHex;
},
/**
* Signs an orderHash and returns it's elliptic curve signature and signature type.
* This method currently supports TestRPC, Geth and Parity above and below V1.6.6

View File

@@ -1,12 +1,13 @@
import { SignerType } from '@0xproject/types';
import { Order, SignatureType, SignerType } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import * as chai from 'chai';
import { JSONRPCErrorCallback, JSONRPCRequestPayload } from 'ethereum-types';
import * as ethUtil from 'ethereumjs-util';
import * as _ from 'lodash';
import 'mocha';
import * as Sinon from 'sinon';
import { generatePseudoRandomSalt } from '../src';
import { generatePseudoRandomSalt, orderHashUtils } from '../src';
import { constants } from '../src/constants';
import { signatureUtils } from '../src/signature_utils';
import { chaiSetup } from './utils/chai_setup';
@@ -115,19 +116,53 @@ describe('Signature utils', () => {
expect(salt.lessThan(twoPow256)).to.be.true();
});
});
describe('#ecSignOrderAsync', () => {
let makerAddress: string;
const fakeExchangeContractAddress = '0x1dc4c1cefef38a777b15aa20260a54e584b16c48';
let order: Order;
before(async () => {
const availableAddreses = await web3Wrapper.getAvailableAddressesAsync();
makerAddress = availableAddreses[0];
order = {
makerAddress,
takerAddress: constants.NULL_ADDRESS,
senderAddress: constants.NULL_ADDRESS,
feeRecipientAddress: constants.NULL_ADDRESS,
makerAssetData: constants.NULL_ADDRESS,
takerAssetData: constants.NULL_ADDRESS,
exchangeAddress: fakeExchangeContractAddress,
salt: new BigNumber(0),
makerFee: new BigNumber(0),
takerFee: new BigNumber(0),
makerAssetAmount: new BigNumber(0),
takerAssetAmount: new BigNumber(0),
expirationTimeSeconds: new BigNumber(0),
};
});
it('should result in the same signature as signing order hash without prefix', async () => {
const orderHashHex = orderHashUtils.getOrderHashHex(order);
const sig = ethUtil.ecsign(
ethUtil.toBuffer(orderHashHex),
Buffer.from('F2F48EE19680706196E2E339E5DA3491186E0C4C5030670656B0E0164837257D', 'hex'),
);
const signatureBuffer = Buffer.concat([
ethUtil.toBuffer(sig.v),
ethUtil.toBuffer(sig.r),
ethUtil.toBuffer(sig.s),
ethUtil.toBuffer(SignatureType.EIP712),
]);
const signatureHex = `0x${signatureBuffer.toString('hex')}`;
const eip712Signature = await signatureUtils.ecSignOrderAsync(provider, order, makerAddress);
expect(signatureHex).to.eq(eip712Signature);
});
});
describe('#ecSignOrderHashAsync', () => {
let stubs: Sinon.SinonStub[] = [];
let makerAddress: string;
before(async () => {
const availableAddreses = await web3Wrapper.getAvailableAddressesAsync();
makerAddress = availableAddreses[0];
});
afterEach(() => {
// clean up any stubs after the test has completed
_.each(stubs, s => s.restore());
stubs = [];
});
it('Should return the correct Signature', async () => {
it('should return the correct Signature', async () => {
const orderHash = '0x6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222b0';
const expectedSignature =
'0x1b61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc3340349190569279751135161d22529dc25add4f6069af05be04cacbda2ace225403';